Spaces:
Runtime error
Runtime error
File size: 10,149 Bytes
4782147 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | import { test, expect } from "@playwright/test";
import { TEST_USERS } from "../constants/test-users";
// Use admin auth state for all tests in this file
test.use({ storageState: TEST_USERS.admin.authFile });
test.describe("Admin Users List", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/admin/users");
// Wait for the users table to load
await page.waitForSelector("[data-testid='users-table']", {
timeout: 10000,
});
});
test.describe("Search State Preservation", () => {
test("should maintain search state when navigating to user detail and back", async ({
page,
}) => {
await page.goto("/admin/users");
await page.waitForSelector("[data-testid='users-table']");
// Perform a search
const searchInput = page.getByTestId("users-search-input");
await searchInput.fill("Test");
await searchInput.press("Enter");
await page.waitForTimeout(500);
// Navigate to page 2 if available
const page2Link = page.getByRole("link", { name: "2" });
if (await page2Link.isVisible({ timeout: 1000 }).catch(() => false)) {
await page2Link.click();
await page.waitForTimeout(500);
}
await page.waitForLoadState("networkidle");
// Click on a user to navigate to detail page
const userRow = page
.locator("[data-testid='users-table'] tbody tr")
.first();
await userRow.click();
// Wait for user detail page to load
await page.waitForURL(/\/admin\/users\/.+/);
await page.waitForSelector("[data-testid='user-detail-content']");
await page.waitForLoadState("networkidle");
// Click back button
const backButton = page.getByTestId("admin-users-back-button");
await backButton.click();
await page.waitForLoadState("networkidle");
// Should return to users list with search and pagination state preserved
await page.waitForURL("/admin/users?page=2&query=Test");
await page.waitForSelector("[data-testid='users-table']");
// Search input should still contain "Test User"
const searchValue = await searchInput.inputValue();
expect(searchValue).toBe("Test");
// Should still be on page 2 if we were there before
if (await page2Link.isVisible({ timeout: 1000 }).catch(() => false)) {
await expect(page2Link).toHaveAttribute("aria-current", "page");
}
});
test("should preserve search params in URL when navigating to user detail", async ({
page,
}) => {
await page.goto(
"/admin/users?search=Test&page=2&sort=name&direction=asc",
);
await page.waitForSelector("[data-testid='users-table']");
await page.waitForLoadState("networkidle");
// Click on a user
const userRow = page
.locator("[data-testid='users-table'] tbody tr")
.first();
await userRow.click();
// Wait for user detail page
await page.waitForURL(/\/admin\/users\/.+/);
await page.waitForLoadState("networkidle");
// URL should contain searchPageParams with encoded search state
const currentUrl = page.url();
expect(currentUrl).toContain("searchPageParams=");
});
});
test.describe("Users Table", () => {
test("should display user information in table rows", async ({ page }) => {
await page.waitForLoadState("networkidle");
// Check that user rows are displayed
const rows = page.locator("[data-testid='users-table'] tbody tr");
// Should have at least one user row
const rowCount = await rows.count();
expect(rowCount).toBeGreaterThan(0);
// First row should contain user data (name, email, etc.)
const firstRowText = await rows.first().textContent();
expect(firstRowText).toBeTruthy();
expect(firstRowText).toMatch(/@/);
});
test("should display users table with correct columns", async ({
page,
}) => {
// Check table is present
await expect(page.getByTestId("users-table")).toBeVisible();
// Check sortable headers exist
await expect(page.getByTestId("sort-header-name")).toBeVisible();
await expect(page.getByTestId("sort-header-role")).toBeVisible();
await expect(page.getByTestId("header-status")).toBeVisible();
await expect(page.getByTestId("sort-header-createdAt")).toBeVisible();
});
});
test.describe("Pagination", () => {
test("should display pagination controls", async ({ page }) => {
// Check pagination is present
const pagination = page.getByTestId("table-pagination");
await expect(pagination).toBeVisible();
// Check total count is displayed (should be more than 15 with our seeded data)
const totalCount = page.getByTestId("users-total-count");
await expect(totalCount).toBeVisible();
const countText = await totalCount.textContent();
expect(countText).toMatch(/\d+ users? total/);
// Check navigation buttons exist (only Previous and Next are shown)
await expect(
page.getByRole("link", { name: "Go to previous page" }),
).toBeVisible();
await expect(
page.getByRole("link", { name: "Go to next page" }),
).toBeVisible();
// Page numbers should be visible
await expect(page.getByRole("link", { name: "1" })).toBeVisible();
});
test("should navigate through pages", async ({ page }) => {
// Initially on page 1 (check if page 1 button is active)
await expect(page.getByRole("link", { name: "1" })).toHaveAttribute(
"aria-current",
"page",
);
// Click next page
await page.getByRole("link", { name: "Go to next page" }).click();
// Should be on page 2 (check if page 2 button is now active)
await expect(page.getByRole("link", { name: "2" })).toHaveAttribute(
"aria-current",
"page",
);
// Click to go to page 3 if it exists
const page3Link = page.getByRole("link", { name: "3" });
if (await page3Link.isVisible()) {
await page3Link.click();
await expect(page.getByRole("link", { name: "3" })).toHaveAttribute(
"aria-current",
"page",
);
}
// Previous page button should work
await page.getByRole("link", { name: "Go to previous page" }).click();
// Should now be on page 2
await expect(page.getByRole("link", { name: "2" })).toHaveAttribute(
"aria-current",
"page",
);
// Click page 1 to go back to first page
await page.getByRole("link", { name: "1" }).click();
await expect(page.getByRole("link", { name: "1" })).toHaveAttribute(
"aria-current",
"page",
);
});
test("should change items per page", async ({ page }) => {
// Find the items per page selector
const itemsPerPageSelect = page.getByRole("combobox", {
name: /rows per page/i,
});
// Change to 20 items per page if possible
if (
await itemsPerPageSelect.isVisible({ timeout: 1000 }).catch(() => false)
) {
await itemsPerPageSelect.click();
const option20 = page.getByRole("option", { name: "20" });
if (await option20.isVisible({ timeout: 1000 }).catch(() => false)) {
await option20.click();
await page.waitForTimeout(500);
// Should now show different number of items
const newRows = await page
.locator("[data-testid='users-table'] tbody tr")
.count();
expect(newRows).toBeGreaterThanOrEqual(1);
expect(newRows).toBeLessThanOrEqual(20);
}
}
});
});
test.describe("Search", () => {
test("should search for users", async ({ page }) => {
// Search for a specific user (using the actual seeded admin user name)
const searchInput = page.getByTestId("users-search-input");
await searchInput.fill("Admin");
await searchInput.press("Enter");
// Wait for search results
await page.waitForTimeout(500);
// Should show filtered results
const rows = page.locator("[data-testid='users-table'] tbody tr");
const rowCount = await rows.count();
expect(rowCount).toBeGreaterThan(0);
// First result should match the search
await expect(rows.first()).toContainText("Admin");
});
});
test.describe("Sorting", () => {
test("should sort users by different columns", async ({ page }) => {
// Sort by name
await page.getByTestId("sort-header-name").click();
await page.waitForTimeout(500);
// Get first row data
const firstRowBefore = await page
.locator("[data-testid='users-table'] tbody tr")
.first()
.textContent();
// Sort by name again (should reverse order)
await page.getByTestId("sort-header-name").click();
await page.waitForTimeout(500);
// Get first row data after sorting
const firstRowAfter = await page
.locator("[data-testid='users-table'] tbody tr")
.first()
.textContent();
// Data should be different after sorting
expect(firstRowBefore).not.toBe(firstRowAfter);
// Sort by created date
await page.getByTestId("sort-header-createdAt").click();
await page.waitForTimeout(500);
// Verify sorting indicator is visible
const createdHeader = page.getByTestId("sort-header-createdAt");
const sortIcon = createdHeader.locator("svg");
await expect(sortIcon).toBeVisible();
});
});
test.describe("Navigation", () => {
test("should navigate to user detail page", async ({ page }) => {
// Click on the first user row (entire row is clickable)
const firstRow = page
.locator("[data-testid='users-table'] tbody tr")
.first();
await firstRow.click();
// Should navigate to user detail page
await page.waitForURL(/\/admin\/users\/.+/);
// User detail page should load
await expect(page.getByTestId("user-detail-content")).toBeVisible({
timeout: 10000,
});
});
});
});
|