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, }); }); }); });