# 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: workflows/px-source-workflow.spec.ts >> PX Source Workflow Tests >> assign user to source
- Location: e2e/tests/workflows/px-source-workflow.spec.ts:42:7

# Error details

```
Test timeout of 120000ms exceeded.
```

```
Error: locator.fill: Test timeout of 120000ms exceeded.
Call log:
  - waiting for locator('input[name="email"]')

```

# Page snapshot

```yaml
- generic [ref=e2]:
  - complementary [ref=e3]:
    - generic [ref=e4]:
      - img "Al Hammadi" [ref=e6]
      - img "Al Hammadi" [ref=e8]
    - navigation [ref=e9]:
      - link [ref=e10] [cursor=pointer]:
        - /url: /analytics/dashboard/
        - img [ref=e11]
      - link [ref=e16] [cursor=pointer]:
        - /url: /
        - img [ref=e17]
      - button [ref=e20] [cursor=pointer]:
        - img [ref=e21]
      - link [ref=e22] [cursor=pointer]:
        - /url: /complaints/
        - img [ref=e23]
      - link [ref=e26] [cursor=pointer]:
        - /url: /inquiries/
        - img [ref=e27]
      - link [ref=e30] [cursor=pointer]:
        - /url: /observations/
        - img [ref=e31]
      - button [ref=e35] [cursor=pointer]:
        - img [ref=e36]
      - link [ref=e40] [cursor=pointer]:
        - /url: /appreciation/
        - img [ref=e41]
      - link [ref=e43] [cursor=pointer]:
        - /url: /suggestions/?feedback_type=suggestion
        - img [ref=e44]
      - button [ref=e47] [cursor=pointer]:
        - img [ref=e48]
      - button [ref=e52] [cursor=pointer]:
        - img [ref=e53]
      - link [ref=e58] [cursor=pointer]:
        - /url: /organizations/departments/
        - img [ref=e59]
      - link [ref=e63] [cursor=pointer]:
        - /url: /organizations/patients/
        - img [ref=e64]
      - button [ref=e68] [cursor=pointer]:
        - img [ref=e69]
      - button [ref=e74] [cursor=pointer]:
        - img [ref=e75]
      - button [ref=e82] [cursor=pointer]:
        - img [ref=e83]
      - link [ref=e86] [cursor=pointer]:
        - /url: /notifications/send-sms/
        - img [ref=e87]
      - link [ref=e89] [cursor=pointer]:
        - /url: /config/
        - img [ref=e90]
      - link [ref=e93] [cursor=pointer]:
        - /url: /config/deleted/
        - img [ref=e94]
      - link [ref=e97] [cursor=pointer]:
        - /url: /accounts/acknowledgements/my-acknowledgements/
        - img [ref=e98]
      - button [ref=e104] [cursor=pointer]:
        - img [ref=e105]
      - button [ref=e108] [cursor=pointer]:
        - img [ref=e109]
    - button [ref=e114] [cursor=pointer]:
      - img [ref=e115]
    - generic [ref=e118]:
      - link "🇬🇧" [ref=e119] [cursor=pointer]:
        - /url: /core/set-language/?language=en
        - generic [ref=e120]: 🇬🇧
      - link "🇸🇦" [ref=e121] [cursor=pointer]:
        - /url: /core/set-language/?language=ar
        - generic [ref=e122]: 🇸🇦
  - generic [ref=e123]:
    - banner [ref=e124]:
      - generic [ref=e125]:
        - heading "Good morning, E2E! ☀️" [level=2] [ref=e126]
        - paragraph [ref=e127]: Welcome to PX360 Patient Experience Management
      - generic [ref=e128]:
        - generic [ref=e129]:
          - img [ref=e130]
          - textbox "Search..." [ref=e133]
        - button "11" [ref=e135] [cursor=pointer]:
          - img [ref=e136]
          - generic [ref=e139]: "11"
        - generic [ref=e140]:
          - link "🇬🇧 English" [ref=e141] [cursor=pointer]:
            - /url: /core/set-language/?language=en
            - generic [ref=e142]: 🇬🇧
            - generic [ref=e143]: English
          - link "🇸🇦 Arabic" [ref=e144] [cursor=pointer]:
            - /url: /core/set-language/?language=ar
            - generic [ref=e145]: 🇸🇦
            - generic [ref=e146]: Arabic
        - button "E2E Hospital Admin Hospital Admin E" [ref=e148] [cursor=pointer]:
          - generic [ref=e149]:
            - generic [ref=e150]: E2E Hospital Admin
            - generic [ref=e151]: Hospital Admin
          - generic [ref=e152]: E
          - img [ref=e153]
    - main [ref=e155]:
      - generic [ref=e156]:
        - navigation [ref=e157]:
          - list [ref=e158]:
            - listitem [ref=e159]:
              - link "PX Sources" [ref=e160] [cursor=pointer]:
                - /url: /px-sources/
            - listitem [ref=e161]:
              - img [ref=e162]
            - listitem [ref=e164]:
              - link "E2E Test Source 1778178890341" [ref=e165] [cursor=pointer]:
                - /url: /px-sources/acd2176e-4471-47a1-94b2-5c0c0a2c9233/
            - listitem [ref=e166]:
              - img [ref=e167]
            - listitem [ref=e169]: Create Source User
        - generic [ref=e171]:
          - generic [ref=e172]:
            - heading "Create Source User" [level=1] [ref=e173]:
              - img [ref=e174]
              - text: Create Source User
            - paragraph [ref=e177]: E2E Test Source 1778178890341
          - link "Back to Source" [ref=e178] [cursor=pointer]:
            - /url: /px-sources/acd2176e-4471-47a1-94b2-5c0c0a2c9233/
            - img [ref=e179]
            - text: Back to Source
        - generic [ref=e182]:
          - generic [ref=e184]:
            - button "Select Existing User" [ref=e185] [cursor=pointer]:
              - img [ref=e186]
              - text: Select Existing User
            - button "Create New User" [ref=e191] [cursor=pointer]:
              - img [ref=e192]
              - text: Create New User
          - generic [ref=e196]:
            - generic [ref=e197]:
              - generic [ref=e198]:
                - generic [ref=e199]: Select User *
                - combobox "Select User *" [ref=e200]:
                  - option "Choose a user..." [selected]
                  - option "IBRAHIM.ABDELAZEEZ@gmail.com - IBRAHIM ABDELAZEEZ I ALSHUWAIER (Staff)"
                  - option "TURKI@hh.med.sa - TURKI ABDULAZIZ M ALKHAMIS (Director)"
                  - option "ZEENATH@hh.med.sa - ZEENATH ABBONU KUNHIBI (PX Employee)"
                  - option "abber@hh.med.sa - ABEER MOHAMMED DAWII ALGHAMDI (Staff)"
                  - option "brr.lshmry@alhammadi.med.sa - ابرار الشمري"
                  - option "e2e-champion@px360.test - E2E Champion (Champion)"
                  - option "e2e-dept-manager@px360.test - E2E Dept Manager (Department Manager)"
                  - option "e2e-hospital-admin@px360.test - E2E Hospital Admin (Hospital Admin)"
                  - option "e2e-px-admin@px360.test - E2E PX Admin (PX Admin)"
                  - option "e2e-px-employee@px360.test - E2E PX Employee (PX Employee)"
                  - option "e2e-source-user@px360.test - E2E Source User (PX Source User)"
                  - option "e2e-staff@px360.test - E2E Staff (Staff)"
                  - option "e2e-viewer@px360.test - E2E Viewer (Viewer)"
                  - option "hy.lrwyly@alhammadi.med.sa - هيا الرويلي"
                  - option "ismail@tenhal.sa - ismail mosa"
                  - option "mh.lhnyzl@alhammadi.med.sa - مها الحنيظل"
                  - option "ml.lqhtny@alhammadi.med.sa - امال القحطاني"
                  - option "mnth.lzgyby@alhammadi.med.sa - منتهى الزغيبي"
                  - option "mrm.lqhtny@alhammadi.med.sa - مرام القحطاني"
                  - option "my.ldkhyl@alhammadi.med.sa - مي سليمان الدخيل"
                  - option "pxadmin@dev.local - PX Admin (PX Admin)"
                  - option "rhf.lnzy@alhammadi.med.sa - رهف علي العنزي"
                  - option "sh@gmail.com - SHAHAD MOSLEH K ALANAZI (PX Employee)"
                  - option "shwq.lhrby@alhammadi.med.sa - اشواق الحربي"
                  - option "thny.lqrny@alhammadi.med.sa - تهاني عبدالله القرني"
                  - option "thyr.lqhtny@alhammadi.med.sa - أثير القحطاني"
                - paragraph [ref=e201]:
                  - img [ref=e202]
                  - text: Select an existing user to assign as source user. A user can only manage one source.
              - generic [ref=e205]:
                - img [ref=e206]
                - generic [ref=e208]:
                  - paragraph [ref=e209]: Quick Tip
                  - paragraph [ref=e210]: If you need to create a new user account first, switch to the 'Create New User' tab above.
            - generic [ref=e212]:
              - text: Status
              - generic [ref=e213] [cursor=pointer]:
                - checkbox "Active" [checked] [ref=e214]
                - generic [ref=e215]: Active
              - paragraph [ref=e216]: Inactive users will not be able to access their dashboard.
            - heading "Permissions" [level=5] [ref=e218]
            - generic [ref=e219]:
              - generic [ref=e221] [cursor=pointer]:
                - checkbox "Can create complaints" [checked] [ref=e222]
                - generic [ref=e223]: Can create complaints
              - generic [ref=e225] [cursor=pointer]:
                - checkbox "Can create inquiries" [checked] [ref=e226]
                - generic [ref=e227]: Can create inquiries
              - generic [ref=e229] [cursor=pointer]:
                - checkbox "Can create observations" [checked] [ref=e230]
                - generic [ref=e231]: Can create observations
              - generic [ref=e233] [cursor=pointer]:
                - checkbox "Can create suggestions" [checked] [ref=e234]
                - generic [ref=e235]: Can create suggestions
            - generic [ref=e237]:
              - img [ref=e238]
              - generic [ref=e240]: Permissions control what the source user can do in their dashboard. Uncheck to restrict access.
            - generic [ref=e241]:
              - button "Create Source User" [ref=e242] [cursor=pointer]:
                - img [ref=e243]
                - text: Create Source User
              - link "Cancel" [ref=e245] [cursor=pointer]:
                - /url: /px-sources/acd2176e-4471-47a1-94b2-5c0c0a2c9233/
                - img [ref=e246]
                - text: Cancel
    - contentinfo [ref=e249]:
      - paragraph [ref=e250]:
        - text: Powered by
        - link "tenhal.sa" [ref=e251] [cursor=pointer]:
          - /url: https://tenhal.sa
```

# Test source

```ts
  1   | import { test, expect } from '@playwright/test';
  2   | import { RoleAuthHelper, submitContentForm } from '../../helpers/helpers';
  3   | import { HOSPITAL_ID } from '../../helpers/api-helper';
  4   | 
  5   | test.describe('PX Source Workflow Tests', () => {
  6   |   test.setTimeout(120000);
  7   |   test.describe.configure({ mode: 'serial' });
  8   | 
  9   |   let sourceId: string;
  10  | 
  11  |   test('create PX source', async ({ page }) => {
  12  |     const auth = new RoleAuthHelper(page);
  13  |     await auth.login('hospital_admin');
  14  |     await page.waitForTimeout(800);
  15  | 
  16  |     await page.goto('/px-sources/new/');
  17  |     await page.waitForLoadState('domcontentloaded');
  18  |     await page.waitForTimeout(2500);
  19  | 
  20  |     const pageText = (await page.textContent('body')) || '';
  21  |     if (pageText.includes('404') || pageText.includes('403')) {
  22  |       test.skip();
  23  |       return;
  24  |     }
  25  | 
  26  |     await page.locator('input[name="name_en"]').fill(`E2E Test Source ${Date.now()}`);
  27  |     await page.waitForTimeout(300);
  28  |     await page.locator('input[name="code"]').fill(`E2E-${Date.now()}`);
  29  |     await page.waitForTimeout(300);
  30  |     await page.locator('input[name="contact_email"]').fill('e2e-source-test@px360.test');
  31  |     await page.waitForTimeout(300);
  32  | 
  33  |     await submitContentForm(page);
  34  | 
  35  |     const url = page.url();
  36  |     const m = url.match(/\/px-sources\/([0-9a-f-]+)\/?$/);
  37  |     sourceId = m ? m[1] : '';
  38  |     expect(url).not.toContain('/new/');
  39  |     await page.waitForTimeout(800);
  40  |   });
  41  | 
  42  |   test('assign user to source', async ({ page }) => {
  43  |     if (!sourceId) { test.skip(); return; }
  44  | 
  45  |     const auth = new RoleAuthHelper(page);
  46  |     await auth.login('hospital_admin');
  47  |     await page.waitForTimeout(800);
  48  | 
  49  |     await page.goto(`/px-sources/${sourceId}/users/create/`);
  50  |     await page.waitForLoadState('domcontentloaded');
  51  |     await page.waitForTimeout(2500);
  52  | 
> 53  |     await page.locator('input[name="email"]').fill(`e2e-source-test-${Date.now()}@px360.test`);
      |                                               ^ Error: locator.fill: Test timeout of 120000ms exceeded.
  54  |     await page.waitForTimeout(300);
  55  |     await page.locator('input[name="first_name"]').fill('E2E');
  56  |     await page.waitForTimeout(300);
  57  |     await page.locator('input[name="last_name"]').fill('SourceTest');
  58  |     await page.waitForTimeout(300);
  59  | 
  60  |     await submitContentForm(page);
  61  |     expect(page.url()).not.toContain('/users/create');
  62  |     await page.waitForTimeout(800);
  63  |   });
  64  | 
  65  |   test('login as source user and access dashboard', async ({ page }) => {
  66  |     const auth = new RoleAuthHelper(page);
  67  |     await auth.login('source_user');
  68  |     await page.waitForTimeout(800);
  69  | 
  70  |     await page.waitForURL(/\/px-sources\/dashboard/, { timeout: 10000 }).catch(() => {});
  71  |     await page.waitForLoadState('domcontentloaded');
  72  |     await page.waitForTimeout(2500);
  73  | 
  74  |     expect(page.url()).toContain('/px-sources/');
  75  |     await page.waitForTimeout(800);
  76  |   });
  77  | 
  78  |   test('source user creates complaint', async ({ page }) => {
  79  |     const auth = new RoleAuthHelper(page);
  80  |     await auth.login('source_user');
  81  |     await page.waitForTimeout(800);
  82  | 
  83  |     await page.goto('/px-sources/complaints/new/');
  84  |     await page.waitForLoadState('domcontentloaded');
  85  |     await page.waitForTimeout(2500);
  86  | 
  87  |     const fields: [string, string][] = [
  88  |       ['complainant_name', `E2E Source User ${Date.now()}`],
  89  |       ['mobile_number', '0509998877'],
  90  |       ['patient_name', `E2E Patient ${Date.now()}`],
  91  |       ['national_id', `E2E${Date.now()}`.substring(0, 20)],
  92  |       ['complaint_details', `E2E source complaint test ${Date.now()}. Please ignore.`],
  93  |     ];
  94  |     for (const [name, value] of fields) {
  95  |       const f = page.locator(`input[name="${name}"], textarea[name="${name}"]`);
  96  |       if (await f.count() > 0) { await f.fill(value); await page.waitForTimeout(200); }
  97  |     }
  98  |     const relField = page.locator('select[name="relation_to_patient"]');
  99  |     if (await relField.count() > 0) await relField.selectOption('patient');
  100 |     const dateField = page.locator('input[name="incident_date"]');
  101 |     if (await dateField.count() > 0) {
  102 |       const now = new Date();
  103 |       const p = (n: number) => String(n).padStart(2, '0');
  104 |       await dateField.fill(`${now.getFullYear()}-${p(now.getMonth()+1)}-${p(now.getDate())}`);
  105 |     }
  106 | 
  107 |     await submitContentForm(page);
  108 |     expect(page.url()).not.toContain('/complaints/new');
  109 |     await page.waitForTimeout(800);
  110 |   });
  111 | 
  112 |   test('source user creates inquiry', async ({ page }) => {
  113 |     const auth = new RoleAuthHelper(page);
  114 |     await auth.login('source_user');
  115 |     await page.waitForTimeout(800);
  116 | 
  117 |     await page.goto('/px-sources/inquiries/new/');
  118 |     await page.waitForLoadState('domcontentloaded');
  119 |     await page.waitForTimeout(2500);
  120 | 
  121 |     const subjectField = page.locator('input[name="subject"]');
  122 |     if (await subjectField.count() > 0) await subjectField.fill(`E2E Source Inquiry ${Date.now()}`);
  123 |     const msgField = page.locator('textarea[name="message"]');
  124 |     if (await msgField.count() > 0) await msgField.fill(`E2E source inquiry test ${Date.now()}. Please ignore.`);
  125 | 
  126 |     await submitContentForm(page);
  127 |     expect(page.url()).not.toContain('/inquiries/new');
  128 |     await page.waitForTimeout(800);
  129 |   });
  130 | 
  131 |   test('source user creates observation', async ({ page }) => {
  132 |     const auth = new RoleAuthHelper(page);
  133 |     await auth.login('source_user');
  134 |     await page.waitForTimeout(800);
  135 | 
  136 |     await page.goto('/px-sources/observations/new/');
  137 |     await page.waitForLoadState('domcontentloaded');
  138 |     await page.waitForTimeout(2500);
  139 | 
  140 |     const descField = page.locator('textarea[name="description"]');
  141 |     if (await descField.count() > 0) await descField.fill(`E2E source observation test ${Date.now()}. Please ignore.`);
  142 | 
  143 |     await submitContentForm(page);
  144 |     expect(page.url()).not.toContain('/observations/new');
  145 |     await page.waitForTimeout(800);
  146 |   });
  147 | 
  148 |   test('source user creates suggestion', async ({ page }) => {
  149 |     const auth = new RoleAuthHelper(page);
  150 |     await auth.login('source_user');
  151 |     await page.waitForTimeout(800);
  152 | 
  153 |     await page.goto('/px-sources/suggestions/new/');
```