@stencil/vitest 0.1.0 → 0.1.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.
@@ -1,8 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'child_process';
3
- import { existsSync, unlinkSync, writeFileSync } from 'fs';
4
- import { join } from 'path';
5
- import { createJiti } from 'jiti';
6
3
  function parseArgs(argv) {
7
4
  const parsed = {
8
5
  watch: false,
@@ -40,10 +37,6 @@ function parseArgs(argv) {
40
37
  parsed.port = argv[i + 1];
41
38
  i += 2;
42
39
  break;
43
- case '--stencil-config':
44
- parsed.stencilConfig = argv[i + 1];
45
- i += 2;
46
- break;
47
40
  case '--verbose':
48
41
  case '-v':
49
42
  parsed.verbose = true;
@@ -123,13 +116,12 @@ Default (no flags): Build Stencil in dev mode and run tests once
123
116
  Watch mode: Build Stencil in watch mode and run Vitest with interactive features
124
117
 
125
118
  Stencil Options:
126
- --watch Run Stencil in watch mode (enables Vitest interactive mode)
127
- --prod Build in production mode (default: dev mode)
128
- --serve Start dev server (requires --watch)
129
- --port <number> Dev server port (default: 3333)
130
- --stencil-config <path> Path to stencil config file
131
- --verbose, -v Show detailed logging
132
- --debug Enable Stencil debug mode
119
+ --watch Run Stencil in watch mode (enables Vitest interactive mode)
120
+ --prod Build in production mode (default: dev mode)
121
+ --serve Start dev server (requires --watch)
122
+ --port <number> Dev server port (default: 3333)
123
+ --verbose, -v Show detailed logging
124
+ --debug Enable Stencil debug mode
133
125
 
134
126
  Vitest Options:
135
127
  --project <name> Run tests for specific project
@@ -264,161 +256,6 @@ function handleStencilOutput(data) {
264
256
  }
265
257
  }
266
258
  }
267
- /**
268
- * Extracts screenshot directory patterns from vitest config to ignore in Stencil watch
269
- */
270
- async function getScreenshotPatternsFromVitestConfig(customVitestConfig) {
271
- const patterns = [];
272
- try {
273
- let vitestConfigPath;
274
- // Use custom config if provided via --config flag
275
- if (customVitestConfig) {
276
- const resolvedPath = join(cwd, customVitestConfig);
277
- if (existsSync(resolvedPath)) {
278
- vitestConfigPath = resolvedPath;
279
- }
280
- else if (verbose) {
281
- log(`Specified vitest config not found: ${customVitestConfig}, falling back to defaults`);
282
- }
283
- }
284
- // Look for vitest.config.ts/js in common locations if no custom config
285
- if (!vitestConfigPath) {
286
- const possibleConfigs = [
287
- join(cwd, 'vitest.config.ts'),
288
- join(cwd, 'vitest.config.js'),
289
- join(cwd, 'vitest.config.mjs'),
290
- ];
291
- vitestConfigPath = possibleConfigs.find(existsSync);
292
- }
293
- if (!vitestConfigPath) {
294
- if (verbose) {
295
- log('No vitest config found, using default screenshot patterns');
296
- }
297
- // Return sensible defaults
298
- return [/__screenshots__/, /__snapshots__/, /\.(png|jpg|jpeg|webp|gif)$/];
299
- }
300
- if (verbose) {
301
- log(`Loading vitest config from ${vitestConfigPath}`);
302
- }
303
- // Use jiti to load TypeScript/ESM config
304
- const jiti = createJiti(cwd, { interopDefault: true });
305
- const vitestConfig = await jiti.import(vitestConfigPath);
306
- // Extract screenshot directory from browser test config
307
- const projects = vitestConfig?.default?.test?.projects || vitestConfig?.test?.projects || [];
308
- const customScreenshotDirs = new Set();
309
- for (const project of projects) {
310
- const browserConfig = project?.test?.browser;
311
- if (browserConfig?.enabled) {
312
- const screenshotDir = browserConfig.expect?.toMatchScreenshot?.screenshotDirectory;
313
- if (screenshotDir) {
314
- customScreenshotDirs.add(screenshotDir);
315
- }
316
- }
317
- }
318
- // Add custom screenshot directories if found
319
- for (const dir of customScreenshotDirs) {
320
- patterns.push(new RegExp(`/${dir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`));
321
- }
322
- // Always add default screenshot and snapshot directories
323
- patterns.push(/__screenshots__/);
324
- patterns.push(/__snapshots__/);
325
- patterns.push(/\.vitest-attachments/);
326
- // Always add common image extensions
327
- patterns.push(/\.(png|jpg|jpeg|webp|gif)$/);
328
- if (verbose && patterns.length > 0) {
329
- log(`Extracted ${patterns.length} screenshot patterns from vitest config`);
330
- }
331
- }
332
- catch (error) {
333
- if (verbose) {
334
- log(`Failed to parse vitest config: ${error instanceof Error ? error.message : String(error)}`);
335
- log('Using default screenshot patterns');
336
- }
337
- // Fallback to sensible defaults
338
- return [/__screenshots__/, /__snapshots__/, /\.(png|jpg|jpeg|webp|gif)$/];
339
- }
340
- return patterns.length > 0 ? patterns : [/__screenshots__/, /__snapshots__/, /\.(png|jpg|jpeg|webp|gif)$/];
341
- }
342
- /**
343
- * Creates a temporary stencil config that extends the user's config
344
- * and adds watchIgnoredRegex patterns for screenshots
345
- */
346
- async function createTemporaryStencilConfig(userSpecifiedConfig, vitestConfigPath) {
347
- try {
348
- let userConfigPath;
349
- // Use user-specified config if provided
350
- if (userSpecifiedConfig) {
351
- const resolvedPath = join(cwd, userSpecifiedConfig);
352
- if (existsSync(resolvedPath)) {
353
- userConfigPath = resolvedPath;
354
- }
355
- else {
356
- if (verbose) {
357
- log(`Specified config file not found: ${userSpecifiedConfig}`);
358
- }
359
- return null;
360
- }
361
- }
362
- else {
363
- // Find the user's stencil config using default locations
364
- const possibleConfigs = [
365
- join(cwd, 'stencil.config.ts'),
366
- join(cwd, 'stencil.config.js'),
367
- join(cwd, 'stencil.config.mjs'),
368
- ];
369
- userConfigPath = possibleConfigs.find(existsSync);
370
- if (!userConfigPath) {
371
- if (verbose) {
372
- log('No stencil config found, skipping watchIgnoredRegex injection');
373
- }
374
- return null;
375
- }
376
- }
377
- // Get screenshot patterns to ignore
378
- const screenshotPatterns = await getScreenshotPatternsFromVitestConfig(vitestConfigPath);
379
- // Load the user's config using jiti
380
- const jiti = createJiti(cwd, { interopDefault: true });
381
- const userConfig = await jiti.import(userConfigPath);
382
- // Extract the actual config object
383
- const actualConfig = userConfig.config || userConfig.default?.config || userConfig.default || userConfig;
384
- // Merge with watchIgnoredRegex
385
- const mergedConfig = {
386
- ...actualConfig,
387
- watchIgnoredRegex: [...(actualConfig?.watchIgnoredRegex || []), ...screenshotPatterns],
388
- };
389
- // Create temp file as sibling of stencil.config so tsconfig.json can be found
390
- // Stencil looks for tsconfig.json relative to the config file location
391
- const tempConfigPath = join(cwd, `.stencil-test-${Date.now()}.config.mjs`);
392
- // Serialize the config - convert RegExp objects to strings for the output
393
- const patternsArray = mergedConfig.watchIgnoredRegex.map((pattern) => pattern.toString()).join(',\n ');
394
- // Create config without watchIgnoredRegex first
395
- const { _watchIgnoredRegex, ...configWithoutWatch } = mergedConfig;
396
- // Generate a simple config that doesn't need imports
397
- const tempConfigContent = `
398
- // Auto-generated temporary config by stencil-test
399
- // This extends your stencil config and adds watchIgnoredRegex for screenshot files
400
- export const config = {
401
- ${JSON.stringify(configWithoutWatch, null, 2).slice(2, -2)},
402
- "watchIgnoredRegex": [
403
- ${patternsArray}
404
- ]
405
- };
406
- `;
407
- writeFileSync(tempConfigPath, tempConfigContent, 'utf-8');
408
- if (verbose) {
409
- log(`Created temporary stencil config at ${tempConfigPath}`);
410
- log(`Added ${screenshotPatterns.length} watch ignore patterns`);
411
- }
412
- return tempConfigPath;
413
- }
414
- catch (error) {
415
- if (verbose) {
416
- log(`Failed to create temporary config: ${error instanceof Error ? error.message : String(error)}`);
417
- }
418
- return null;
419
- }
420
- }
421
- let tempStencilConfigPath = null;
422
259
  /**
423
260
  * Clean up child processes and optionally exit with code.
424
261
  */
@@ -435,18 +272,6 @@ function cleanup(exitCode) {
435
272
  stencilProcess.kill();
436
273
  stencilProcess = null;
437
274
  }
438
- // Clean up temporary config file
439
- if (tempStencilConfigPath && existsSync(tempStencilConfigPath)) {
440
- try {
441
- unlinkSync(tempStencilConfigPath);
442
- if (verbose) {
443
- log('Cleaned up temporary stencil config');
444
- }
445
- }
446
- catch {
447
- // Ignore cleanup errors
448
- }
449
- }
450
275
  if (typeof exitCode === 'number') {
451
276
  process.exit(exitCode);
452
277
  }
@@ -454,94 +279,63 @@ function cleanup(exitCode) {
454
279
  // Set up signal handlers for clean shutdown
455
280
  process.on('SIGINT', () => cleanup());
456
281
  process.on('SIGTERM', () => cleanup());
457
- // Main async function to setup and start the processes
458
- (async () => {
459
- // Extract vitest --config path if provided
460
- let vitestConfigPath;
461
- const configIndex = args.vitestArgs.indexOf('--config');
462
- if (configIndex !== -1 && configIndex + 1 < args.vitestArgs.length) {
463
- vitestConfigPath = args.vitestArgs[configIndex + 1];
464
- }
465
- // Create temporary stencil config in watch mode to prevent screenshot infinite loops
466
- if (args.watch) {
467
- tempStencilConfigPath = await createTemporaryStencilConfig(args.stencilConfig, vitestConfigPath);
468
- }
469
- // Build Stencil arguments
470
- const stencilArgs = ['stencil', 'build'];
471
- // Add --dev by default, unless --prod is explicitly passed
472
- if (args.prod) {
473
- stencilArgs.push('--prod');
474
- }
475
- else {
476
- stencilArgs.push('--dev');
477
- }
478
- if (args.watch) {
479
- stencilArgs.push('--watch');
480
- log('Starting Stencil in watch mode...');
481
- // Use temporary config if created, otherwise use user-specified config
482
- if (tempStencilConfigPath) {
483
- stencilArgs.push('--config', tempStencilConfigPath);
484
- if (verbose) {
485
- log('Using temporary config with screenshot ignore patterns');
486
- }
487
- }
488
- else if (args.stencilConfig) {
489
- // If temp config creation failed but user specified a config, use it directly
490
- stencilArgs.push('--config', args.stencilConfig);
491
- }
492
- if (args.serve) {
493
- stencilArgs.push('--serve');
494
- if (args.port) {
495
- stencilArgs.push('--port', args.port);
496
- }
282
+ // Build Stencil arguments
283
+ const stencilArgs = ['stencil', 'build'];
284
+ // Add --dev by default, unless --prod is explicitly passed
285
+ if (args.prod) {
286
+ stencilArgs.push('--prod');
287
+ }
288
+ else {
289
+ stencilArgs.push('--dev');
290
+ }
291
+ if (args.watch) {
292
+ stencilArgs.push('--watch');
293
+ log('Starting Stencil in watch mode...');
294
+ if (args.serve) {
295
+ stencilArgs.push('--serve');
296
+ if (args.port) {
297
+ stencilArgs.push('--port', args.port);
497
298
  }
498
299
  }
499
- else {
500
- log('Building Stencil...');
501
- // In non-watch mode, just pass through the user's config if specified
502
- if (args.stencilConfig) {
503
- stencilArgs.push('--config', args.stencilConfig);
504
- }
300
+ }
301
+ else {
302
+ log('Building Stencil...');
303
+ }
304
+ if (args.debug) {
305
+ stencilArgs.push('--debug');
306
+ }
307
+ // Add any additional stencil args
308
+ stencilArgs.push(...args.stencilArgs);
309
+ stencilProcess = spawn('npx', stencilArgs, {
310
+ cwd,
311
+ shell: true,
312
+ });
313
+ // Pipe stdout and watch for build completion
314
+ stencilProcess.stdout?.on('data', handleStencilOutput);
315
+ stencilProcess.stderr?.on('data', (data) => {
316
+ process.stderr.write(data);
317
+ });
318
+ stencilProcess.on('error', (error) => {
319
+ console.error('[stencil-test] Failed to start Stencil:', error);
320
+ process.exit(1);
321
+ });
322
+ stencilProcess.on('exit', (code) => {
323
+ if (verbose) {
324
+ log(`Stencil exited with code ${code}`);
505
325
  }
506
- if (args.debug) {
507
- stencilArgs.push('--debug');
326
+ // In one-time build mode, stencil exits after build
327
+ // Don't cleanup immediately - let tests finish first
328
+ if (!args.watch) {
329
+ stencilProcess = null;
508
330
  }
509
- // Add any additional stencil args
510
- stencilArgs.push(...args.stencilArgs);
511
- stencilProcess = spawn('npx', stencilArgs, {
512
- cwd,
513
- shell: true,
514
- });
515
- // Pipe stdout and watch for build completion
516
- stencilProcess.stdout?.on('data', handleStencilOutput);
517
- stencilProcess.stderr?.on('data', (data) => {
518
- process.stderr.write(data);
519
- });
520
- stencilProcess.on('error', (error) => {
521
- console.error('[stencil-test] Failed to start Stencil:', error);
522
- process.exit(1);
523
- });
524
- stencilProcess.on('exit', (code) => {
525
- if (verbose) {
526
- log(`Stencil exited with code ${code}`);
527
- }
528
- // In one-time build mode, stencil exits after build
529
- // Don't cleanup immediately - let tests finish first
530
- if (!args.watch) {
531
- stencilProcess = null;
532
- }
533
- else {
534
- // In watch mode, stencil shouldn't exit - if it does, something went wrong
535
- log(`Stencil exited unexpectedly with code ${code}`);
536
- cleanup(code || 1);
537
- }
538
- });
539
- // Watch mode: Vitest handles test file watching automatically
540
- // Stencil handles component file watching automatically
541
- if (args.watch && verbose) {
542
- log('Watch mode enabled - Vitest will watch test files and Stencil will watch component files');
331
+ else {
332
+ // In watch mode, stencil shouldn't exit - if it does, something went wrong
333
+ log(`Stencil exited unexpectedly with code ${code}`);
334
+ cleanup(code || 1);
543
335
  }
544
- })().catch((error) => {
545
- console.error('[stencil-test] Fatal error:', error);
546
- cleanup(1);
547
336
  });
337
+ // Watch mode: Vitest handles test file watching automatically
338
+ // Stencil handles component file watching automatically
339
+ if (args.watch && verbose) {
340
+ log('Watch mode enabled - Vitest will watch test files and Stencil will watch component files');
341
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Setup file for happy-dom environment
3
+ * Auto-loaded when using a project named 'happy-dom' or containing 'happy-dom'
4
+ *
5
+ * Configures happy-dom with Stencil-specific setup
6
+ * happy-dom generally has better built-in support than jsdom, so fewer polyfills are needed
7
+ */
8
+ /**
9
+ * Main setup function for happy-dom environment
10
+ */
11
+ export declare function setup(): Promise<void>;
12
+ //# sourceMappingURL=happy-dom-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"happy-dom-setup.d.ts","sourceRoot":"","sources":["../../src/testing/happy-dom-setup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,wBAAsB,KAAK,kBAG1B"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Setup file for happy-dom environment
3
+ * Auto-loaded when using a project named 'happy-dom' or containing 'happy-dom'
4
+ *
5
+ * Configures happy-dom with Stencil-specific setup
6
+ * happy-dom generally has better built-in support than jsdom, so fewer polyfills are needed
7
+ */
8
+ /**
9
+ * Main setup function for happy-dom environment
10
+ */
11
+ export async function setup() {
12
+ // happy-dom is generally more complete than jsdom out of the box
13
+ // Add polyfills here as needed when issues are discovered
14
+ }
15
+ // Auto-run setup
16
+ setup();
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Setup jsdom environment for Stencil component testing
3
+ *
4
+ * This module provides polyfills and initialization for testing Stencil components
5
+ * in a jsdom environment. It handles:
6
+ * - Polyfilling adoptedStyleSheets for Shadow DOM
7
+ * - Polyfilling CSS support detection
8
+ * - Polyfilling requestAnimationFrame and related APIs
9
+ * - Loading and initializing Stencil lazy loader
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // vitest.config.ts
14
+ * export default defineVitestConfig({
15
+ * test: {
16
+ * setupFiles: ['@stencil/test-utils/jsdom-setup'],
17
+ * },
18
+ * });
19
+ * ```
20
+ */
21
+ /**
22
+ * Apply polyfills to a jsdom window object for Stencil components
23
+ * This function is reused by both the setup file and the custom environment
24
+ */
25
+ export declare function applyJsdomPolyfills(window: Window & typeof globalThis): void;
26
+ /**
27
+ * Initialize jsdom environment for Stencil testing
28
+ */
29
+ export declare function setup(): Promise<void>;
30
+ //# sourceMappingURL=jsdom-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdom-setup.d.ts","sourceRoot":"","sources":["../../src/testing/jsdom-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,UAAU,QA2DrE;AAED;;GAEG;AACH,wBAAsB,KAAK,kBAa1B"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Setup jsdom environment for Stencil component testing
3
+ *
4
+ * This module provides polyfills and initialization for testing Stencil components
5
+ * in a jsdom environment. It handles:
6
+ * - Polyfilling adoptedStyleSheets for Shadow DOM
7
+ * - Polyfilling CSS support detection
8
+ * - Polyfilling requestAnimationFrame and related APIs
9
+ * - Loading and initializing Stencil lazy loader
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // vitest.config.ts
14
+ * export default defineVitestConfig({
15
+ * test: {
16
+ * setupFiles: ['@stencil/test-utils/jsdom-setup'],
17
+ * },
18
+ * });
19
+ * ```
20
+ */
21
+ /**
22
+ * Apply polyfills to a jsdom window object for Stencil components
23
+ * This function is reused by both the setup file and the custom environment
24
+ */
25
+ export function applyJsdomPolyfills(window) {
26
+ // Polyfill adoptedStyleSheets for Shadow DOM
27
+ if (!window.document.adoptedStyleSheets) {
28
+ Object.defineProperty(window.document, 'adoptedStyleSheets', {
29
+ value: [],
30
+ writable: true,
31
+ configurable: true,
32
+ });
33
+ }
34
+ if (typeof window.ShadowRoot !== 'undefined' &&
35
+ !Object.prototype.hasOwnProperty.call(window.ShadowRoot.prototype, 'adoptedStyleSheets')) {
36
+ Object.defineProperty(window.ShadowRoot.prototype, 'adoptedStyleSheets', {
37
+ get() {
38
+ if (!this._adoptedStyleSheets) {
39
+ this._adoptedStyleSheets = [];
40
+ }
41
+ return this._adoptedStyleSheets;
42
+ },
43
+ set(value) {
44
+ this._adoptedStyleSheets = value;
45
+ },
46
+ configurable: true,
47
+ });
48
+ }
49
+ // Polyfill CSS support
50
+ if (!window.CSS) {
51
+ window.CSS = {
52
+ supports: () => true,
53
+ };
54
+ }
55
+ // Polyfill scrollTo
56
+ window.scrollTo = () => { };
57
+ // Add requestAnimationFrame and related APIs
58
+ if (!window.requestAnimationFrame) {
59
+ window.requestAnimationFrame = (cb) => {
60
+ return setTimeout(cb, 0);
61
+ };
62
+ }
63
+ if (!window.cancelAnimationFrame) {
64
+ window.cancelAnimationFrame = (id) => {
65
+ clearTimeout(id);
66
+ };
67
+ }
68
+ if (!window.requestIdleCallback) {
69
+ window.requestIdleCallback = (cb) => {
70
+ return setTimeout(cb, 0);
71
+ };
72
+ }
73
+ if (!window.cancelIdleCallback) {
74
+ window.cancelIdleCallback = (id) => {
75
+ clearTimeout(id);
76
+ };
77
+ }
78
+ }
79
+ /**
80
+ * Initialize jsdom environment for Stencil testing
81
+ */
82
+ export async function setup() {
83
+ // Only run in jsdom environment (Node.js with DOM)
84
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
85
+ return;
86
+ }
87
+ // Skip if running in actual browser (Playwright, WebdriverIO, etc.)
88
+ // In actual browsers, process is undefined or doesn't have cwd
89
+ if (typeof process === 'undefined' || typeof process.cwd !== 'function') {
90
+ return;
91
+ }
92
+ applyJsdomPolyfills(window);
93
+ }
94
+ // Auto-run setup when imported
95
+ setup();
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Setup for mock-doc environment
3
+ * This file is automatically loaded when using the mock-doc environment in Node.js
4
+ *
5
+ * IMPORTANT: This should only be imported/executed in Node.js runtime, not in browsers.
6
+ * The projects-based config ensures this is only loaded for node:mock-doc projects.
7
+ */
8
+ import { setupGlobal, teardownGlobal } from '@stencil/core/mock-doc';
9
+ /**
10
+ * Apply polyfills to a window object for Stencil components
11
+ * This function is reused by both the setup file and the custom environment
12
+ */
13
+ export declare function applyMockDocPolyfills(win: any): void;
14
+ declare let win: any;
15
+ declare let doc: any;
16
+ export { win, doc, setupGlobal, teardownGlobal };
17
+ //# sourceMappingURL=mock-doc-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-doc-setup.d.ts","sourceRoot":"","sources":["../../src/testing/mock-doc-setup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAc,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEjF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,QAmD7C;AAOD,QAAA,IAAI,GAAG,EAAE,GAAG,CAAC;AACb,QAAA,IAAI,GAAG,EAAE,GAAG,CAAC;AA6Bb,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Setup for mock-doc environment
3
+ * This file is automatically loaded when using the mock-doc environment in Node.js
4
+ *
5
+ * IMPORTANT: This should only be imported/executed in Node.js runtime, not in browsers.
6
+ * The projects-based config ensures this is only loaded for node:mock-doc projects.
7
+ */
8
+ import { MockWindow, setupGlobal, teardownGlobal } from '@stencil/core/mock-doc';
9
+ /**
10
+ * Apply polyfills to a window object for Stencil components
11
+ * This function is reused by both the setup file and the custom environment
12
+ */
13
+ export function applyMockDocPolyfills(win) {
14
+ // Set baseURI manually
15
+ Object.defineProperty(win.document, 'baseURI', {
16
+ value: 'http://localhost:3000/',
17
+ writable: false,
18
+ configurable: true,
19
+ });
20
+ // Setup global with mock-doc globals
21
+ setupGlobal(win);
22
+ // Add MessageEvent if it doesn't exist (needed by Node's undici)
23
+ if (!win.MessageEvent) {
24
+ win.MessageEvent = class MessageEvent extends win.Event {
25
+ constructor(type, eventInitDict) {
26
+ super(type, eventInitDict);
27
+ }
28
+ };
29
+ }
30
+ // Add AbortController if it doesn't exist
31
+ if (!win.AbortController) {
32
+ win.AbortController = class AbortController {
33
+ constructor() {
34
+ this.signal = {
35
+ aborted: false,
36
+ addEventListener: () => { },
37
+ removeEventListener: () => { },
38
+ dispatchEvent: () => true,
39
+ };
40
+ }
41
+ abort() {
42
+ this.signal.aborted = true;
43
+ }
44
+ };
45
+ }
46
+ // Add requestAnimationFrame and related APIs
47
+ win.requestAnimationFrame = (cb) => {
48
+ return setTimeout(cb, 0);
49
+ };
50
+ win.cancelAnimationFrame = (id) => {
51
+ clearTimeout(id);
52
+ };
53
+ win.requestIdleCallback = (cb) => {
54
+ return setTimeout(cb, 0);
55
+ };
56
+ win.cancelIdleCallback = (id) => {
57
+ clearTimeout(id);
58
+ };
59
+ }
60
+ // Only setup mock-doc if we're actually in Node.js (not a real browser)
61
+ // Check for Node.js-specific globals that don't exist in browsers
62
+ const isNodeEnvironment = typeof process !== 'undefined' && process?.versions?.node !== undefined && typeof window === 'undefined';
63
+ let win;
64
+ let doc;
65
+ if (!isNodeEnvironment) {
66
+ // We're in a real browser, skip mock-doc setup and export real globals
67
+ win = typeof window !== 'undefined' ? window : undefined;
68
+ doc = typeof document !== 'undefined' ? document : undefined;
69
+ }
70
+ else {
71
+ // We're in Node.js, setup mock-doc
72
+ // Create mock window with URL
73
+ win = new MockWindow('http://localhost:3000/');
74
+ doc = win.document;
75
+ // Apply polyfills
76
+ applyMockDocPolyfills(win);
77
+ // Assign to globalThis
78
+ globalThis.window = win;
79
+ globalThis.document = doc;
80
+ globalThis.HTMLElement = win.HTMLElement;
81
+ globalThis.CustomEvent = win.CustomEvent;
82
+ globalThis.Event = win.Event;
83
+ globalThis.Element = win.Element;
84
+ globalThis.Node = win.Node;
85
+ globalThis.DocumentFragment = win.DocumentFragment;
86
+ globalThis.requestAnimationFrame = win.requestAnimationFrame;
87
+ globalThis.cancelAnimationFrame = win.cancelAnimationFrame;
88
+ }
89
+ // Export the mock window for use in custom setup
90
+ export { win, doc, setupGlobal, teardownGlobal };
@@ -0,0 +1,19 @@
1
+ import type { Config as StencilConfig } from '@stencil/core/internal';
2
+ /**
3
+ * Load Stencil configuration from a file path
4
+ * Uses jiti to handle TypeScript files in Node.js
5
+ */
6
+ export declare function loadStencilConfig(configPath: string): Promise<StencilConfig | undefined>;
7
+ /**
8
+ * Get the source directory from Stencil config
9
+ */
10
+ export declare function getStencilSrcDir(config?: StencilConfig): string;
11
+ /**
12
+ * Get all output directories from Stencil config for exclusion
13
+ */
14
+ export declare function getStencilOutputDirs(config?: StencilConfig): string[];
15
+ /**
16
+ * Create resolve aliases from Stencil config
17
+ */
18
+ export declare function getStencilResolveAliases(config?: StencilConfig): Record<string, string>;
19
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAItE;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAsB9F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAE/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,EAAE,CAyCrE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBvF"}
@@ -0,0 +1,92 @@
1
+ import { existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ /**
4
+ * Load Stencil configuration from a file path
5
+ * Uses jiti to handle TypeScript files in Node.js
6
+ */
7
+ export async function loadStencilConfig(configPath) {
8
+ const resolvedPath = resolve(process.cwd(), configPath);
9
+ if (!existsSync(resolvedPath)) {
10
+ console.warn(`Stencil config not found at ${resolvedPath}`);
11
+ return undefined;
12
+ }
13
+ try {
14
+ // Use jiti for loading TypeScript configs in Node.js
15
+ const { createJiti } = await import('jiti');
16
+ const jiti = createJiti(process.cwd(), {
17
+ interopDefault: true,
18
+ moduleCache: false,
19
+ });
20
+ const configModule = (await jiti.import(resolvedPath));
21
+ return configModule.config || configModule.default || configModule;
22
+ }
23
+ catch (error) {
24
+ console.error(`Failed to load Stencil config from ${resolvedPath}:`, error);
25
+ return undefined;
26
+ }
27
+ }
28
+ /**
29
+ * Get the source directory from Stencil config
30
+ */
31
+ export function getStencilSrcDir(config) {
32
+ return config?.srcDir || 'src';
33
+ }
34
+ /**
35
+ * Get all output directories from Stencil config for exclusion
36
+ */
37
+ export function getStencilOutputDirs(config) {
38
+ if (!config?.outputTargets) {
39
+ return ['dist', 'www', 'build', '.stencil'];
40
+ }
41
+ const outputDirs = new Set();
42
+ config.outputTargets.forEach((target) => {
43
+ // Add common output directories based on target type
44
+ if (target.dir) {
45
+ outputDirs.add(target.dir);
46
+ }
47
+ if (target.buildDir) {
48
+ outputDirs.add(target.buildDir);
49
+ }
50
+ // Handle Stencil default directories for output types that don't specify dir
51
+ // Based on Stencil's default behavior:
52
+ if (target.type === 'dist' || target.type === 'dist-custom-elements' || target.type === 'dist-hydrate-script') {
53
+ // These all default to 'dist' directory
54
+ if (!target.dir && !target.buildDir) {
55
+ outputDirs.add('dist');
56
+ }
57
+ }
58
+ if (target.type === 'www') {
59
+ // www defaults to 'www' directory
60
+ if (!target.dir && !target.buildDir) {
61
+ outputDirs.add('www');
62
+ }
63
+ }
64
+ // Note: esmLoaderPath is a relative import path like '../loader', not a directory
65
+ // We should NOT extract this as an exclude pattern
66
+ });
67
+ // Always include common output directories
68
+ outputDirs.add('.stencil');
69
+ // Filter out invalid paths (those that navigate up with ..)
70
+ const validDirs = Array.from(outputDirs).filter((dir) => !dir.includes('..'));
71
+ return validDirs.length > 0 ? validDirs : ['dist', 'www', 'build', '.stencil'];
72
+ }
73
+ /**
74
+ * Create resolve aliases from Stencil config
75
+ */
76
+ export function getStencilResolveAliases(config) {
77
+ const srcDir = getStencilSrcDir(config);
78
+ const aliases = {
79
+ '@': resolve(process.cwd(), srcDir),
80
+ };
81
+ // Add component alias if components directory exists
82
+ const componentsDir = resolve(process.cwd(), srcDir, 'components');
83
+ if (existsSync(componentsDir)) {
84
+ aliases['@components'] = componentsDir;
85
+ }
86
+ // Add utils alias if utils directory exists
87
+ const utilsDir = resolve(process.cwd(), srcDir, 'utils');
88
+ if (existsSync(utilsDir)) {
89
+ aliases['@utils'] = utilsDir;
90
+ }
91
+ return aliases;
92
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/stenciljs/vitest"
6
6
  },
7
- "version": "0.1.0",
7
+ "version": "0.1.2",
8
8
  "description": "First-class testing utilities for Stencil design systems with Vitest",
9
9
  "license": "MIT",
10
10
  "type": "module",
@@ -56,22 +56,6 @@
56
56
  "web components",
57
57
  "component testing"
58
58
  ],
59
- "scripts": {
60
- "build": "tsc && tsc -p tsconfig.bin.json",
61
- "dev": "tsc --watch",
62
- "test:e2e": "pnpm --filter test-project test",
63
- "test:unit": "vitest run",
64
- "test:unit:watch": "vitest",
65
- "test:engines": "pnpm installed-check --no-workspaces --ignore-dev",
66
- "lint": "eslint .",
67
- "lint:fix": "eslint . --fix",
68
- "format": "prettier --check .",
69
- "format:fix": "prettier --write .",
70
- "knip": "knip",
71
- "quality": "pnpm format && pnpm lint && pnpm knip",
72
- "quality:fix": "pnpm format:fix && pnpm lint:fix && pnpm knip",
73
- "release": "semantic-release"
74
- },
75
59
  "publishConfig": {
76
60
  "access": "public"
77
61
  },
@@ -108,7 +92,7 @@
108
92
  "dependencies": {
109
93
  "jiti": "^2.6.1",
110
94
  "local-pkg": "^1.1.2",
111
- "vitest-environment-stencil": "workspace:*"
95
+ "vitest-environment-stencil": "0.1.2"
112
96
  },
113
97
  "devDependencies": {
114
98
  "@eslint/js": "^9.39.2",
@@ -132,5 +116,18 @@
132
116
  "engines": {
133
117
  "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
134
118
  },
135
- "packageManager": "pnpm@10.26.0"
136
- }
119
+ "scripts": {
120
+ "build": "tsc && tsc -p tsconfig.bin.json",
121
+ "dev": "tsc --watch",
122
+ "test": "pnpm --filter test-project test",
123
+ "test:engines": "pnpm installed-check --no-workspaces --ignore-dev",
124
+ "lint": "eslint .",
125
+ "lint:fix": "eslint . --fix",
126
+ "format": "prettier --check .",
127
+ "format:fix": "prettier --write .",
128
+ "knip": "knip",
129
+ "quality": "pnpm format && pnpm lint && pnpm knip",
130
+ "quality:fix": "pnpm format:fix && pnpm lint:fix && pnpm knip",
131
+ "release": "semantic-release"
132
+ }
133
+ }