aria-ease 5.0.3 → 6.0.1

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,8 @@
1
1
  import {
2
2
  ContractReporter,
3
+ closeSharedBrowser,
3
4
  contract_default
4
- } from "./chunk-TUWQNVQJ.js";
5
+ } from "./chunk-7RMRFSJL.js";
5
6
  import "./chunk-I2KLQ2HA.js";
6
7
 
7
8
  // src/utils/test/src/test.ts
@@ -74,56 +75,58 @@ async function testUiComponent(componentName, component, url) {
74
75
  if (!componentName || typeof componentName !== "string") {
75
76
  throw new Error("\u274C testUiComponent requires a valid componentName (string)");
76
77
  }
77
- if (!component || !(component instanceof HTMLElement)) {
78
- throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
78
+ if (!url && (!component || !(component instanceof HTMLElement))) {
79
+ throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
79
80
  }
80
81
  if (url && typeof url !== "string") {
81
82
  throw new Error("\u274C testUiComponent url parameter must be a string");
82
83
  }
83
84
  let results;
84
- try {
85
- results = await axe(component);
86
- } catch (error) {
87
- throw new Error(
88
- `\u274C Axe accessibility scan failed
85
+ if (component) {
86
+ try {
87
+ results = await axe(component);
88
+ } catch (error) {
89
+ throw new Error(
90
+ `\u274C Axe accessibility scan failed
89
91
  Error: ${error instanceof Error ? error.message : String(error)}`
90
- );
92
+ );
93
+ }
94
+ } else {
95
+ results = { violations: [] };
91
96
  }
92
- async function checkDevServer(testUrl) {
93
- const urlsToTry = testUrl ? [testUrl] : [
94
- "http://localhost:5173",
95
- "http://localhost:3000",
96
- "http://localhost:8080",
97
- "http://localhost:4173"
98
- ];
99
- for (const serverUrl of urlsToTry) {
100
- try {
101
- const response = await fetch(serverUrl, {
102
- method: "HEAD",
103
- signal: AbortSignal.timeout(1e3)
104
- });
105
- if (response.ok || response.status === 304) {
106
- return serverUrl;
107
- }
108
- } catch {
109
- return null;
97
+ async function checkDevServer(url2) {
98
+ try {
99
+ const response = await fetch(url2, {
100
+ method: "HEAD",
101
+ signal: AbortSignal.timeout(1e3)
102
+ });
103
+ if (response.ok || response.status === 304) {
104
+ return url2;
110
105
  }
106
+ } catch {
107
+ return null;
111
108
  }
112
109
  return null;
113
110
  }
114
111
  let contract;
115
112
  try {
116
- const devServerUrl = await checkDevServer(url);
117
- if (devServerUrl) {
118
- console.log(`\u{1F3AD} Running Playwright E2E tests on ${devServerUrl}`);
119
- const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-O22AQ4RK.js");
120
- contract = await runContractTestsPlaywright(componentName, devServerUrl);
121
- } else {
122
- console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
123
- console.log(`\u26A0\uFE0F No dev server detected. Some tests may be skipped.
124
- For full coverage start your dev server and provide a URL.
125
- `);
113
+ if (url) {
114
+ const devServerUrl = await checkDevServer(url);
115
+ if (devServerUrl) {
116
+ console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
117
+ const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-HL2VPEEV.js");
118
+ contract = await runContractTestsPlaywright(componentName, devServerUrl);
119
+ } else {
120
+ throw new Error(
121
+ `\u274C Dev server not running at ${url}
122
+ Please start your dev server and try again.`
123
+ );
124
+ }
125
+ } else if (component) {
126
+ console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
126
127
  contract = await runContractTests(componentName, component);
128
+ } else {
129
+ throw new Error("\u274C Either component or URL must be provided");
127
130
  }
128
131
  } catch (error) {
129
132
  if (error instanceof Error) {
@@ -189,7 +192,11 @@ if (typeof window === "undefined") {
189
192
  );
190
193
  };
191
194
  }
195
+ async function cleanupTests() {
196
+ await closeSharedBrowser();
197
+ }
192
198
  export {
199
+ cleanupTests,
193
200
  runTest,
194
201
  testUiComponent
195
202
  };
@@ -204,7 +204,7 @@ ${"\u2550".repeat(60)}`);
204
204
  } else if (totalFailures === 0) {
205
205
  this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
206
206
  if (this.skipped > 0) {
207
- this.log(`\u25CB ${this.skipped} tests skipped (jsdom limitation)`);
207
+ this.log(`\u25CB ${this.skipped} tests skipped`);
208
208
  }
209
209
  if (this.optionalSuggestions > 0) {
210
210
  this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
@@ -253,9 +253,55 @@ ${"\u2550".repeat(60)}`);
253
253
  }
254
254
  };
255
255
 
256
+ // src/utils/test/contract/playwrightTestHarness.ts
257
+ import { chromium } from "playwright";
258
+ var sharedBrowser = null;
259
+ var sharedContext = null;
260
+ async function getOrCreateBrowser() {
261
+ if (!sharedBrowser) {
262
+ sharedBrowser = await chromium.launch({
263
+ headless: true,
264
+ // Launch with clean browser profile - no extensions, no user data
265
+ args: [
266
+ "--disable-extensions",
267
+ "--disable-blink-features=AutomationControlled"
268
+ ]
269
+ });
270
+ }
271
+ return sharedBrowser;
272
+ }
273
+ async function getOrCreateContext() {
274
+ if (!sharedContext) {
275
+ const browser = await getOrCreateBrowser();
276
+ sharedContext = await browser.newContext({
277
+ // Isolated context - no permissions, no geolocation, etc.
278
+ permissions: [],
279
+ // Ignore HTTPS errors for local dev servers
280
+ ignoreHTTPSErrors: true
281
+ });
282
+ }
283
+ return sharedContext;
284
+ }
285
+ async function createTestPage() {
286
+ const context = await getOrCreateContext();
287
+ return await context.newPage();
288
+ }
289
+ async function closeSharedBrowser() {
290
+ if (sharedContext) {
291
+ await sharedContext.close();
292
+ sharedContext = null;
293
+ }
294
+ if (sharedBrowser) {
295
+ await sharedBrowser.close();
296
+ sharedBrowser = null;
297
+ }
298
+ }
299
+
256
300
  export {
257
301
  __export,
258
302
  __reExport,
259
303
  contract_default,
260
- ContractReporter
304
+ ContractReporter,
305
+ createTestPage,
306
+ closeSharedBrowser
261
307
  };
@@ -2,11 +2,9 @@ import {
2
2
  ContractReporter,
3
3
  __export,
4
4
  __reExport,
5
- contract_default
6
- } from "./chunk-KJ33RDSC.js";
7
-
8
- // src/utils/test/contract/contractTestRunnerPlaywright.ts
9
- import { chromium } from "playwright";
5
+ contract_default,
6
+ createTestPage
7
+ } from "./chunk-PDZQOXUN.js";
10
8
 
11
9
  // node_modules/@playwright/test/index.mjs
12
10
  var test_exports = {};
@@ -35,30 +33,33 @@ async function runContractTestsPlaywright(componentName, url) {
35
33
  const failures = [];
36
34
  const passes = [];
37
35
  const skipped = [];
38
- let browser = null;
36
+ let page = null;
39
37
  try {
40
- browser = await chromium.launch({ headless: true });
41
- const context = await browser.newContext();
42
- const page = await context.newPage();
43
- await page.goto(url, {
44
- waitUntil: "domcontentloaded",
45
- timeout: 9e5
46
- });
47
- await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
38
+ page = await createTestPage();
39
+ if (url) {
40
+ await page.goto(url, {
41
+ waitUntil: "domcontentloaded",
42
+ timeout: 3e4
43
+ });
44
+ await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
45
+ }
48
46
  const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
49
47
  if (!mainSelector) {
50
48
  throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
51
49
  }
52
- await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 9e5 });
50
+ await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
53
51
  if (componentName === "menu" && componentContract.selectors.trigger) {
54
52
  await page.locator(componentContract.selectors.trigger).first().waitFor({
55
53
  state: "visible",
56
- timeout: 1e4
54
+ timeout: 5e3
57
55
  }).catch(() => {
58
56
  console.warn("Menu trigger not visible, continuing with tests...");
59
57
  });
60
58
  }
61
59
  async function resolveRelativeTarget(selector, relative) {
60
+ if (!page) {
61
+ throw new Error("Page is not initialized");
62
+ }
62
63
  const items = await page.locator(selector).all();
63
64
  switch (relative) {
64
65
  case "first":
@@ -128,8 +129,8 @@ async function runContractTestsPlaywright(componentName, url) {
128
129
  for (const dynamicTest of componentContract.dynamic || []) {
129
130
  const { action, assertions } = dynamicTest;
130
131
  const failuresBeforeTest = failures.length;
131
- if (componentContract.selectors.listbox || componentContract.selectors.container) {
132
- const popupSelector = componentContract.selectors.listbox || componentContract.selectors.container;
132
+ if (componentContract.selectors.popup) {
133
+ const popupSelector = componentContract.selectors.popup;
133
134
  if (!popupSelector) continue;
134
135
  const popupElement = page.locator(popupSelector).first();
135
136
  const isPopupVisible = await popupElement.isVisible().catch(() => false);
@@ -145,16 +146,16 @@ async function runContractTestsPlaywright(componentName, url) {
145
146
  const closeElement = page.locator(closeSelector).first();
146
147
  await closeElement.focus();
147
148
  await page.keyboard.press("Escape");
148
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
149
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
149
150
  }
150
151
  if (!menuClosed && componentContract.selectors.trigger) {
151
152
  const triggerElement = page.locator(componentContract.selectors.trigger).first();
152
153
  await triggerElement.click();
153
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
154
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
154
155
  }
155
156
  if (!menuClosed) {
156
157
  await page.mouse.click(10, 10);
157
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
158
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
158
159
  }
159
160
  if (!menuClosed) {
160
161
  throw new Error(
@@ -174,6 +175,23 @@ This indicates a problem with the menu component's close functionality.`
174
175
  }
175
176
  }
176
177
  }
178
+ if (componentContract.selectors.panel && componentContract.selectors.trigger && !componentContract.selectors.popup) {
179
+ const triggerSelector = componentContract.selectors.trigger;
180
+ const panelSelector = componentContract.selectors.panel;
181
+ if (triggerSelector && panelSelector) {
182
+ const allTriggers = await page.locator(triggerSelector).all();
183
+ for (const trigger of allTriggers) {
184
+ const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
185
+ const triggerPanel = await trigger.getAttribute("aria-controls");
186
+ if (isExpanded && triggerPanel) {
187
+ await trigger.click();
188
+ const panel = page.locator(`#${triggerPanel}`);
189
+ await (0, test_exports.expect)(panel).toBeHidden({ timeout: 1e3 }).catch(() => {
190
+ });
191
+ }
192
+ }
193
+ }
194
+ }
177
195
  let shouldSkipTest = false;
178
196
  for (const act of action) {
179
197
  if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
@@ -334,7 +352,7 @@ This indicates a problem with the menu component's close functionality.`
334
352
  }
335
353
  if (assertion.assertion === "toBeVisible") {
336
354
  try {
337
- await (0, test_exports.expect)(target).toBeVisible({ timeout: 3e3 });
355
+ await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
338
356
  passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
339
357
  } catch {
340
358
  const debugState = await page.evaluate((sel) => {
@@ -348,7 +366,7 @@ This indicates a problem with the menu component's close functionality.`
348
366
  }
349
367
  if (assertion.assertion === "notToBeVisible") {
350
368
  try {
351
- await (0, test_exports.expect)(target).toBeHidden({ timeout: 5e3 });
369
+ await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
352
370
  passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
353
371
  } catch {
354
372
  const debugState = await page.evaluate((sel) => {
@@ -450,7 +468,7 @@ This indicates a problem with the menu component's close functionality.`
450
468
  }
451
469
  }
452
470
  } finally {
453
- if (browser) await browser.close();
471
+ if (page) await page.close();
454
472
  }
455
473
  return { passes, failures, skipped };
456
474
  }
package/dist/index.cjs CHANGED
@@ -228,7 +228,7 @@ ${"\u2550".repeat(60)}`);
228
228
  } else if (totalFailures === 0) {
229
229
  this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
230
230
  if (this.skipped > 0) {
231
- this.log(`\u25CB ${this.skipped} tests skipped (jsdom limitation)`);
231
+ this.log(`\u25CB ${this.skipped} tests skipped`);
232
232
  }
233
233
  if (this.optionalSuggestions > 0) {
234
234
  this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
@@ -279,6 +279,56 @@ ${"\u2550".repeat(60)}`);
279
279
  }
280
280
  });
281
281
 
282
+ // src/utils/test/contract/playwrightTestHarness.ts
283
+ async function getOrCreateBrowser() {
284
+ if (!sharedBrowser) {
285
+ sharedBrowser = await import_playwright.chromium.launch({
286
+ headless: true,
287
+ // Launch with clean browser profile - no extensions, no user data
288
+ args: [
289
+ "--disable-extensions",
290
+ "--disable-blink-features=AutomationControlled"
291
+ ]
292
+ });
293
+ }
294
+ return sharedBrowser;
295
+ }
296
+ async function getOrCreateContext() {
297
+ if (!sharedContext) {
298
+ const browser = await getOrCreateBrowser();
299
+ sharedContext = await browser.newContext({
300
+ // Isolated context - no permissions, no geolocation, etc.
301
+ permissions: [],
302
+ // Ignore HTTPS errors for local dev servers
303
+ ignoreHTTPSErrors: true
304
+ });
305
+ }
306
+ return sharedContext;
307
+ }
308
+ async function createTestPage() {
309
+ const context = await getOrCreateContext();
310
+ return await context.newPage();
311
+ }
312
+ async function closeSharedBrowser() {
313
+ if (sharedContext) {
314
+ await sharedContext.close();
315
+ sharedContext = null;
316
+ }
317
+ if (sharedBrowser) {
318
+ await sharedBrowser.close();
319
+ sharedBrowser = null;
320
+ }
321
+ }
322
+ var import_playwright, sharedBrowser, sharedContext;
323
+ var init_playwrightTestHarness = __esm({
324
+ "src/utils/test/contract/playwrightTestHarness.ts"() {
325
+ "use strict";
326
+ import_playwright = require("playwright");
327
+ sharedBrowser = null;
328
+ sharedContext = null;
329
+ }
330
+ });
331
+
282
332
  // node_modules/@playwright/test/index.mjs
283
333
  var test_exports = {};
284
334
  __export(test_exports, {
@@ -314,30 +364,33 @@ async function runContractTestsPlaywright(componentName, url) {
314
364
  const failures = [];
315
365
  const passes = [];
316
366
  const skipped = [];
317
- let browser = null;
367
+ let page = null;
318
368
  try {
319
- browser = await import_playwright.chromium.launch({ headless: true });
320
- const context = await browser.newContext();
321
- const page = await context.newPage();
322
- await page.goto(url, {
323
- waitUntil: "domcontentloaded",
324
- timeout: 9e5
325
- });
326
- await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
369
+ page = await createTestPage();
370
+ if (url) {
371
+ await page.goto(url, {
372
+ waitUntil: "domcontentloaded",
373
+ timeout: 3e4
374
+ });
375
+ await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
376
+ }
327
377
  const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
328
378
  if (!mainSelector) {
329
379
  throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
330
380
  }
331
- await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 9e5 });
381
+ await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
332
382
  if (componentName === "menu" && componentContract.selectors.trigger) {
333
383
  await page.locator(componentContract.selectors.trigger).first().waitFor({
334
384
  state: "visible",
335
- timeout: 1e4
385
+ timeout: 5e3
336
386
  }).catch(() => {
337
387
  console.warn("Menu trigger not visible, continuing with tests...");
338
388
  });
339
389
  }
340
390
  async function resolveRelativeTarget(selector, relative) {
391
+ if (!page) {
392
+ throw new Error("Page is not initialized");
393
+ }
341
394
  const items = await page.locator(selector).all();
342
395
  switch (relative) {
343
396
  case "first":
@@ -407,8 +460,8 @@ async function runContractTestsPlaywright(componentName, url) {
407
460
  for (const dynamicTest of componentContract.dynamic || []) {
408
461
  const { action, assertions } = dynamicTest;
409
462
  const failuresBeforeTest = failures.length;
410
- if (componentContract.selectors.listbox || componentContract.selectors.container) {
411
- const popupSelector = componentContract.selectors.listbox || componentContract.selectors.container;
463
+ if (componentContract.selectors.popup) {
464
+ const popupSelector = componentContract.selectors.popup;
412
465
  if (!popupSelector) continue;
413
466
  const popupElement = page.locator(popupSelector).first();
414
467
  const isPopupVisible = await popupElement.isVisible().catch(() => false);
@@ -424,16 +477,16 @@ async function runContractTestsPlaywright(componentName, url) {
424
477
  const closeElement = page.locator(closeSelector).first();
425
478
  await closeElement.focus();
426
479
  await page.keyboard.press("Escape");
427
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
480
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
428
481
  }
429
482
  if (!menuClosed && componentContract.selectors.trigger) {
430
483
  const triggerElement = page.locator(componentContract.selectors.trigger).first();
431
484
  await triggerElement.click();
432
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
485
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
433
486
  }
434
487
  if (!menuClosed) {
435
488
  await page.mouse.click(10, 10);
436
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 3e3 }).then(() => true).catch(() => false);
489
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
437
490
  }
438
491
  if (!menuClosed) {
439
492
  throw new Error(
@@ -453,6 +506,23 @@ This indicates a problem with the menu component's close functionality.`
453
506
  }
454
507
  }
455
508
  }
509
+ if (componentContract.selectors.panel && componentContract.selectors.trigger && !componentContract.selectors.popup) {
510
+ const triggerSelector = componentContract.selectors.trigger;
511
+ const panelSelector = componentContract.selectors.panel;
512
+ if (triggerSelector && panelSelector) {
513
+ const allTriggers = await page.locator(triggerSelector).all();
514
+ for (const trigger of allTriggers) {
515
+ const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
516
+ const triggerPanel = await trigger.getAttribute("aria-controls");
517
+ if (isExpanded && triggerPanel) {
518
+ await trigger.click();
519
+ const panel = page.locator(`#${triggerPanel}`);
520
+ await (0, test_exports.expect)(panel).toBeHidden({ timeout: 1e3 }).catch(() => {
521
+ });
522
+ }
523
+ }
524
+ }
525
+ }
456
526
  let shouldSkipTest = false;
457
527
  for (const act of action) {
458
528
  if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
@@ -613,7 +683,7 @@ This indicates a problem with the menu component's close functionality.`
613
683
  }
614
684
  if (assertion.assertion === "toBeVisible") {
615
685
  try {
616
- await (0, test_exports.expect)(target).toBeVisible({ timeout: 3e3 });
686
+ await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
617
687
  passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
618
688
  } catch {
619
689
  const debugState = await page.evaluate((sel) => {
@@ -627,7 +697,7 @@ This indicates a problem with the menu component's close functionality.`
627
697
  }
628
698
  if (assertion.assertion === "notToBeVisible") {
629
699
  try {
630
- await (0, test_exports.expect)(target).toBeHidden({ timeout: 5e3 });
700
+ await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
631
701
  passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
632
702
  } catch {
633
703
  const debugState = await page.evaluate((sel) => {
@@ -729,19 +799,19 @@ This indicates a problem with the menu component's close functionality.`
729
799
  }
730
800
  }
731
801
  } finally {
732
- if (browser) await browser.close();
802
+ if (page) await page.close();
733
803
  }
734
804
  return { passes, failures, skipped };
735
805
  }
736
- var import_playwright, import_fs, import_meta2;
806
+ var import_fs, import_meta2;
737
807
  var init_contractTestRunnerPlaywright = __esm({
738
808
  "src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
739
809
  "use strict";
740
- import_playwright = require("playwright");
741
810
  init_test();
742
811
  import_fs = require("fs");
743
812
  init_contract();
744
813
  init_ContractReporter();
814
+ init_playwrightTestHarness();
745
815
  import_meta2 = {};
746
816
  }
747
817
  });
@@ -749,6 +819,7 @@ var init_contractTestRunnerPlaywright = __esm({
749
819
  // index.ts
750
820
  var index_exports = {};
751
821
  __export(index_exports, {
822
+ cleanupTests: () => cleanupTests,
752
823
  makeAccordionAccessible: () => makeAccordionAccessible,
753
824
  makeBlockAccessible: () => makeBlockAccessible,
754
825
  makeCheckboxAccessible: () => makeCheckboxAccessible,
@@ -1994,60 +2065,63 @@ async function runContractTests(componentName, component) {
1994
2065
  }
1995
2066
 
1996
2067
  // src/utils/test/src/test.ts
2068
+ init_playwrightTestHarness();
1997
2069
  async function testUiComponent(componentName, component, url) {
1998
2070
  if (!componentName || typeof componentName !== "string") {
1999
2071
  throw new Error("\u274C testUiComponent requires a valid componentName (string)");
2000
2072
  }
2001
- if (!component || !(component instanceof HTMLElement)) {
2002
- throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
2073
+ if (!url && (!component || !(component instanceof HTMLElement))) {
2074
+ throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
2003
2075
  }
2004
2076
  if (url && typeof url !== "string") {
2005
2077
  throw new Error("\u274C testUiComponent url parameter must be a string");
2006
2078
  }
2007
2079
  let results;
2008
- try {
2009
- results = await (0, import_jest_axe.axe)(component);
2010
- } catch (error) {
2011
- throw new Error(
2012
- `\u274C Axe accessibility scan failed
2080
+ if (component) {
2081
+ try {
2082
+ results = await (0, import_jest_axe.axe)(component);
2083
+ } catch (error) {
2084
+ throw new Error(
2085
+ `\u274C Axe accessibility scan failed
2013
2086
  Error: ${error instanceof Error ? error.message : String(error)}`
2014
- );
2087
+ );
2088
+ }
2089
+ } else {
2090
+ results = { violations: [] };
2015
2091
  }
2016
- async function checkDevServer(testUrl) {
2017
- const urlsToTry = testUrl ? [testUrl] : [
2018
- "http://localhost:5173",
2019
- "http://localhost:3000",
2020
- "http://localhost:8080",
2021
- "http://localhost:4173"
2022
- ];
2023
- for (const serverUrl of urlsToTry) {
2024
- try {
2025
- const response = await fetch(serverUrl, {
2026
- method: "HEAD",
2027
- signal: AbortSignal.timeout(1e3)
2028
- });
2029
- if (response.ok || response.status === 304) {
2030
- return serverUrl;
2031
- }
2032
- } catch {
2033
- return null;
2092
+ async function checkDevServer(url2) {
2093
+ try {
2094
+ const response = await fetch(url2, {
2095
+ method: "HEAD",
2096
+ signal: AbortSignal.timeout(1e3)
2097
+ });
2098
+ if (response.ok || response.status === 304) {
2099
+ return url2;
2034
2100
  }
2101
+ } catch {
2102
+ return null;
2035
2103
  }
2036
2104
  return null;
2037
2105
  }
2038
2106
  let contract;
2039
2107
  try {
2040
- const devServerUrl = await checkDevServer(url);
2041
- if (devServerUrl) {
2042
- console.log(`\u{1F3AD} Running Playwright E2E tests on ${devServerUrl}`);
2043
- const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
2044
- contract = await runContractTestsPlaywright2(componentName, devServerUrl);
2045
- } else {
2046
- console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
2047
- console.log(`\u26A0\uFE0F No dev server detected. Some tests may be skipped.
2048
- For full coverage start your dev server and provide a URL.
2049
- `);
2108
+ if (url) {
2109
+ const devServerUrl = await checkDevServer(url);
2110
+ if (devServerUrl) {
2111
+ console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
2112
+ const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
2113
+ contract = await runContractTestsPlaywright2(componentName, devServerUrl);
2114
+ } else {
2115
+ throw new Error(
2116
+ `\u274C Dev server not running at ${url}
2117
+ Please start your dev server and try again.`
2118
+ );
2119
+ }
2120
+ } else if (component) {
2121
+ console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
2050
2122
  contract = await runContractTests(componentName, component);
2123
+ } else {
2124
+ throw new Error("\u274C Either component or URL must be provided");
2051
2125
  }
2052
2126
  } catch (error) {
2053
2127
  if (error instanceof Error) {
@@ -2113,8 +2187,12 @@ if (typeof window === "undefined") {
2113
2187
  );
2114
2188
  };
2115
2189
  }
2190
+ async function cleanupTests() {
2191
+ await closeSharedBrowser();
2192
+ }
2116
2193
  // Annotate the CommonJS export names for ESM import in node:
2117
2194
  0 && (module.exports = {
2195
+ cleanupTests,
2118
2196
  makeAccordionAccessible,
2119
2197
  makeBlockAccessible,
2120
2198
  makeCheckboxAccessible,
package/dist/index.d.cts CHANGED
@@ -143,9 +143,14 @@ declare function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, lis
143
143
  * Runs static and interactions accessibility test on UI components.
144
144
  * @param {string} componentName The name of the component contract to test against
145
145
  * @param {HTMLElement} component The UI component to be tested
146
- * @param {string} url Optional URL to run full Playwright E2E tests (requires dev server running)
146
+ * @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
147
147
  */
148
148
 
149
- declare function testUiComponent(componentName: string, component: HTMLElement, url?: string): Promise<JestAxeResult>;
149
+ declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null): Promise<JestAxeResult>;
150
+ /**
151
+ * Cleanup function to close the shared Playwright browser
152
+ * Call this in afterAll() or after all tests complete
153
+ */
154
+ declare function cleanupTests(): Promise<void>;
150
155
 
151
- export { makeAccordionAccessible, makeBlockAccessible, makeCheckboxAccessible, makeComboboxAccessible, makeMenuAccessible, makeRadioAccessible, makeToggleAccessible, testUiComponent };
156
+ export { cleanupTests, makeAccordionAccessible, makeBlockAccessible, makeCheckboxAccessible, makeComboboxAccessible, makeMenuAccessible, makeRadioAccessible, makeToggleAccessible, testUiComponent };