aria-ease 5.0.2 → 6.0.0

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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var default2 = require('playwright/test');
4
3
  var playwright = require('playwright');
4
+ var test = require('@playwright/test');
5
5
  var fs = require('fs');
6
6
  var jestAxe = require('jest-axe');
7
7
  var fs$1 = require('fs/promises');
@@ -9,31 +9,10 @@ var fs$1 = require('fs/promises');
9
9
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
- function _interopNamespace(e) {
13
- if (e && e.__esModule) return e;
14
- var n = Object.create(null);
15
- if (e) {
16
- Object.keys(e).forEach(function (k) {
17
- if (k !== 'default') {
18
- var d = Object.getOwnPropertyDescriptor(e, k);
19
- Object.defineProperty(n, k, d.get ? d : {
20
- enumerable: true,
21
- get: function () { return e[k]; }
22
- });
23
- }
24
- });
25
- }
26
- n.default = e;
27
- return Object.freeze(n);
28
- }
29
-
30
- var default2__namespace = /*#__PURE__*/_interopNamespace(default2);
31
12
  var fs__default = /*#__PURE__*/_interopDefault(fs$1);
32
13
 
33
14
  var __defProp = Object.defineProperty;
34
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
35
15
  var __getOwnPropNames = Object.getOwnPropertyNames;
36
- var __hasOwnProp = Object.prototype.hasOwnProperty;
37
16
  var __esm = (fn, res) => function __init() {
38
17
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
39
18
  };
@@ -41,15 +20,6 @@ var __export = (target, all) => {
41
20
  for (var name in all)
42
21
  __defProp(target, name, { get: all[name], enumerable: true });
43
22
  };
44
- var __copyProps = (to, from, except, desc) => {
45
- if (from && typeof from === "object" || typeof from === "function") {
46
- for (let key of __getOwnPropNames(from))
47
- if (!__hasOwnProp.call(to, key) && key !== except)
48
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
49
- }
50
- return to;
51
- };
52
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
53
23
 
54
24
  // src/utils/test/contract/contract.json
55
25
  var contract_default;
@@ -63,6 +33,10 @@ var init_contract = __esm({
63
33
  combobox: {
64
34
  path: "./contracts/ComboboxContract.json",
65
35
  component: "combobox"
36
+ },
37
+ accordion: {
38
+ path: "./contracts/AccordionContract.json",
39
+ component: "accordion"
66
40
  }
67
41
  };
68
42
  }
@@ -243,7 +217,7 @@ ${"\u2550".repeat(60)}`);
243
217
  } else if (totalFailures === 0) {
244
218
  this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
245
219
  if (this.skipped > 0) {
246
- this.log(`\u25CB ${this.skipped} tests skipped (jsdom limitation)`);
220
+ this.log(`\u25CB ${this.skipped} tests skipped`);
247
221
  }
248
222
  if (this.optionalSuggestions > 0) {
249
223
  this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
@@ -293,15 +267,50 @@ ${"\u2550".repeat(60)}`);
293
267
  };
294
268
  }
295
269
  });
296
-
297
- // node_modules/@playwright/test/index.mjs
298
- var test_exports = {};
299
- __export(test_exports, {
300
- default: () => default2__namespace.default
301
- });
302
- var init_test = __esm({
303
- "node_modules/@playwright/test/index.mjs"() {
304
- __reExport(test_exports, default2__namespace);
270
+ async function getOrCreateBrowser() {
271
+ if (!sharedBrowser) {
272
+ sharedBrowser = await playwright.chromium.launch({
273
+ headless: true,
274
+ // Launch with clean browser profile - no extensions, no user data
275
+ args: [
276
+ "--disable-extensions",
277
+ "--disable-blink-features=AutomationControlled"
278
+ ]
279
+ });
280
+ }
281
+ return sharedBrowser;
282
+ }
283
+ async function getOrCreateContext() {
284
+ if (!sharedContext) {
285
+ const browser = await getOrCreateBrowser();
286
+ sharedContext = await browser.newContext({
287
+ // Isolated context - no permissions, no geolocation, etc.
288
+ permissions: [],
289
+ // Ignore HTTPS errors for local dev servers
290
+ ignoreHTTPSErrors: true
291
+ });
292
+ }
293
+ return sharedContext;
294
+ }
295
+ async function createTestPage() {
296
+ const context = await getOrCreateContext();
297
+ return await context.newPage();
298
+ }
299
+ async function closeSharedBrowser() {
300
+ if (sharedContext) {
301
+ await sharedContext.close();
302
+ sharedContext = null;
303
+ }
304
+ if (sharedBrowser) {
305
+ await sharedBrowser.close();
306
+ sharedBrowser = null;
307
+ }
308
+ }
309
+ var sharedBrowser, sharedContext;
310
+ var init_playwrightTestHarness = __esm({
311
+ "src/utils/test/contract/playwrightTestHarness.ts"() {
312
+ sharedBrowser = null;
313
+ sharedContext = null;
305
314
  }
306
315
  });
307
316
 
@@ -326,34 +335,35 @@ async function runContractTestsPlaywright(componentName, url) {
326
335
  const failures = [];
327
336
  const passes = [];
328
337
  const skipped = [];
329
- let browser = null;
338
+ let page = null;
339
+ const useNavigation = !!url;
330
340
  try {
331
- browser = await playwright.chromium.launch({ headless: true });
332
- const context = await browser.newContext();
333
- const page = await context.newPage();
334
- await page.goto(url, {
335
- waitUntil: "domcontentloaded",
336
- timeout: 9e4
337
- });
341
+ page = await createTestPage();
342
+ if (url) {
343
+ await page.goto(url, {
344
+ waitUntil: "domcontentloaded",
345
+ timeout: 3e4
346
+ });
347
+ await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
348
+ }
338
349
  const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
339
350
  if (!mainSelector) {
340
351
  throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
341
352
  }
342
- await page.waitForSelector(mainSelector, { timeout: 9e4 });
353
+ const elementTimeout = useNavigation ? 3e4 : 5e3;
354
+ await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: elementTimeout });
343
355
  if (componentName === "menu" && componentContract.selectors.trigger) {
344
- await page.waitForFunction(
345
- (selector) => {
346
- const trigger = document.querySelector(selector);
347
- return trigger && trigger.getAttribute("data-menu-initialized") === "true";
348
- },
349
- componentContract.selectors.trigger,
350
- { timeout: 1e4 }
351
- ).catch(() => {
352
- console.warn("Menu initialization signal not detected, continuing with tests...");
356
+ await page.locator(componentContract.selectors.trigger).first().waitFor({
357
+ state: "visible",
358
+ timeout: 5e3
359
+ }).catch(() => {
360
+ console.warn("Menu trigger not visible, continuing with tests...");
353
361
  });
354
- await page.waitForTimeout(300);
355
362
  }
356
363
  async function resolveRelativeTarget(selector, relative) {
364
+ if (!page) {
365
+ throw new Error("Page is not initialized");
366
+ }
357
367
  const items = await page.locator(selector).all();
358
368
  switch (relative) {
359
369
  case "first":
@@ -423,11 +433,11 @@ async function runContractTestsPlaywright(componentName, url) {
423
433
  for (const dynamicTest of componentContract.dynamic || []) {
424
434
  const { action, assertions } = dynamicTest;
425
435
  const failuresBeforeTest = failures.length;
426
- if (componentContract.selectors.listbox || componentContract.selectors.container) {
427
- const popupSelector = componentContract.selectors.listbox || componentContract.selectors.container;
436
+ if (componentContract.selectors.popup) {
437
+ const popupSelector = componentContract.selectors.popup;
428
438
  if (!popupSelector) continue;
429
439
  const popupElement = page.locator(popupSelector).first();
430
- const isPopupVisible = await popupElement.isVisible();
440
+ const isPopupVisible = await popupElement.isVisible().catch(() => false);
431
441
  if (isPopupVisible) {
432
442
  let menuClosed = false;
433
443
  let closeSelector = componentContract.selectors.input;
@@ -439,73 +449,35 @@ async function runContractTestsPlaywright(componentName, url) {
439
449
  if (closeSelector) {
440
450
  const closeElement = page.locator(closeSelector).first();
441
451
  await closeElement.focus();
442
- await page.waitForTimeout(200);
443
452
  await page.keyboard.press("Escape");
444
- menuClosed = await page.waitForFunction(
445
- (selector) => {
446
- const popup = document.querySelector(selector);
447
- return popup && getComputedStyle(popup).display === "none";
448
- },
449
- popupSelector,
450
- { timeout: 3e3 }
451
- ).then(() => true).catch(() => false);
453
+ menuClosed = await test.expect(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
452
454
  }
453
455
  if (!menuClosed && componentContract.selectors.trigger) {
454
456
  const triggerElement = page.locator(componentContract.selectors.trigger).first();
455
457
  await triggerElement.click();
456
- await page.waitForTimeout(500);
457
- menuClosed = await page.waitForFunction(
458
- (selector) => {
459
- const popup = document.querySelector(selector);
460
- return popup && getComputedStyle(popup).display === "none";
461
- },
462
- popupSelector,
463
- { timeout: 3e3 }
464
- ).then(() => true).catch(() => false);
458
+ menuClosed = await test.expect(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
465
459
  }
466
460
  if (!menuClosed) {
467
461
  await page.mouse.click(10, 10);
468
- await page.waitForTimeout(500);
469
- menuClosed = await page.waitForFunction(
470
- (selector) => {
471
- const popup = document.querySelector(selector);
472
- return popup && getComputedStyle(popup).display === "none";
473
- },
474
- popupSelector,
475
- { timeout: 3e3 }
476
- ).then(() => true).catch(() => false);
477
- if (menuClosed) {
478
- console.log("\u{1F3AF} Strategy 3 (Click outside) worked");
479
- }
462
+ menuClosed = await test.expect(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
480
463
  }
481
464
  if (!menuClosed) {
482
- throw new Error(
483
- `\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
465
+ if (useNavigation) {
466
+ throw new Error(
467
+ `\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
484
468
  1. Escape key
485
469
  2. Clicking trigger
486
470
  3. Clicking outside
487
471
  This indicates a problem with the menu component's close functionality.`
488
- );
489
- }
490
- if (componentName === "menu" && componentContract.selectors.trigger) {
491
- await page.waitForFunction(
492
- (selector) => {
493
- const trigger = document.querySelector(selector);
494
- return document.activeElement === trigger;
495
- },
496
- componentContract.selectors.trigger,
497
- { timeout: 2e3 }
498
- ).catch(async () => {
499
- const triggerElement = page.locator(componentContract.selectors.trigger).first();
500
- await triggerElement.focus();
501
- await page.waitForTimeout(200);
502
- });
472
+ );
473
+ }
503
474
  }
504
- await page.waitForTimeout(500);
505
475
  if (componentContract.selectors.input) {
506
- const inputElement = page.locator(componentContract.selectors.input).first();
507
- await inputElement.clear();
508
- await page.waitForTimeout(100);
476
+ await page.locator(componentContract.selectors.input).first().clear();
477
+ }
478
+ if (componentName === "menu" && componentContract.selectors.trigger) {
479
+ const triggerElement = page.locator(componentContract.selectors.trigger).first();
480
+ await triggerElement.focus();
509
481
  }
510
482
  }
511
483
  }
@@ -549,7 +521,6 @@ This indicates a problem with the menu component's close functionality.`
549
521
  continue;
550
522
  }
551
523
  await page.locator(focusSelector).first().focus();
552
- await page.waitForTimeout(100);
553
524
  }
554
525
  if (act.type === "type" && act.value) {
555
526
  const typeSelector = componentContract.selectors[act.target];
@@ -558,7 +529,6 @@ This indicates a problem with the menu component's close functionality.`
558
529
  continue;
559
530
  }
560
531
  await page.locator(typeSelector).first().fill(act.value);
561
- await page.waitForTimeout(100);
562
532
  }
563
533
  if (act.type === "click") {
564
534
  if (act.target === "document") {
@@ -575,7 +545,6 @@ This indicates a problem with the menu component's close functionality.`
575
545
  continue;
576
546
  }
577
547
  await relativeElement.click();
578
- await page.waitForTimeout(componentName === "menu" ? 800 : 200);
579
548
  } else {
580
549
  const actionSelector = componentContract.selectors[act.target];
581
550
  if (!actionSelector) {
@@ -583,7 +552,6 @@ This indicates a problem with the menu component's close functionality.`
583
552
  continue;
584
553
  }
585
554
  await page.locator(actionSelector).first().click();
586
- await page.waitForTimeout(componentName === "menu" ? 800 : 200);
587
555
  }
588
556
  }
589
557
  if (act.type === "keypress" && act.key) {
@@ -606,9 +574,7 @@ This indicates a problem with the menu component's close functionality.`
606
574
  keyValue = keyValue.replace(/ /g, "");
607
575
  }
608
576
  if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
609
- await page.waitForTimeout(componentName === "menu" ? 200 : 100);
610
577
  await page.keyboard.press(keyValue);
611
- await page.waitForTimeout(componentName === "menu" ? 300 : 100);
612
578
  } else {
613
579
  const keypressSelector = componentContract.selectors[act.target];
614
580
  if (!keypressSelector) {
@@ -637,7 +603,6 @@ This indicates a problem with the menu component's close functionality.`
637
603
  continue;
638
604
  }
639
605
  await relativeElement.hover();
640
- await page.waitForTimeout(100);
641
606
  } else {
642
607
  const hoverSelector = componentContract.selectors[act.target];
643
608
  if (!hoverSelector) {
@@ -645,12 +610,9 @@ This indicates a problem with the menu component's close functionality.`
645
610
  continue;
646
611
  }
647
612
  await page.locator(hoverSelector).first().hover();
648
- await page.waitForTimeout(100);
649
613
  }
650
614
  }
651
- await page.waitForTimeout(componentName === "menu" ? 200 : 100);
652
615
  }
653
- await page.waitForTimeout(componentName === "menu" ? 300 : 100);
654
616
  for (const assertion of assertions) {
655
617
  let target;
656
618
  if (assertion.target === "relative") {
@@ -679,7 +641,7 @@ This indicates a problem with the menu component's close functionality.`
679
641
  }
680
642
  if (assertion.assertion === "toBeVisible") {
681
643
  try {
682
- await (0, test_exports.expect)(target).toBeVisible({ timeout: 3e3 });
644
+ await test.expect(target).toBeVisible({ timeout: 2e3 });
683
645
  passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
684
646
  } catch {
685
647
  const debugState = await page.evaluate((sel) => {
@@ -693,7 +655,7 @@ This indicates a problem with the menu component's close functionality.`
693
655
  }
694
656
  if (assertion.assertion === "notToBeVisible") {
695
657
  try {
696
- await (0, test_exports.expect)(target).toBeHidden({ timeout: 5e3 });
658
+ await test.expect(target).toBeHidden({ timeout: 2e3 });
697
659
  passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
698
660
  } catch {
699
661
  const debugState = await page.evaluate((sel) => {
@@ -715,7 +677,7 @@ This indicates a problem with the menu component's close functionality.`
715
677
  failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
716
678
  }
717
679
  } else {
718
- await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: 3e3 });
680
+ await test.expect(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: 3e3 });
719
681
  passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
720
682
  }
721
683
  } catch {
@@ -745,7 +707,7 @@ This indicates a problem with the menu component's close functionality.`
745
707
  }
746
708
  if (assertion.assertion === "toHaveFocus") {
747
709
  try {
748
- await (0, test_exports.expect)(target).toBeFocused({ timeout: 5e3 });
710
+ await test.expect(target).toBeFocused({ timeout: 5e3 });
749
711
  passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
750
712
  } catch {
751
713
  const actualFocus = await page.evaluate(() => {
@@ -795,15 +757,15 @@ This indicates a problem with the menu component's close functionality.`
795
757
  }
796
758
  }
797
759
  } finally {
798
- if (browser) await browser.close();
760
+ if (page) await page.close();
799
761
  }
800
762
  return { passes, failures, skipped };
801
763
  }
802
764
  var init_contractTestRunnerPlaywright = __esm({
803
765
  "src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
804
- init_test();
805
766
  init_contract();
806
767
  init_ContractReporter();
768
+ init_playwrightTestHarness();
807
769
  }
808
770
  });
809
771
 
@@ -871,60 +833,63 @@ async function runContractTests(componentName, component) {
871
833
  }
872
834
 
873
835
  // src/utils/test/src/test.ts
836
+ init_playwrightTestHarness();
874
837
  async function testUiComponent(componentName, component, url) {
875
838
  if (!componentName || typeof componentName !== "string") {
876
839
  throw new Error("\u274C testUiComponent requires a valid componentName (string)");
877
840
  }
878
- if (!component || !(component instanceof HTMLElement)) {
879
- throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
841
+ if (!url && (!component || !(component instanceof HTMLElement))) {
842
+ throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
880
843
  }
881
844
  if (url && typeof url !== "string") {
882
845
  throw new Error("\u274C testUiComponent url parameter must be a string");
883
846
  }
884
847
  let results;
885
- try {
886
- results = await jestAxe.axe(component);
887
- } catch (error) {
888
- throw new Error(
889
- `\u274C Axe accessibility scan failed
848
+ if (component) {
849
+ try {
850
+ results = await jestAxe.axe(component);
851
+ } catch (error) {
852
+ throw new Error(
853
+ `\u274C Axe accessibility scan failed
890
854
  Error: ${error instanceof Error ? error.message : String(error)}`
891
- );
855
+ );
856
+ }
857
+ } else {
858
+ results = { violations: [] };
892
859
  }
893
- async function checkDevServer(testUrl) {
894
- const urlsToTry = testUrl ? [testUrl] : [
895
- "http://localhost:5173",
896
- "http://localhost:3000",
897
- "http://localhost:8080",
898
- "http://localhost:4173"
899
- ];
900
- for (const serverUrl of urlsToTry) {
901
- try {
902
- const response = await fetch(serverUrl, {
903
- method: "HEAD",
904
- signal: AbortSignal.timeout(1e3)
905
- });
906
- if (response.ok || response.status === 304) {
907
- return serverUrl;
908
- }
909
- } catch {
910
- return null;
860
+ async function checkDevServer(url2) {
861
+ try {
862
+ const response = await fetch(url2, {
863
+ method: "HEAD",
864
+ signal: AbortSignal.timeout(1e3)
865
+ });
866
+ if (response.ok || response.status === 304) {
867
+ return url2;
911
868
  }
869
+ } catch {
870
+ return null;
912
871
  }
913
872
  return null;
914
873
  }
915
874
  let contract;
916
875
  try {
917
- const devServerUrl = await checkDevServer(url);
918
- if (devServerUrl) {
919
- console.log(`\u{1F3AD} Running Playwright E2E tests on ${devServerUrl}`);
920
- const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
921
- contract = await runContractTestsPlaywright2(componentName, devServerUrl);
922
- } else {
923
- console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
924
- console.log(`\u26A0\uFE0F No dev server detected. Some tests may be skipped.
925
- For full coverage start your dev server and provide a URL.
926
- `);
876
+ if (url) {
877
+ const devServerUrl = await checkDevServer(url);
878
+ if (devServerUrl) {
879
+ console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
880
+ const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
881
+ contract = await runContractTestsPlaywright2(componentName, devServerUrl);
882
+ } else {
883
+ throw new Error(
884
+ `\u274C Dev server not running at ${url}
885
+ Please start your dev server and try again.`
886
+ );
887
+ }
888
+ } else if (component) {
889
+ console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
927
890
  contract = await runContractTests(componentName, component);
891
+ } else {
892
+ throw new Error("\u274C Either component or URL must be provided");
928
893
  }
929
894
  } catch (error) {
930
895
  if (error instanceof Error) {
@@ -990,5 +955,9 @@ if (typeof window === "undefined") {
990
955
  );
991
956
  };
992
957
  }
958
+ async function cleanupTests() {
959
+ await closeSharedBrowser();
960
+ }
993
961
 
962
+ exports.cleanupTests = cleanupTests;
994
963
  exports.testUiComponent = testUiComponent;
@@ -8,10 +8,15 @@ interface JestAxeResult {
8
8
  * Runs static and interactions accessibility test on UI components.
9
9
  * @param {string} componentName The name of the component contract to test against
10
10
  * @param {HTMLElement} component The UI component to be tested
11
- * @param {string} url Optional URL to run full Playwright E2E tests (requires dev server running)
11
+ * @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
12
12
  */
13
13
 
14
- declare function testUiComponent(componentName: string, component: HTMLElement, url?: string): Promise<JestAxeResult>;
14
+ declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null): Promise<JestAxeResult>;
15
15
  declare let runTest: () => Promise<void>;
16
+ /**
17
+ * Cleanup function to close the shared Playwright browser
18
+ * Call this in afterAll() or after all tests complete
19
+ */
20
+ declare function cleanupTests(): Promise<void>;
16
21
 
17
- export { runTest, testUiComponent };
22
+ export { cleanupTests, runTest, testUiComponent };
@@ -8,10 +8,15 @@ interface JestAxeResult {
8
8
  * Runs static and interactions accessibility test on UI components.
9
9
  * @param {string} componentName The name of the component contract to test against
10
10
  * @param {HTMLElement} component The UI component to be tested
11
- * @param {string} url Optional URL to run full Playwright E2E tests (requires dev server running)
11
+ * @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
12
12
  */
13
13
 
14
- declare function testUiComponent(componentName: string, component: HTMLElement, url?: string): Promise<JestAxeResult>;
14
+ declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null): Promise<JestAxeResult>;
15
15
  declare let runTest: () => Promise<void>;
16
+ /**
17
+ * Cleanup function to close the shared Playwright browser
18
+ * Call this in afterAll() or after all tests complete
19
+ */
20
+ declare function cleanupTests(): Promise<void>;
16
21
 
17
- export { runTest, testUiComponent };
22
+ export { cleanupTests, runTest, testUiComponent };
@@ -1,4 +1,4 @@
1
- import { ContractReporter, contract_default } from './chunk-AVCZIF6G.js';
1
+ import { closeSharedBrowser, ContractReporter, contract_default } from './chunk-7RMRFSJL.js';
2
2
  import { axe } from 'jest-axe';
3
3
  import fs from 'fs/promises';
4
4
 
@@ -67,56 +67,58 @@ async function testUiComponent(componentName, component, url) {
67
67
  if (!componentName || typeof componentName !== "string") {
68
68
  throw new Error("\u274C testUiComponent requires a valid componentName (string)");
69
69
  }
70
- if (!component || !(component instanceof HTMLElement)) {
71
- throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
70
+ if (!url && (!component || !(component instanceof HTMLElement))) {
71
+ throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
72
72
  }
73
73
  if (url && typeof url !== "string") {
74
74
  throw new Error("\u274C testUiComponent url parameter must be a string");
75
75
  }
76
76
  let results;
77
- try {
78
- results = await axe(component);
79
- } catch (error) {
80
- throw new Error(
81
- `\u274C Axe accessibility scan failed
77
+ if (component) {
78
+ try {
79
+ results = await axe(component);
80
+ } catch (error) {
81
+ throw new Error(
82
+ `\u274C Axe accessibility scan failed
82
83
  Error: ${error instanceof Error ? error.message : String(error)}`
83
- );
84
+ );
85
+ }
86
+ } else {
87
+ results = { violations: [] };
84
88
  }
85
- async function checkDevServer(testUrl) {
86
- const urlsToTry = testUrl ? [testUrl] : [
87
- "http://localhost:5173",
88
- "http://localhost:3000",
89
- "http://localhost:8080",
90
- "http://localhost:4173"
91
- ];
92
- for (const serverUrl of urlsToTry) {
93
- try {
94
- const response = await fetch(serverUrl, {
95
- method: "HEAD",
96
- signal: AbortSignal.timeout(1e3)
97
- });
98
- if (response.ok || response.status === 304) {
99
- return serverUrl;
100
- }
101
- } catch {
102
- return null;
89
+ async function checkDevServer(url2) {
90
+ try {
91
+ const response = await fetch(url2, {
92
+ method: "HEAD",
93
+ signal: AbortSignal.timeout(1e3)
94
+ });
95
+ if (response.ok || response.status === 304) {
96
+ return url2;
103
97
  }
98
+ } catch {
99
+ return null;
104
100
  }
105
101
  return null;
106
102
  }
107
103
  let contract;
108
104
  try {
109
- const devServerUrl = await checkDevServer(url);
110
- if (devServerUrl) {
111
- console.log(`\u{1F3AD} Running Playwright E2E tests on ${devServerUrl}`);
112
- const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-BFHRDJTJ.js');
113
- contract = await runContractTestsPlaywright(componentName, devServerUrl);
114
- } else {
115
- console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
116
- console.log(`\u26A0\uFE0F No dev server detected. Some tests may be skipped.
117
- For full coverage start your dev server and provide a URL.
118
- `);
105
+ if (url) {
106
+ const devServerUrl = await checkDevServer(url);
107
+ if (devServerUrl) {
108
+ console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
109
+ const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-CREJNINL.js');
110
+ contract = await runContractTestsPlaywright(componentName, devServerUrl);
111
+ } else {
112
+ throw new Error(
113
+ `\u274C Dev server not running at ${url}
114
+ Please start your dev server and try again.`
115
+ );
116
+ }
117
+ } else if (component) {
118
+ console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
119
119
  contract = await runContractTests(componentName, component);
120
+ } else {
121
+ throw new Error("\u274C Either component or URL must be provided");
120
122
  }
121
123
  } catch (error) {
122
124
  if (error instanceof Error) {
@@ -182,5 +184,8 @@ if (typeof window === "undefined") {
182
184
  );
183
185
  };
184
186
  }
187
+ async function cleanupTests() {
188
+ await closeSharedBrowser();
189
+ }
185
190
 
186
- export { runTest, testUiComponent };
191
+ export { cleanupTests, runTest, testUiComponent };