@stencil/vitest 0.0.1-dev.20260109124515.90fb962

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.
Files changed (53) hide show
  1. package/README.md +296 -0
  2. package/dist/bin/stencil-test.d.ts +3 -0
  3. package/dist/bin/stencil-test.d.ts.map +1 -0
  4. package/dist/bin/stencil-test.js +341 -0
  5. package/dist/config.d.ts +73 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +302 -0
  8. package/dist/environments/env/happy-dom.d.ts +4 -0
  9. package/dist/environments/env/happy-dom.d.ts.map +1 -0
  10. package/dist/environments/env/happy-dom.js +15 -0
  11. package/dist/environments/env/jsdom.d.ts +4 -0
  12. package/dist/environments/env/jsdom.d.ts.map +1 -0
  13. package/dist/environments/env/jsdom.js +32 -0
  14. package/dist/environments/env/mock-doc.d.ts +4 -0
  15. package/dist/environments/env/mock-doc.d.ts.map +1 -0
  16. package/dist/environments/env/mock-doc.js +14 -0
  17. package/dist/environments/stencil.d.ts +28 -0
  18. package/dist/environments/stencil.d.ts.map +1 -0
  19. package/dist/environments/stencil.js +71 -0
  20. package/dist/environments/types.d.ts +6 -0
  21. package/dist/environments/types.d.ts.map +1 -0
  22. package/dist/environments/types.js +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +5 -0
  26. package/dist/setup/config-loader.d.ts +19 -0
  27. package/dist/setup/config-loader.d.ts.map +1 -0
  28. package/dist/setup/config-loader.js +92 -0
  29. package/dist/setup/happy-dom-setup.d.ts +12 -0
  30. package/dist/setup/happy-dom-setup.d.ts.map +1 -0
  31. package/dist/setup/happy-dom-setup.js +16 -0
  32. package/dist/setup/jsdom-setup.d.ts +30 -0
  33. package/dist/setup/jsdom-setup.d.ts.map +1 -0
  34. package/dist/setup/jsdom-setup.js +95 -0
  35. package/dist/setup/mock-doc-setup.d.ts +17 -0
  36. package/dist/setup/mock-doc-setup.d.ts.map +1 -0
  37. package/dist/setup/mock-doc-setup.js +90 -0
  38. package/dist/testing/html-serializer.d.ts +27 -0
  39. package/dist/testing/html-serializer.d.ts.map +1 -0
  40. package/dist/testing/html-serializer.js +152 -0
  41. package/dist/testing/matchers.d.ts +181 -0
  42. package/dist/testing/matchers.d.ts.map +1 -0
  43. package/dist/testing/matchers.js +460 -0
  44. package/dist/testing/render.d.ts +11 -0
  45. package/dist/testing/render.d.ts.map +1 -0
  46. package/dist/testing/render.js +118 -0
  47. package/dist/testing/snapshot-serializer.d.ts +17 -0
  48. package/dist/testing/snapshot-serializer.d.ts.map +1 -0
  49. package/dist/testing/snapshot-serializer.js +50 -0
  50. package/dist/types.d.ts +81 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +1 -0
  53. package/package.json +133 -0
package/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # @stencil/vitest
2
+
3
+ First-class testing utilities for Stencil components, powered by Vitest.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Install
8
+
9
+ ```bash
10
+ npm i --save-dev @stencil/vitest vitest
11
+ ```
12
+
13
+ For browser testing, also install:
14
+
15
+ ```bash
16
+ npm i -D @vitest/browser-playwright
17
+ # or
18
+ npm i -D @vitest/browser-webdriverio
19
+ ```
20
+
21
+ ### 2. Create `vitest.config.ts`
22
+
23
+ ```typescript
24
+ import { defineVitestConfig } from '@stencil/vitest/config';
25
+ import { playwright } from '@vitest/browser-playwright';
26
+
27
+ export default defineVitestConfig({
28
+ stencilConfig: './stencil.config.ts',
29
+ test: {
30
+ projects: [
31
+ // Unit tests - node environment for functions / logic
32
+ {
33
+ test: {
34
+ name: 'unit',
35
+ include: ['src/**/*.unit.{ts,tsx}'],
36
+ environment: 'node',
37
+ },
38
+ },
39
+ // Spec tests - via a node DOM of your choice
40
+ {
41
+ test: {
42
+ name: 'spec',
43
+ include: ['src/**/*.spec.{ts,tsx}'],
44
+ environment: 'stencil',
45
+ setupFiles: ['./vitest-setup.ts'],
46
+
47
+ // Optional environment options
48
+
49
+ // environmentOptions: {
50
+ // stencil: {
51
+ // domEnvironment: 'happy-dom' | 'jsdom' | 'mock-doc' (default)
52
+ // ^^ Make sure to install relevant packages
53
+ // },
54
+ // },
55
+ },
56
+ },
57
+ // Browser tests
58
+ {
59
+ test: {
60
+ name: 'browser',
61
+ include: ['src/**/*.test.{ts,tsx}'],
62
+ setupFiles: ['./vitest-setup.ts'],
63
+ browser: {
64
+ enabled: true,
65
+ provider: playwright(),
66
+ headless: true,
67
+ instances: [{ browser: 'chromium' }],
68
+ },
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ });
74
+ ```
75
+
76
+ [refer Vitest documentation for all configuration options](https://vitest.dev/config/)
77
+
78
+ ### 3. Load your components
79
+
80
+ ```typescript
81
+ // vitest-setup.ts
82
+
83
+ // Load Stencil components.
84
+ // Adjust according to your build output of choice *
85
+ await import('./dist/test-components/test-components.esm.js');
86
+
87
+ export {};
88
+ // * Bear in mind, you may need `buildDist: true` (in your stencil.config)
89
+ // or `--prod` to use an output other than the browser lazy-loader
90
+ ```
91
+
92
+ ### 4. Write Tests
93
+
94
+ ```tsx
95
+ // src/components/my-button/my-button.spec.tsx
96
+
97
+ import { describe, it, expect } from 'vitest';
98
+ import { render, h } from '@stencil/vitest';
99
+
100
+ describe('my-button', () => {
101
+ it('renders with text', async () => {
102
+ const { root, waitForChanges } = await render(<my-button label="Click me" />);
103
+ root.click();
104
+ await waitForChanges();
105
+ expect(root).toEqualHtml(`
106
+ <my-button class="hydrated">
107
+ <mock:shadow-root>
108
+ <button class="button button--secondary button--small" type="button">
109
+ <slot></slot>
110
+ </button>
111
+ </mock:shadow-root>
112
+ Small
113
+ </my-button>
114
+ `);
115
+ });
116
+ });
117
+ ```
118
+
119
+ ### 5. Run tests
120
+
121
+ ```json
122
+ // package.json
123
+ {
124
+ "scripts": {
125
+ "test": "stencil-test",
126
+ "test:watch": "stencil-test --watch",
127
+ "test:e2e": "stencil-test --project browser",
128
+ "test:spec": "stencil-test --project spec"
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## API
134
+
135
+ ### Rendering
136
+
137
+ #### `render(VNode)`
138
+
139
+ Render a component for testing.
140
+
141
+ ```tsx
142
+ import { render, h } from '@stencil/vitest';
143
+
144
+ const { root, waitForChanges, setProps, unmount } = await render(<my-component name="World" />);
145
+
146
+ // Access the element
147
+ expect(root.textContent).toContain('World');
148
+
149
+ // Update props
150
+ root.name = 'Stencil';
151
+ await waitForChanges();
152
+ // or
153
+ await setProps({ name: 'Stencil' });
154
+
155
+ // Unmount component
156
+ unmount();
157
+ ```
158
+
159
+ ### Available matchers:
160
+
161
+ ```typescript
162
+ // DOM assertions
163
+ expect(element).toHaveClass('active');
164
+ expect(element).toHaveClasses(['active', 'primary']);
165
+ expect(element).toMatchClasses(['active']); // Partial match
166
+ expect(element).toHaveAttribute('aria-label', 'Close');
167
+ expect(element).toEqualAttribute('type', 'button');
168
+ expect(element).toEqualAttributes({ type: 'button', disabled: true });
169
+ expect(element).toHaveProperty('value', 'test');
170
+ expect(element).toHaveTextContent('Hello World');
171
+ expect(element).toEqualText('Exact text match');
172
+ expect(element).toBeVisible();
173
+
174
+ // Shadow DOM
175
+ expect(element).toHaveShadowRoot();
176
+ expect(element).toEqualHtml('<div>Expected HTML</div>');
177
+ expect(element).toEqualLightHtml('<div>Light DOM only</div>');
178
+ ```
179
+
180
+ ### Event Testing
181
+
182
+ Test custom events emitted by your components:
183
+
184
+ ```tsx
185
+ const { root, spyOnEvent, waitForChanges } = await render(<my-button />);
186
+
187
+ // Spy on events
188
+ const clickSpy = spyOnEvent('buttonClick');
189
+ const changeSpy = spyOnEvent('valueChange');
190
+
191
+ // Trigger events
192
+ root.click();
193
+ await waitForChanges();
194
+
195
+ // Assert events were emitted
196
+ expect(clickSpy).toHaveReceivedEvent();
197
+ expect(clickSpy).toHaveReceivedEventTimes(1);
198
+ expect(clickSpy).toHaveReceivedEventDetail({ buttonId: 'my-button' });
199
+
200
+ // Access event data
201
+ expect(clickSpy.events).toHaveLength(1);
202
+ expect(clickSpy.firstEvent?.detail).toEqual({ buttonId: 'my-button' });
203
+ expect(clickSpy.lastEvent?.detail).toEqual({ buttonId: 'my-button' });
204
+ ```
205
+
206
+ ## Snapshots
207
+
208
+ The package includes a custom snapshot serializer for Stencil components that properly handles shadow DOM:
209
+
210
+ ```tsx
211
+ import { render, h } from '@stencil/vitest';
212
+ ...
213
+ const { root } = await render(<my-component />);
214
+ expect(root).toMatchSnapshot();
215
+ ```
216
+
217
+ **Snapshot output example:**
218
+
219
+ ```html
220
+ <my-component>
221
+ <mock:shadow-root>
222
+ <button class="primary">
223
+ <slot />
224
+ </button>
225
+ </mock:shadow-root>
226
+ Click me
227
+ </my-component>
228
+ ```
229
+
230
+ ## Screenshot Testing
231
+
232
+ Browser tests can include screenshot comparisons using Vitest's screenshot capabilities:
233
+
234
+ ```tsx
235
+ import { render, h } from '@stencil/vitest';
236
+ ...
237
+ const { root } = await render(<my-button variant="primary">Primary Button</my-button>);
238
+ await expect(root).toMatchScreenshot();
239
+ ```
240
+
241
+ Refer to Vitest's [screenshot testing documentation](https://vitest.dev/guide/snapshot.html#visual-snapshots) for more details.
242
+
243
+ ## CLI
244
+
245
+ The `stencil-test` CLI wraps both Stencil builds with Vitest testing.
246
+
247
+ ### Add to package.json
248
+
249
+ ```json
250
+ {
251
+ "scripts": {
252
+ "test": "stencil-test",
253
+ "test:watch": "stencil-test --watch"
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Usage
259
+
260
+ ```bash
261
+ # Build once, test once
262
+ stencil-test
263
+
264
+ # Watch mode (rebuilds on component changes, interactive Vitest)
265
+ stencil-test --watch
266
+
267
+ # Watch mode with dev server
268
+ stencil-test --watch --serve
269
+
270
+ # Production build before testing
271
+ stencil-test --prod
272
+
273
+ # Pass arguments to Vitest
274
+ stencil-test --watch --coverage
275
+
276
+ # Test specific files
277
+ stencil-test button.spec.ts
278
+
279
+ # Test specific project
280
+ stencil-test --project browser
281
+ ```
282
+
283
+ ### CLI Options
284
+
285
+ The `stencil-test` CLI supports most of Stencil's CLI options and all of Vitest CLI options
286
+
287
+ - For full Stencil CLI options, see [Stencil CLI docs](https://stenciljs.com/docs/cli).
288
+ - For full Vitest CLI options, see [Vitest CLI docs](https://vitest.dev/guide/cli.html).
289
+
290
+ ## License
291
+
292
+ MIT
293
+
294
+ ## Contributing
295
+
296
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=stencil-test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stencil-test.d.ts","sourceRoot":"","sources":["../../bin/stencil-test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ function parseArgs(argv) {
4
+ const parsed = {
5
+ watch: false,
6
+ serve: false,
7
+ verbose: false,
8
+ debug: false,
9
+ prod: false,
10
+ coverage: false,
11
+ noCoverage: false,
12
+ ui: false,
13
+ help: false,
14
+ vitestArgs: [],
15
+ stencilArgs: [],
16
+ };
17
+ let i = 0;
18
+ while (i < argv.length) {
19
+ const arg = argv[i];
20
+ switch (arg) {
21
+ // Help
22
+ case '--help':
23
+ case '-h':
24
+ parsed.help = true;
25
+ i++;
26
+ break;
27
+ // Stencil flags
28
+ case '--watch':
29
+ parsed.watch = true;
30
+ i++;
31
+ break;
32
+ case '--serve':
33
+ parsed.serve = true;
34
+ i++;
35
+ break;
36
+ case '--port':
37
+ parsed.port = argv[i + 1];
38
+ i += 2;
39
+ break;
40
+ case '--verbose':
41
+ case '-v':
42
+ parsed.verbose = true;
43
+ i++;
44
+ break;
45
+ case '--debug':
46
+ parsed.debug = true;
47
+ i++;
48
+ break;
49
+ case '--prod':
50
+ parsed.prod = true;
51
+ i++;
52
+ break;
53
+ // Vitest flags
54
+ case '--project':
55
+ if (!parsed.project)
56
+ parsed.project = [];
57
+ parsed.project.push(argv[i + 1]);
58
+ parsed.vitestArgs.push(arg, argv[i + 1]);
59
+ i += 2;
60
+ break;
61
+ case '--reporter':
62
+ if (!parsed.reporter)
63
+ parsed.reporter = [];
64
+ parsed.reporter.push(argv[i + 1]);
65
+ parsed.vitestArgs.push(arg, argv[i + 1]);
66
+ i += 2;
67
+ break;
68
+ case '--coverage':
69
+ parsed.coverage = true;
70
+ parsed.vitestArgs.push(arg);
71
+ i++;
72
+ break;
73
+ case '--no-coverage':
74
+ parsed.noCoverage = true;
75
+ parsed.vitestArgs.push(arg);
76
+ i++;
77
+ break;
78
+ case '--ui':
79
+ parsed.ui = true;
80
+ parsed.vitestArgs.push(arg);
81
+ i++;
82
+ break;
83
+ // Unknown flags or test patterns - pass to vitest
84
+ default:
85
+ if (arg.startsWith('-')) {
86
+ // Unknown flag - could be for either, but default to vitest
87
+ parsed.vitestArgs.push(arg);
88
+ // Check if next arg is a value for this flag
89
+ if (i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
90
+ parsed.vitestArgs.push(argv[i + 1]);
91
+ i += 2;
92
+ }
93
+ else {
94
+ i++;
95
+ }
96
+ }
97
+ else {
98
+ // Positional arg - likely a test path pattern
99
+ parsed.vitestArgs.push(arg);
100
+ i++;
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ return parsed;
106
+ }
107
+ const args = parseArgs(process.argv.slice(2));
108
+ if (args.help) {
109
+ console.log(`
110
+ stencil-test - Integrated Stencil build and Vitest testing
111
+
112
+ Usage:
113
+ stencil-test [options] [testPathPattern]
114
+
115
+ Default (no flags): Build Stencil in dev mode and run tests once
116
+ Watch mode: Build Stencil in watch mode and run Vitest with interactive features
117
+
118
+ Stencil Options:
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
125
+
126
+ Vitest Options:
127
+ --project <name> Run tests for specific project
128
+ --reporter <name> Use specified reporter (default, verbose, dot, json, etc.)
129
+ --coverage Collect coverage information
130
+ --no-coverage Disable coverage
131
+ --ui Enable Vitest UI
132
+ [testPathPattern] Run only tests matching this pattern
133
+
134
+ Interactive Watch Mode (when --watch is enabled):
135
+ Press 'p' to filter by filename pattern
136
+ Press 't' to filter by test name pattern
137
+ Press 'f' to rerun only failed tests
138
+ Press 'u' to update snapshots
139
+ Press 'a' to rerun all tests
140
+ Press 'q' to quit
141
+
142
+ Examples:
143
+ stencil-test # Build once, test once
144
+ stencil-test --watch # Watch mode with auto-testing
145
+ stencil-test --watch --serve # Watch mode with dev server
146
+ stencil-test button.spec.ts # Test specific file
147
+ stencil-test --project browser # Test specific project
148
+ stencil-test --watch --coverage # Watch with coverage
149
+ `);
150
+ process.exit(0);
151
+ }
152
+ let stencilProcess = null;
153
+ let vitestProcess = null;
154
+ let debounceTimer = null;
155
+ let buildCount = 0;
156
+ let isRunningTests = false;
157
+ const DEBOUNCE_MS = 500;
158
+ const cwd = process.cwd();
159
+ const verbose = args.verbose;
160
+ function log(message) {
161
+ console.log(`[stencil-test] ${message}`);
162
+ }
163
+ /**
164
+ * Runs Vitest tests with the configured arguments
165
+ * In watch mode, starts Vitest in its own watch mode for interactive features
166
+ * In non-watch mode, runs tests once
167
+ */
168
+ function runTests() {
169
+ // Prevent overlapping test runs in non-watch mode
170
+ if (!args.watch && isRunningTests) {
171
+ if (verbose) {
172
+ log('Tests already running, skipping...');
173
+ }
174
+ return;
175
+ }
176
+ // Kill existing test process if running
177
+ if (vitestProcess) {
178
+ vitestProcess.kill();
179
+ vitestProcess = null;
180
+ }
181
+ isRunningTests = true;
182
+ if (verbose) {
183
+ log(`Running tests (build #${buildCount})...`);
184
+ }
185
+ else {
186
+ log('Running tests...');
187
+ }
188
+ // In watch mode, use Vitest's watch mode for interactive features
189
+ // In non-watch mode, use 'run' for a single test run
190
+ const vitestMode = args.watch ? 'watch' : 'run';
191
+ const commandArgs = ['vitest', vitestMode, ...args.vitestArgs].filter(Boolean);
192
+ if (verbose) {
193
+ log(`Command: npx ${commandArgs.join(' ')}`);
194
+ }
195
+ vitestProcess = spawn('npx', commandArgs, {
196
+ cwd,
197
+ stdio: 'inherit',
198
+ shell: true,
199
+ env: {
200
+ ...process.env,
201
+ NODE_ENV: 'test',
202
+ },
203
+ });
204
+ vitestProcess.on('exit', (code) => {
205
+ isRunningTests = false;
206
+ if (verbose) {
207
+ if (code === 0) {
208
+ log('Tests passed ✓');
209
+ }
210
+ else {
211
+ log(`Tests failed with code ${code}`);
212
+ }
213
+ }
214
+ vitestProcess = null;
215
+ // In one-time mode, exit after tests complete
216
+ if (!args.watch) {
217
+ process.exit(code || 0);
218
+ }
219
+ });
220
+ vitestProcess.on('error', (error) => {
221
+ isRunningTests = false;
222
+ console.error('[stencil-test] Failed to start test process:', error);
223
+ vitestProcess = null;
224
+ });
225
+ }
226
+ function handleStencilOutput(data) {
227
+ const output = data.toString();
228
+ process.stdout.write(output);
229
+ // Detect build completion
230
+ // Stencil outputs "build finished" on first build or "rebuild finished" in watch mode
231
+ if (output.includes('build finished') || output.includes('rebuild finished')) {
232
+ buildCount++;
233
+ if (verbose) {
234
+ log(`Build #${buildCount} finished`);
235
+ }
236
+ // Check if there were errors
237
+ const hasErrors = output.toLowerCase().includes('[ error ]');
238
+ if (hasErrors) {
239
+ if (verbose) {
240
+ log('Skipping tests due to build errors');
241
+ }
242
+ return;
243
+ }
244
+ // Run tests after the first successful build
245
+ // In watch mode, Vitest will automatically re-run when the dist/ files change
246
+ if (!vitestProcess) {
247
+ if (debounceTimer) {
248
+ clearTimeout(debounceTimer);
249
+ if (verbose) {
250
+ log('Debouncing initial test run');
251
+ }
252
+ }
253
+ debounceTimer = setTimeout(() => {
254
+ runTests();
255
+ }, DEBOUNCE_MS);
256
+ }
257
+ }
258
+ }
259
+ /**
260
+ * Clean up child processes and optionally exit with code.
261
+ */
262
+ function cleanup(exitCode) {
263
+ log('Shutting down...');
264
+ if (debounceTimer) {
265
+ clearTimeout(debounceTimer);
266
+ }
267
+ if (vitestProcess) {
268
+ vitestProcess.kill();
269
+ vitestProcess = null;
270
+ }
271
+ if (stencilProcess) {
272
+ stencilProcess.kill();
273
+ stencilProcess = null;
274
+ }
275
+ if (typeof exitCode === 'number') {
276
+ process.exit(exitCode);
277
+ }
278
+ }
279
+ // Set up signal handlers for clean shutdown
280
+ process.on('SIGINT', () => cleanup());
281
+ process.on('SIGTERM', () => cleanup());
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);
298
+ }
299
+ }
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}`);
325
+ }
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;
330
+ }
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);
335
+ }
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
+ }