aria-ease 6.2.1 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -17
- package/bin/cli.cjs +227 -115
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-HL2VPEEV.js → contractTestRunnerPlaywright-ACAWN34W.js} +227 -115
- package/bin/{test-HH2EW2NM.js → test-A3ESFXOR.js} +1 -1
- package/dist/{contractTestRunnerPlaywright-EXEBWWPC.js → contractTestRunnerPlaywright-O7FF7GV4.js} +227 -115
- package/dist/index.cjs +229 -116
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +3 -2
- package/dist/src/{Types.d-CxWrr421.d.ts → Types.d-CRjhbrcw.d.cts} +10 -0
- package/dist/src/{Types.d-CxWrr421.d.cts → Types.d-CRjhbrcw.d.ts} +10 -0
- package/dist/src/accordion/index.d.cts +1 -1
- package/dist/src/accordion/index.d.ts +1 -1
- package/dist/src/block/index.d.cts +1 -1
- package/dist/src/block/index.d.ts +1 -1
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -1
- package/dist/src/combobox/index.cjs +1 -1
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/combobox/index.js +1 -1
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/radio/index.cjs +1 -0
- package/dist/src/radio/index.d.cts +1 -1
- package/dist/src/radio/index.d.ts +1 -1
- package/dist/src/radio/index.js +1 -0
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- package/dist/src/utils/test/{contractTestRunnerPlaywright-LJHY3AB4.js → contractTestRunnerPlaywright-7BPRTIN4.js} +227 -115
- package/dist/src/utils/test/contracts/AccordionContract.json +1 -0
- package/dist/src/utils/test/contracts/ComboboxContract.json +1 -0
- package/dist/src/utils/test/contracts/MenuContract.json +1 -0
- package/dist/src/utils/test/index.cjs +227 -115
- package/dist/src/utils/test/index.js +1 -1
- package/package.json +1 -1
package/dist/{contractTestRunnerPlaywright-EXEBWWPC.js → contractTestRunnerPlaywright-O7FF7GV4.js}
RENAMED
|
@@ -19,6 +19,11 @@ import { default as default2 } from "playwright/test";
|
|
|
19
19
|
import { readFileSync } from "fs";
|
|
20
20
|
async function runContractTestsPlaywright(componentName, url) {
|
|
21
21
|
const reporter = new ContractReporter(true);
|
|
22
|
+
const actionTimeoutMs = 400;
|
|
23
|
+
const assertionTimeoutMs = 400;
|
|
24
|
+
function isBrowserClosedError(error) {
|
|
25
|
+
return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
|
|
26
|
+
}
|
|
22
27
|
const contractTyped = contract_default;
|
|
23
28
|
const contractPath = contractTyped[componentName]?.path;
|
|
24
29
|
if (!contractPath) {
|
|
@@ -37,17 +42,29 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
37
42
|
try {
|
|
38
43
|
page = await createTestPage();
|
|
39
44
|
if (url) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
try {
|
|
46
|
+
await page.goto(url, {
|
|
47
|
+
waitUntil: "domcontentloaded",
|
|
48
|
+
timeout: 3e4
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
44
55
|
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
45
56
|
}
|
|
46
57
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
47
58
|
if (!mainSelector) {
|
|
48
|
-
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
59
|
+
throw new Error(`CRITICAL: No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`CRITICAL: Component element '${mainSelector}' not found on page within 30s. This usually means the component didn't render or the contract selector is incorrect. Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
66
|
+
);
|
|
49
67
|
}
|
|
50
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
51
68
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
52
69
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
53
70
|
state: "visible",
|
|
@@ -127,6 +144,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
147
|
+
if (!page || page.isClosed()) {
|
|
148
|
+
console.warn(`
|
|
149
|
+
\u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
|
|
150
|
+
`);
|
|
151
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
130
154
|
const { action, assertions } = dynamicTest;
|
|
131
155
|
const failuresBeforeTest = failures.length;
|
|
132
156
|
if (componentContract.selectors.popup) {
|
|
@@ -146,16 +170,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
146
170
|
const closeElement = page.locator(closeSelector).first();
|
|
147
171
|
await closeElement.focus();
|
|
148
172
|
await page.keyboard.press("Escape");
|
|
149
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
173
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
150
174
|
}
|
|
151
175
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
152
176
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
153
|
-
await triggerElement.click();
|
|
154
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
177
|
+
await triggerElement.click({ timeout: actionTimeoutMs });
|
|
178
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
155
179
|
}
|
|
156
180
|
if (!menuClosed) {
|
|
157
181
|
await page.mouse.click(10, 10);
|
|
158
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
182
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
159
183
|
}
|
|
160
184
|
if (!menuClosed) {
|
|
161
185
|
throw new Error(
|
|
@@ -184,9 +208,9 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
184
208
|
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
185
209
|
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
186
210
|
if (isExpanded && triggerPanel) {
|
|
187
|
-
await trigger.click();
|
|
211
|
+
await trigger.click({ timeout: actionTimeoutMs });
|
|
188
212
|
const panel = page.locator(`#${triggerPanel}`);
|
|
189
|
-
await (0, test_exports.expect)(panel).toBeHidden({ timeout:
|
|
213
|
+
await (0, test_exports.expect)(panel).toBeHidden({ timeout: assertionTimeoutMs }).catch(() => {
|
|
190
214
|
});
|
|
191
215
|
}
|
|
192
216
|
}
|
|
@@ -225,134 +249,192 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
225
249
|
continue;
|
|
226
250
|
}
|
|
227
251
|
for (const act of action) {
|
|
252
|
+
if (!page || page.isClosed()) {
|
|
253
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
228
256
|
if (act.type === "focus") {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
try {
|
|
258
|
+
const focusSelector = componentContract.selectors[act.target];
|
|
259
|
+
if (!focusSelector) {
|
|
260
|
+
failures.push(`Selector for focus target ${act.target} not found.`);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
await page.locator(focusSelector).first().focus({ timeout: actionTimeoutMs });
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (isBrowserClosedError(error)) {
|
|
266
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
failures.push(`Failed to focus ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
232
270
|
continue;
|
|
233
271
|
}
|
|
234
|
-
await page.locator(focusSelector).first().focus();
|
|
235
272
|
}
|
|
236
273
|
if (act.type === "type" && act.value) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
274
|
+
try {
|
|
275
|
+
const typeSelector = componentContract.selectors[act.target];
|
|
276
|
+
if (!typeSelector) {
|
|
277
|
+
failures.push(`Selector for type target ${act.target} not found.`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
await page.locator(typeSelector).first().fill(act.value, { timeout: actionTimeoutMs });
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (isBrowserClosedError(error)) {
|
|
283
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
failures.push(`Failed to type into ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
240
287
|
continue;
|
|
241
288
|
}
|
|
242
|
-
await page.locator(typeSelector).first().fill(act.value);
|
|
243
289
|
}
|
|
244
290
|
if (act.type === "click") {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
291
|
+
try {
|
|
292
|
+
if (act.target === "document") {
|
|
293
|
+
await page.mouse.click(10, 10);
|
|
294
|
+
} else if (act.target === "relative" && act.relativeTarget) {
|
|
295
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
296
|
+
if (!relativeSelector) {
|
|
297
|
+
failures.push(`Relative selector not defined for click action.`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
301
|
+
if (!relativeElement) {
|
|
302
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
await relativeElement.click({ timeout: actionTimeoutMs });
|
|
306
|
+
} else {
|
|
307
|
+
const actionSelector = componentContract.selectors[act.target];
|
|
308
|
+
if (!actionSelector) {
|
|
309
|
+
failures.push(`Selector for action target ${act.target} not found.`);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
await page.locator(actionSelector).first().click({ timeout: actionTimeoutMs });
|
|
257
313
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
failures.push(`Selector for action target ${act.target} not found.`);
|
|
263
|
-
continue;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (isBrowserClosedError(error)) {
|
|
316
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
317
|
+
break;
|
|
264
318
|
}
|
|
265
|
-
|
|
319
|
+
failures.push(`Failed to click ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
320
|
+
continue;
|
|
266
321
|
}
|
|
267
322
|
}
|
|
268
323
|
if (act.type === "keypress" && act.key) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
keyValue
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
288
|
-
await page.keyboard.press(keyValue);
|
|
289
|
-
} else {
|
|
290
|
-
const keypressSelector = componentContract.selectors[act.target];
|
|
291
|
-
if (!keypressSelector) {
|
|
292
|
-
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
293
|
-
continue;
|
|
324
|
+
try {
|
|
325
|
+
const keyMap = {
|
|
326
|
+
"Space": "Space",
|
|
327
|
+
"Enter": "Enter",
|
|
328
|
+
"Escape": "Escape",
|
|
329
|
+
"Arrow Up": "ArrowUp",
|
|
330
|
+
"Arrow Down": "ArrowDown",
|
|
331
|
+
"Arrow Left": "ArrowLeft",
|
|
332
|
+
"Arrow Right": "ArrowRight",
|
|
333
|
+
"Home": "Home",
|
|
334
|
+
"End": "End",
|
|
335
|
+
"Tab": "Tab"
|
|
336
|
+
};
|
|
337
|
+
let keyValue = keyMap[act.key] || act.key;
|
|
338
|
+
if (keyValue === "Space") {
|
|
339
|
+
keyValue = " ";
|
|
340
|
+
} else if (keyValue.includes(" ")) {
|
|
341
|
+
keyValue = keyValue.replace(/ /g, "");
|
|
294
342
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
343
|
+
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
344
|
+
await page.keyboard.press(keyValue);
|
|
345
|
+
} else {
|
|
346
|
+
const keypressSelector = componentContract.selectors[act.target];
|
|
347
|
+
if (!keypressSelector) {
|
|
348
|
+
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const target = page.locator(keypressSelector).first();
|
|
352
|
+
const elementCount = await target.count();
|
|
353
|
+
if (elementCount === 0) {
|
|
354
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
await target.press(keyValue, { timeout: actionTimeoutMs });
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (isBrowserClosedError(error)) {
|
|
361
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
299
362
|
break;
|
|
300
363
|
}
|
|
301
|
-
|
|
364
|
+
failures.push(`Failed to press ${act.key} on ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
+
continue;
|
|
302
366
|
}
|
|
303
367
|
}
|
|
304
368
|
if (act.type === "hover") {
|
|
305
|
-
|
|
369
|
+
try {
|
|
370
|
+
if (act.target === "relative" && act.relativeTarget) {
|
|
371
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
372
|
+
if (!relativeSelector) {
|
|
373
|
+
failures.push(`Relative selector not defined for hover action.`);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
377
|
+
if (!relativeElement) {
|
|
378
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
await relativeElement.hover({ timeout: actionTimeoutMs });
|
|
382
|
+
} else {
|
|
383
|
+
const hoverSelector = componentContract.selectors[act.target];
|
|
384
|
+
if (!hoverSelector) {
|
|
385
|
+
failures.push(`Selector for hover target ${act.target} not found.`);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
await page.locator(hoverSelector).first().hover({ timeout: actionTimeoutMs });
|
|
389
|
+
}
|
|
390
|
+
} catch (error) {
|
|
391
|
+
if (isBrowserClosedError(error)) {
|
|
392
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
failures.push(`Failed to hover ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
for (const assertion of assertions) {
|
|
401
|
+
if (!page || page.isClosed()) {
|
|
402
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`);
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
let target;
|
|
406
|
+
try {
|
|
407
|
+
if (assertion.target === "relative") {
|
|
306
408
|
const relativeSelector = componentContract.selectors.relative;
|
|
307
409
|
if (!relativeSelector) {
|
|
308
|
-
failures.push(
|
|
410
|
+
failures.push("Relative selector is not defined in the contract.");
|
|
309
411
|
continue;
|
|
310
412
|
}
|
|
311
|
-
const
|
|
312
|
-
if (!
|
|
313
|
-
failures.push(
|
|
413
|
+
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
414
|
+
if (!relativeTargetValue) {
|
|
415
|
+
failures.push("Relative target or expected value is not defined.");
|
|
314
416
|
continue;
|
|
315
417
|
}
|
|
316
|
-
await
|
|
418
|
+
target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
|
|
317
419
|
} else {
|
|
318
|
-
const
|
|
319
|
-
if (!
|
|
320
|
-
failures.push(`Selector for
|
|
420
|
+
const assertionSelector = componentContract.selectors[assertion.target];
|
|
421
|
+
if (!assertionSelector) {
|
|
422
|
+
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
321
423
|
continue;
|
|
322
424
|
}
|
|
323
|
-
|
|
425
|
+
target = page.locator(assertionSelector).first();
|
|
324
426
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
for (const assertion of assertions) {
|
|
328
|
-
let target;
|
|
329
|
-
if (assertion.target === "relative") {
|
|
330
|
-
const relativeSelector = componentContract.selectors.relative;
|
|
331
|
-
if (!relativeSelector) {
|
|
332
|
-
failures.push("Relative selector is not defined in the contract.");
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
336
|
-
if (!relativeTargetValue) {
|
|
337
|
-
failures.push("Relative target or expected value is not defined.");
|
|
427
|
+
if (!target) {
|
|
428
|
+
failures.push(`Target ${assertion.target} not found.`);
|
|
338
429
|
continue;
|
|
339
430
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const assertionSelector = componentContract.selectors[assertion.target];
|
|
343
|
-
if (!assertionSelector) {
|
|
344
|
-
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
target = page.locator(assertionSelector).first();
|
|
348
|
-
}
|
|
349
|
-
if (!target) {
|
|
350
|
-
failures.push(`Target ${assertion.target} not found.`);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
failures.push(`Failed to resolve target ${assertion.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
351
433
|
continue;
|
|
352
434
|
}
|
|
353
435
|
if (assertion.assertion === "toBeVisible") {
|
|
354
436
|
try {
|
|
355
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
437
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: assertionTimeoutMs });
|
|
356
438
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
357
439
|
} catch {
|
|
358
440
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -366,7 +448,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
366
448
|
}
|
|
367
449
|
if (assertion.assertion === "notToBeVisible") {
|
|
368
450
|
try {
|
|
369
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
451
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: assertionTimeoutMs });
|
|
370
452
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
371
453
|
} catch {
|
|
372
454
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -388,7 +470,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
388
470
|
failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
|
|
389
471
|
}
|
|
390
472
|
} else {
|
|
391
|
-
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout:
|
|
473
|
+
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: assertionTimeoutMs });
|
|
392
474
|
passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
|
|
393
475
|
}
|
|
394
476
|
} catch {
|
|
@@ -418,7 +500,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
418
500
|
}
|
|
419
501
|
if (assertion.assertion === "toHaveFocus") {
|
|
420
502
|
try {
|
|
421
|
-
await (0, test_exports.expect)(target).toBeFocused({ timeout:
|
|
503
|
+
await (0, test_exports.expect)(target).toBeFocused({ timeout: assertionTimeoutMs });
|
|
422
504
|
passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
|
|
423
505
|
} catch {
|
|
424
506
|
const actualFocus = await page.evaluate(() => {
|
|
@@ -453,18 +535,48 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
453
535
|
reporter.summary(failures);
|
|
454
536
|
} catch (error) {
|
|
455
537
|
if (error instanceof Error) {
|
|
456
|
-
if (error.message.includes("Executable doesn't exist")) {
|
|
457
|
-
console.error("\n\u274C Playwright browsers not found!\n");
|
|
538
|
+
if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
|
|
539
|
+
console.error("\n\u274C CRITICAL: Playwright browsers not found!\n");
|
|
458
540
|
console.log("\u{1F4E6} Run: npx playwright install chromium\n");
|
|
459
|
-
failures.push("Playwright browser not installed. Run: npx playwright install chromium");
|
|
460
|
-
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED")) {
|
|
461
|
-
console.error("\n\u274C Cannot connect to dev server!\n");
|
|
541
|
+
failures.push("CRITICAL: Playwright browser not installed. Run: npx playwright install chromium");
|
|
542
|
+
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
|
|
543
|
+
console.error("\n\u274C CRITICAL: Cannot connect to dev server!\n");
|
|
462
544
|
console.log(` Make sure your dev server is running at ${url}
|
|
463
545
|
`);
|
|
464
|
-
failures.push(`Dev server not running at ${url}`);
|
|
546
|
+
failures.push(`CRITICAL: Dev server not running at ${url}`);
|
|
547
|
+
} else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
|
|
548
|
+
console.error("\n\u274C CRITICAL: Component not found on page!\n");
|
|
549
|
+
console.log(` The component selector could not be found within 30 seconds.
|
|
550
|
+
`);
|
|
551
|
+
console.log(` This usually means:
|
|
552
|
+
`);
|
|
553
|
+
console.log(` - The component didn't render
|
|
554
|
+
`);
|
|
555
|
+
console.log(` - The URL is incorrect
|
|
556
|
+
`);
|
|
557
|
+
console.log(` - The component selector in the contract is wrong
|
|
558
|
+
`);
|
|
559
|
+
failures.push(`CRITICAL: Component element not found on page - ${error.message}`);
|
|
560
|
+
} else if (error.message.includes("Target page, context or browser has been closed")) {
|
|
561
|
+
console.error("\n\u274C CRITICAL: Browser/page was closed unexpectedly!\n");
|
|
562
|
+
console.log(` This usually means:
|
|
563
|
+
`);
|
|
564
|
+
console.log(` - The test timeout was too short
|
|
565
|
+
`);
|
|
566
|
+
console.log(` - The browser crashed
|
|
567
|
+
`);
|
|
568
|
+
console.log(` - An external process killed the browser
|
|
569
|
+
`);
|
|
570
|
+
failures.push(`CRITICAL: Browser/page closed unexpectedly - ${error.message}`);
|
|
571
|
+
} else if (error.message.includes("FATAL")) {
|
|
572
|
+
console.error(`
|
|
573
|
+
${error.message}
|
|
574
|
+
`);
|
|
575
|
+
failures.push(error.message);
|
|
465
576
|
} else {
|
|
466
|
-
console.error("\u274C
|
|
467
|
-
|
|
577
|
+
console.error("\n\u274C UNEXPECTED ERROR:", error.message);
|
|
578
|
+
console.error("Stack:", error.stack);
|
|
579
|
+
failures.push(`UNEXPECTED ERROR: ${error.message}`);
|
|
468
580
|
}
|
|
469
581
|
}
|
|
470
582
|
} finally {
|