417 lines
17 KiB
Markdown
417 lines
17 KiB
Markdown
# 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/inquiry-lifecycle.spec.ts >> Inquiry Lifecycle >> respond modal opens on inquiry detail
|
|
- Location: e2e/tests/workflows/inquiry-lifecycle.spec.ts:135:7
|
|
|
|
# Error details
|
|
|
|
```
|
|
Error: locator.click: Error: strict mode violation: locator('button[onclick="showRespondModal()"]') resolved to 2 elements:
|
|
1) <button onclick="showRespondModal()" class="inline-flex items-center px-6 py-2.5 bg-white text-navy font-medium rounded-xl hover:bg-blue-50 transition text-sm">…</button> aka getByRole('button', { name: 'Respond' })
|
|
2) <button onclick="showRespondModal()" class="w-full px-6 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm inline-flex items-center justify-center gap-2">…</button> aka getByRole('button', { name: 'Send Response' })
|
|
|
|
Call log:
|
|
- waiting for locator('button[onclick="showRespondModal()"]')
|
|
|
|
```
|
|
|
|
# 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 "10" [ref=e135] [cursor=pointer]:
|
|
- img [ref=e136]
|
|
- generic [ref=e139]: "10"
|
|
- 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=e157]:
|
|
- generic [ref=e158]:
|
|
- generic [ref=e159]:
|
|
- link "Inquiries" [ref=e160] [cursor=pointer]:
|
|
- /url: /inquiries/
|
|
- img [ref=e161]
|
|
- generic [ref=e163]: Detail
|
|
- generic [ref=e164]:
|
|
- heading "E2E automated test inquiry 1778178510420" [level=1] [ref=e165]
|
|
- generic [ref=e166]: Open
|
|
- generic [ref=e167]: Low
|
|
- generic [ref=e168]:
|
|
- generic [ref=e169]:
|
|
- img [ref=e170]
|
|
- text: INQ-20260507-2… |
|
|
- img [ref=e173]
|
|
- text: 2026-05-07
|
|
- button "Respond" [ref=e175] [cursor=pointer]:
|
|
- img [ref=e176]
|
|
- text: Respond
|
|
- generic [ref=e178]:
|
|
- generic [ref=e179]:
|
|
- generic [ref=e180]:
|
|
- generic [ref=e181]:
|
|
- img [ref=e183]
|
|
- heading "Inquiry Details" [level=5] [ref=e186]
|
|
- generic [ref=e188]:
|
|
- paragraph
|
|
- paragraph [ref=e189]: E2E automated test inquiry - please ignore
|
|
- paragraph
|
|
- generic [ref=e190]:
|
|
- generic [ref=e191]:
|
|
- img [ref=e193]
|
|
- heading "AI Analysis" [level=5] [ref=e196]
|
|
- generic [ref=e197]:
|
|
- generic [ref=e198]:
|
|
- heading "AI Summary (English)" [level=4] [ref=e199]
|
|
- paragraph [ref=e200]: This is an automated test inquiry. The sender has explicitly requested that the inquiry be ignored. Therefore, no further action is required.
|
|
- generic [ref=e201]:
|
|
- heading "AI Summary (Arabic)" [level=4] [ref=e202]
|
|
- paragraph [ref=e203]: هذا استفسار اختبار آلي. طلب المرسل صراحةً تجاهل الاستفسار. لذلك، لا يلزم اتخاذ أي إجراء إضافي.
|
|
- generic [ref=e204]:
|
|
- generic [ref=e205]:
|
|
- img [ref=e207]
|
|
- heading "Timeline" [level=5] [ref=e211]
|
|
- generic [ref=e213]:
|
|
- generic [ref=e214]:
|
|
- paragraph [ref=e215]: Inquiry Created
|
|
- paragraph [ref=e216]: Created by E2E PX Admin
|
|
- paragraph [ref=e217]: 2026-05-07 21:28
|
|
- generic [ref=e218]:
|
|
- paragraph [ref=e219]: Note
|
|
- paragraph [ref=e220]: AI analysis complete. Priority and summaries updated.
|
|
- paragraph [ref=e221]: 2026-05-07 21:28
|
|
- generic [ref=e222]:
|
|
- generic [ref=e223]:
|
|
- generic [ref=e224]:
|
|
- img [ref=e226]
|
|
- heading "Actions" [level=5] [ref=e229]
|
|
- generic [ref=e230]:
|
|
- button "Send Response" [ref=e231] [cursor=pointer]:
|
|
- img [ref=e232]
|
|
- text: Send Response
|
|
- button "Assign" [ref=e234] [cursor=pointer]:
|
|
- img [ref=e235]
|
|
- text: Assign
|
|
- generic [ref=e238]:
|
|
- generic [ref=e239]:
|
|
- generic [ref=e240]: Update Status
|
|
- combobox [ref=e241]:
|
|
- option "Open" [selected]
|
|
- option "In Progress"
|
|
- option "Contacted"
|
|
- option "Contacted, No Response"
|
|
- option "Resolved"
|
|
- option "Closed"
|
|
- button "Change Status" [ref=e242] [cursor=pointer]:
|
|
- img [ref=e243]
|
|
- text: Change Status
|
|
- link "Edit Inquiry" [ref=e248] [cursor=pointer]:
|
|
- /url: /inquiries/110e0bc6-636b-4dc1-ae52-21b0be9f4748/edit/
|
|
- img [ref=e249]
|
|
- text: Edit Inquiry
|
|
- link "Initiate RCA" [ref=e252] [cursor=pointer]:
|
|
- /url: /rca/create/?related_model=inquiry&related_id=110e0bc6-636b-4dc1-ae52-21b0be9f4748
|
|
- img [ref=e253]
|
|
- text: Initiate RCA
|
|
- link "Create QI Project" [ref=e256] [cursor=pointer]:
|
|
- /url: /projects/create/?related_model=inquiry&related_id=110e0bc6-636b-4dc1-ae52-21b0be9f4748
|
|
- img [ref=e257]
|
|
- text: Create QI Project
|
|
- generic [ref=e259]:
|
|
- paragraph [ref=e260]: Department Actions
|
|
- button "Send to Department" [ref=e261] [cursor=pointer]:
|
|
- img [ref=e262]
|
|
- text: Send to Department
|
|
- generic [ref=e265]:
|
|
- generic [ref=e266]:
|
|
- img [ref=e268]
|
|
- heading "Contact Information" [level=5] [ref=e271]
|
|
- generic [ref=e272]:
|
|
- generic [ref=e273]:
|
|
- generic [ref=e274]: Name
|
|
- generic [ref=e275]: E2E Test Contact 1778178510420
|
|
- generic [ref=e276]:
|
|
- generic [ref=e277]: Phone
|
|
- generic [ref=e278]: "0500000001"
|
|
- generic [ref=e279]:
|
|
- generic [ref=e280]: Email
|
|
- generic [ref=e281]: e2e-test-1778178510420@test.com
|
|
- generic [ref=e282]:
|
|
- heading "Status Timeline" [level=3] [ref=e283]:
|
|
- img [ref=e284]
|
|
- text: Status Timeline
|
|
- generic [ref=e288]:
|
|
- generic [ref=e290]: May 07, 2026 21:28
|
|
- generic [ref=e291]: Created
|
|
- generic [ref=e292]:
|
|
- generic [ref=e293]:
|
|
- img [ref=e295]
|
|
- heading "Organization" [level=5] [ref=e299]
|
|
- generic [ref=e300]:
|
|
- generic [ref=e301]:
|
|
- generic [ref=e302]: Hospital
|
|
- generic [ref=e303]: NUZHA
|
|
- generic [ref=e304]:
|
|
- generic [ref=e305]: Department
|
|
- generic [ref=e306]: Information Technology
|
|
- generic [ref=e307]:
|
|
- generic [ref=e308]: Location
|
|
- generic [ref=e309]: "-"
|
|
- generic [ref=e310]:
|
|
- generic [ref=e311]: Category
|
|
- generic [ref=e312]: General Information
|
|
- generic [ref=e313]:
|
|
- generic [ref=e314]: SHCT Taxonomy
|
|
- generic [ref=e315]: MANAGEMENT
|
|
- generic [ref=e316]:
|
|
- generic [ref=e317]: Priority
|
|
- generic [ref=e318]: Low
|
|
- generic [ref=e319]:
|
|
- generic [ref=e320]:
|
|
- img [ref=e322]
|
|
- heading "Notes" [level=5] [ref=e325]
|
|
- generic [ref=e327]:
|
|
- generic [ref=e328]:
|
|
- text: Staff Notes
|
|
- textbox "Add staff notes..." [ref=e329]
|
|
- generic [ref=e330]:
|
|
- text: Supervisor Notes
|
|
- textbox "Add supervisor notes..." [ref=e331]
|
|
- button "Save Notes" [ref=e332] [cursor=pointer]
|
|
- contentinfo [ref=e333]:
|
|
- paragraph [ref=e334]:
|
|
- text: Powered by
|
|
- link "tenhal.sa" [ref=e335] [cursor=pointer]:
|
|
- /url: https://tenhal.sa
|
|
```
|
|
|
|
# Test source
|
|
|
|
```ts
|
|
61 | await auth.login('hospital_admin');
|
|
62 |
|
|
63 | await page.goto('/inquiries/');
|
|
64 | await page.waitForLoadState('domcontentloaded');
|
|
65 | await page.waitForTimeout(2000);
|
|
66 |
|
|
67 | if (inquiryReference) {
|
|
68 | const searchInput = page.locator('input[name="search"]');
|
|
69 | if (await searchInput.count() > 0) {
|
|
70 | await searchInput.fill(inquiryReference);
|
|
71 | await searchInput.press('Enter');
|
|
72 | await page.waitForLoadState('domcontentloaded');
|
|
73 | await page.waitForTimeout(1000);
|
|
74 | }
|
|
75 |
|
|
76 | const tableText = await page.locator('table').textContent();
|
|
77 | expect(tableText).toContain(inquiryReference);
|
|
78 | } else {
|
|
79 | const tableText = await page.locator('table').textContent();
|
|
80 | expect(tableText).toBeTruthy();
|
|
81 | }
|
|
82 | });
|
|
83 |
|
|
84 | test('inquiry list has filter and table', async ({ page }) => {
|
|
85 | const auth = new RoleAuthHelper(page);
|
|
86 | await auth.login('hospital_admin');
|
|
87 |
|
|
88 | await page.goto('/inquiries/');
|
|
89 | await page.waitForLoadState('domcontentloaded');
|
|
90 | await page.waitForTimeout(2000);
|
|
91 |
|
|
92 | const table = page.locator('table.data-table, table');
|
|
93 | expect(await table.count()).toBeGreaterThan(0);
|
|
94 |
|
|
95 | const filterForm = page.locator('form[method="get"]');
|
|
96 | expect(await filterForm.count()).toBeGreaterThan(0);
|
|
97 | });
|
|
98 |
|
|
99 | test('can access inquiry create form (authenticated)', async ({ page }) => {
|
|
100 | const auth = new RoleAuthHelper(page);
|
|
101 | await auth.login('hospital_admin');
|
|
102 |
|
|
103 | await page.goto('/inquiries/new/');
|
|
104 | await page.waitForLoadState('domcontentloaded');
|
|
105 | await page.waitForTimeout(2000);
|
|
106 |
|
|
107 | const form = page.locator('#inquiryForm, form[action*="inquiry_create"]');
|
|
108 | const hasForm = await form.count().then(c => c > 0);
|
|
109 | expect(hasForm).toBeTruthy();
|
|
110 | });
|
|
111 |
|
|
112 | test('inquiry detail page loads for open inquiry', async ({ page }) => {
|
|
113 | const auth = new RoleAuthHelper(page);
|
|
114 | await auth.login('hospital_admin');
|
|
115 |
|
|
116 | await page.goto('/inquiries/?status=open');
|
|
117 | await page.waitForLoadState('domcontentloaded');
|
|
118 | await page.waitForTimeout(2000);
|
|
119 |
|
|
120 | const firstRow = page.locator('table tbody tr').first();
|
|
121 | const hasRows = await firstRow.count().then(c => c > 0);
|
|
122 | if (!hasRows) {
|
|
123 | test.skip();
|
|
124 | return;
|
|
125 | }
|
|
126 |
|
|
127 | await firstRow.click();
|
|
128 | await page.waitForLoadState('domcontentloaded');
|
|
129 | await page.waitForTimeout(1000);
|
|
130 |
|
|
131 | const pageText = await page.textContent('body');
|
|
132 | expect(pageText).toContain('INQ-');
|
|
133 | });
|
|
134 |
|
|
135 | test('respond modal opens on inquiry detail', async ({ page }) => {
|
|
136 | const auth = new RoleAuthHelper(page);
|
|
137 | await auth.login('hospital_admin');
|
|
138 |
|
|
139 | await page.goto('/inquiries/?status=open');
|
|
140 | await page.waitForLoadState('domcontentloaded');
|
|
141 | await page.waitForTimeout(2000);
|
|
142 |
|
|
143 | const firstRow = page.locator('table tbody tr').first();
|
|
144 | const hasRows = await firstRow.count().then(c => c > 0);
|
|
145 | if (!hasRows) {
|
|
146 | test.skip();
|
|
147 | return;
|
|
148 | }
|
|
149 |
|
|
150 | await firstRow.click();
|
|
151 | await page.waitForLoadState('domcontentloaded');
|
|
152 | await page.waitForTimeout(1000);
|
|
153 |
|
|
154 | const respondBtn = page.locator('button[onclick="showRespondModal()"]');
|
|
155 | const hasRespond = await respondBtn.count().then(c => c > 0);
|
|
156 | if (!hasRespond) {
|
|
157 | test.skip();
|
|
158 | return;
|
|
159 | }
|
|
160 |
|
|
> 161 | await respondBtn.click();
|
|
| ^ Error: locator.click: Error: strict mode violation: locator('button[onclick="showRespondModal()"]') resolved to 2 elements:
|
|
162 | const modal = page.locator('#respondModal');
|
|
163 | await modal.waitFor({ state: 'visible', timeout: 5000 });
|
|
164 |
|
|
165 | const form = modal.locator('form#respondForm, form[action*="respond"]');
|
|
166 | expect(await form.count()).toBeGreaterThan(0);
|
|
167 |
|
|
168 | const closeBtn = modal.locator('button[onclick="closeRespondModal()"]');
|
|
169 | if (await closeBtn.count() > 0) {
|
|
170 | await closeBtn.click();
|
|
171 | }
|
|
172 | });
|
|
173 |
|
|
174 | test('inquiry list shows status badges', async ({ page }) => {
|
|
175 | const auth = new RoleAuthHelper(page);
|
|
176 | await auth.login('hospital_admin');
|
|
177 |
|
|
178 | await page.goto('/inquiries/');
|
|
179 | await page.waitForLoadState('domcontentloaded');
|
|
180 | await page.waitForTimeout(2000);
|
|
181 |
|
|
182 | const statusBadges = page.locator('span.rounded-full');
|
|
183 | const badgeCount = await statusBadges.count();
|
|
184 | expect(badgeCount).toBeGreaterThan(0);
|
|
185 | });
|
|
186 | });
|
|
187 |
|
|
``` |