# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: settings/users.spec.ts >> Settings: Users >> create and delete a user
- Location: src/tests/settings/users.spec.ts:468:7

# Error details

```
Error: expect(locator).toBeVisible() failed

Locator: locator('[data-testid="data-table"] [class*="border-b"]').filter({ hasText: 'E2E Delete mp6z5r5nvnfx' }).first()
Expected: visible
Timeout: 10000ms
Error: element(s) not found

Call log:
  - Expect "toBeVisible" with timeout 10000ms
  - waiting for locator('[data-testid="data-table"] [class*="border-b"]').filter({ hasText: 'E2E Delete mp6z5r5nvnfx' }).first()

```

# Page snapshot

```yaml
- generic [ref=e2]:
  - generic [ref=e3]:
    - generic [ref=e5]:
      - generic [ref=e8]:
        - link "MasterEV Home" [ref=e9] [cursor=pointer]:
          - /url: /admin
          - img [ref=e12]
        - link "MasterEV" [ref=e18] [cursor=pointer]:
          - /url: /admin
      - menubar
      - generic [ref=e24] [cursor=pointer]:
        - generic [ref=e25]: 
        - generic [ref=e28]: E2E ADMIN settings-users-spec-ts
        - generic [ref=e29]: 
    - generic [ref=e32]:
      - generic [ref=e36]:
        - heading "Einstellungen" [level=1] [ref=e37]
        - paragraph [ref=e38]: Benutzer:innen- und Systemeinstellungen
      - generic [ref=e42]:
        - tablist [ref=e45]:
          - tab "Benutzer:innen" [selected] [ref=e47] [cursor=pointer]
          - tab "Teams" [ref=e49] [cursor=pointer]
          - tab "Bewerber-Accounts" [ref=e51] [cursor=pointer]
          - tab "Tags" [ref=e53] [cursor=pointer]
          - tab "Vorlagen" [ref=e55] [cursor=pointer]
        - generic [ref=e58]:
          - generic [ref=e59]:
            - generic [ref=e60]:
              - heading "Benutzer:innen" [level=2] [ref=e61]
              - paragraph [ref=e62]: Verwalten Sie die Benutzer:innen des Systems
            - generic [ref=e63]:
              - generic [ref=e65]:
                - generic [ref=e66]:
                  - generic [ref=e67] [cursor=pointer]: 
                  - textbox "Suchen" [active] [ref=e68]: E2E Delete mp6z5r5nvnfx
                - generic [ref=e69]: 
              - button " Erstellen" [ref=e71] [cursor=pointer]:
                - generic [ref=e72]: 
                - generic [ref=e73]: Erstellen
          - generic [ref=e74]:
            - generic [ref=e76]:
              - generic [ref=e78] [cursor=pointer]:
                - text: Name
                - generic [ref=e79]: 
              - generic [ref=e81] [cursor=pointer]:
                - text: Benutzername
                - generic [ref=e82]: 
              - generic [ref=e84] [cursor=pointer]:
                - text: Rolle
                - generic [ref=e85]: 
              - generic [ref=e87]: Programme
              - generic [ref=e89]: Aktiviert
            - generic [ref=e91]:
              - generic [ref=e93]:
                - generic [ref=e94]: 
                - generic [ref=e95]: Lade...
              - generic [ref=e97]: "Keine Ergebnisse gefunden für: E2E UND Delete UND mp6z5r5nvnfx"
    - generic [ref=e100]:
      - link " Impressum" [ref=e103] [cursor=pointer]:
        - /url: /pages/sitenotice
        - generic [ref=e104]: 
        - generic [ref=e105]: Impressum
      - link " Datenschutz" [ref=e108] [cursor=pointer]:
        - /url: /pages/privacy
        - generic [ref=e109]: 
        - generic [ref=e110]: Datenschutz
      - generic [ref=e114] [cursor=pointer]: 
  - generic:
    - generic:
      - alertdialog
```

# Test source

```ts
  1   | import { test, expect, type Page } from '../../fixtures.js';
  2   | 
  3   | const DIALOG = '.p-dialog-mask .p-dialog';
  4   | const unique = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
  5   | 
  6   | /**
  7   |  * Search for text in the data table and wait for the row to settle so
  8   |  * downstream `[data-testid="edit-button"].first()` reliably hits the
  9   |  * filtered row instead of whatever was at the top of the unfiltered
  10  |  * list. The unscoped `getByText` previously matched the search input
  11  |  * itself, leaking past the debounce.
  12  |  */
  13  | async function searchAndFind(page: Page, text: string): Promise<void> {
  14  |   const searchInput = page.locator('masterev-search-input input').first();
  15  |   await expect(searchInput).toBeVisible({ timeout: 5_000 });
  16  |   await searchInput.fill(text);
  17  |   await expect(
  18  |     page
  19  |       .locator('[data-testid="data-table"] [class*="border-b"]')
  20  |       .filter({ hasText: text })
  21  |       .first()
> 22  |   ).toBeVisible({ timeout: 10_000 });
      |     ^ Error: expect(locator).toBeVisible() failed
  23  | }
  24  | 
  25  | /** Create a user via the UI dialog, returning true on success */
  26  | async function createUserViaUI(page: Page, name: string, username: string): Promise<boolean> {
  27  |   const createButton = page.locator('[data-testid="create-button"]').first();
  28  |   await expect(createButton).toBeVisible({ timeout: 10_000 });
  29  |   await createButton.click();
  30  | 
  31  |   const dialog = page.locator(DIALOG).first();
  32  |   await expect(dialog).toBeVisible({ timeout: 5_000 });
  33  | 
  34  |   // Fill name and username
  35  |   await dialog.locator('input').first().fill(name);
  36  |   await dialog.locator('input').nth(1).fill(username);
  37  | 
  38  |   // Select role: click p-select dropdown, pick first option
  39  |   const roleSelect = dialog.locator('p-select').first();
  40  |   await roleSelect.click();
  41  |   const roleOption = page.locator('.p-select-overlay .p-select-option').first();
  42  |   const roleVisible = await roleOption.waitFor({ state: 'visible', timeout: 3_000 }).then(() => true).catch(() => false);
  43  |   if (roleVisible) await roleOption.click();
  44  | 
  45  |   // Select team: try p-multiselect first (multi-team feature flag), fallback to second p-select
  46  |   const multiSelect = dialog.locator('p-multiselect').first();
  47  |   const hasMultiSelect = await multiSelect.waitFor({ state: 'visible', timeout: 2_000 }).then(() => true).catch(() => false);
  48  |   if (hasMultiSelect) {
  49  |     await multiSelect.click();
  50  |     const teamOption = page.locator('.p-multiselect-overlay .p-multiselect-option').first();
  51  |     const teamVisible = await teamOption.waitFor({ state: 'visible', timeout: 3_000 }).then(() => true).catch(() => false);
  52  |     if (teamVisible) await teamOption.click();
  53  |     // Close overlay by pressing Escape
  54  |     await page.keyboard.press('Escape');
  55  |   } else {
  56  |     const teamSelect = dialog.locator('p-select').nth(1);
  57  |     const hasTeamSelect = await teamSelect.waitFor({ state: 'visible', timeout: 2_000 }).then(() => true).catch(() => false);
  58  |     if (hasTeamSelect) {
  59  |       await teamSelect.click();
  60  |       const teamOption = page.locator('.p-select-overlay .p-select-option').first();
  61  |       const teamVisible = await teamOption.waitFor({ state: 'visible', timeout: 3_000 }).then(() => true).catch(() => false);
  62  |       if (teamVisible) await teamOption.click();
  63  |     }
  64  |   }
  65  | 
  66  |   // Select at least one process
  67  |   const processListbox = dialog.locator('p-listbox').first();
  68  |   const processVisible = await processListbox.waitFor({ state: 'visible', timeout: 2_000 }).then(() => true).catch(() => false);
  69  |   if (processVisible) {
  70  |     const firstProcess = processListbox.locator('.p-listbox-option').first();
  71  |     await firstProcess.click();
  72  |   }
  73  | 
  74  |   // Try to save
  75  |   const saveButton = dialog.locator('[data-testid="save-button"]').first();
  76  |   const canSave = await expect(saveButton).toBeEnabled({ timeout: 3_000 }).then(() => true).catch(() => false);
  77  |   if (!canSave) {
  78  |     await dialog.locator('[data-testid="cancel-button"]').first().click();
  79  |     await expect(dialog).not.toBeVisible({ timeout: 5_000 });
  80  |     return false;
  81  |   }
  82  | 
  83  |   await saveButton.click();
  84  |   const closed = await expect(dialog).not.toBeVisible({ timeout: 15_000 }).then(() => true).catch(() => false);
  85  |   if (!closed) {
  86  |     await page.keyboard.press('Escape');
  87  |     await expect(dialog).not.toBeVisible({ timeout: 5_000 }).catch(() => {});
  88  |     return false;
  89  |   }
  90  |   return true;
  91  | }
  92  | 
  93  | test.describe('Settings: Users', () => {
  94  |   test('users page loads and shows user list', async ({ adminPage }) => {
  95  |     await adminPage.goto('/admin/settings/users');
  96  |     await expect(adminPage).toHaveURL(/\/settings\/users/);
  97  |     await expect(adminPage.locator('masterev-infinite-data-table').first()).toBeVisible({ timeout: 10_000 });
  98  |   });
  99  | 
  100 |   test('create user dialog opens and shows required fields', async ({ adminPage }) => {
  101 |     await adminPage.goto('/admin/settings/users');
  102 | 
  103 |     const createButton = adminPage.locator('[data-testid="create-button"]').first();
  104 |     await expect(createButton).toBeVisible({ timeout: 10_000 });
  105 |     await createButton.click();
  106 | 
  107 |     const dialog = adminPage.locator(DIALOG).first();
  108 |     await expect(dialog).toBeVisible({ timeout: 5_000 });
  109 | 
  110 |     // Verify all expected form fields are present
  111 |     await expect(dialog.locator('input').first()).toBeVisible(); // name
  112 |     await expect(dialog.locator('input').nth(1)).toBeVisible();  // username
  113 | 
  114 |     // Role select should be present
  115 |     await expect(dialog.locator('p-select').first()).toBeVisible();
  116 | 
  117 |     // Save and cancel buttons
  118 |     await expect(dialog.locator('[data-testid="save-button"]').first()).toBeVisible();
  119 |     await expect(dialog.locator('[data-testid="cancel-button"]').first()).toBeVisible();
  120 | 
  121 |     // Fill basic fields
  122 |     await dialog.locator('input').first().fill(`E2E User ${unique()}`);
```