# 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/'); ```