File size: 10,149 Bytes
05c5ed5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,
      });
    });
  });
});