@stencil/vitest 1.11.2 → 1.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -22
- package/dist/environments/stencil.d.ts.map +1 -1
- package/dist/environments/stencil.js +20 -0
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +61 -25
- package/dist/setup/mock-doc-setup.d.ts.map +1 -1
- package/dist/setup/mock-doc-setup.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
First-class testing utilities for Stencil components, powered by Vitest.
|
|
4
4
|
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Quick Start](#quick-start)
|
|
8
|
+
- [1. Install](#1-install)
|
|
9
|
+
- [2. Create vitest.config.ts](#2-create-vitestconfigts)
|
|
10
|
+
- [3. Load your components](#3-load-your-components)
|
|
11
|
+
- [4. Write Tests](#4-write-tests)
|
|
12
|
+
- [5. Run tests](#5-run-tests)
|
|
13
|
+
- [API](#api)
|
|
14
|
+
- [Rendering](#rendering)
|
|
15
|
+
- [Available matchers](#available-matchers)
|
|
16
|
+
- [Spying and Mocking](#spying-and-mocking)
|
|
17
|
+
- [Event Testing](#event-testing)
|
|
18
|
+
- [Stencil Vitest Plugin (Experimental)](#stencil-vitest-plugin)
|
|
19
|
+
- [Setup](#setup)
|
|
20
|
+
- [Mocking component dependencies](#mocking-component-dependencies)
|
|
21
|
+
- [Limitations](#limitations)
|
|
22
|
+
- [Snapshots](#snapshots)
|
|
23
|
+
- [Screenshot Testing](#screenshot-testing)
|
|
24
|
+
- [Utils](#utils)
|
|
25
|
+
- [serializeHtml](#serializehtmlelement-options)
|
|
26
|
+
- [prettifyHtml](#prettifyhtmlhtml)
|
|
27
|
+
- [waitForStable](#waitforstableelementorselector-timeout)
|
|
28
|
+
- [waitForExist](#waitforexistselector-timeout)
|
|
29
|
+
- [CLI](#cli)
|
|
30
|
+
- [Usage](#usage)
|
|
31
|
+
- [Flags](#flags)
|
|
32
|
+
- [Global Variables](#global-variables)
|
|
33
|
+
- [Limitations / Gotchas](#limitations--gotchas)
|
|
34
|
+
- [License](#license)
|
|
35
|
+
- [Contributing](#contributing)
|
|
36
|
+
|
|
5
37
|
## Quick Start
|
|
6
38
|
|
|
7
39
|
### 1. Install
|
|
@@ -442,9 +474,12 @@ expect(clickSpy.lastEvent?.detail).toEqual({ buttonId: 'my-button' });
|
|
|
442
474
|
|
|
443
475
|
## Stencil Vitest Plugin
|
|
444
476
|
|
|
445
|
-
|
|
477
|
+
All examples so far have mentioned setting up tests against **pre-built dist outputs**; Stencil compiles your components once and tests run against those bundles. Whilst this method is fast and reliable, it does mean Vitest never sees individual component source files as discrete modules and so does have 2 key limitations:
|
|
478
|
+
|
|
479
|
+
1. `vi.mock()` cannot intercept imports made by your components, because the dependency is already bundled away before Vitest gets involved.
|
|
480
|
+
2. Coverage reports will not work out-of-the-box without additional configuration (`sourceMap: true` / [3rd party tools](https://github.com/cenfun/vitest-monocart-coverage)) and even then, may not be accurate.
|
|
446
481
|
|
|
447
|
-
`stencilVitestPlugin` solves this by hooking into Vite's transform pipeline
|
|
482
|
+
The experimental `stencilVitestPlugin` solves this by hooking into Vite's transform pipeline: Stencil files are compiled on-the-fly before Vitest imports them; each component file becomes its own entry in Vitest's module graph — and its imports are independently resolvable and mockable.
|
|
448
483
|
|
|
449
484
|
### Setup
|
|
450
485
|
|
|
@@ -461,11 +496,11 @@ export default defineVitestConfig({
|
|
|
461
496
|
plugins: [stencilVitestPlugin()],
|
|
462
497
|
test: {
|
|
463
498
|
name: 'plugin',
|
|
464
|
-
environment: 'stencil',
|
|
465
499
|
include: ['src/**/*.plugin.spec.{ts,tsx}'],
|
|
466
500
|
// No dist setup file needed — each component source file registers
|
|
467
|
-
|
|
468
|
-
|
|
501
|
+
|
|
502
|
+
environment: 'stencil',
|
|
503
|
+
// ^^ you can use the plugin with any setup - even browser tests!
|
|
469
504
|
},
|
|
470
505
|
},
|
|
471
506
|
],
|
|
@@ -523,7 +558,7 @@ it('renders using the mocked utility', async () => {
|
|
|
523
558
|
|
|
524
559
|
#### Class inheritance
|
|
525
560
|
|
|
526
|
-
In Stencil v4 `
|
|
561
|
+
In Stencil v4 `transpile()` (used within the plugin) is a single-file compiler. When a component class `extends` a base class that lives in a separate file, `transpile()` cannot follow the import to merge the parent's metadata and will throw an error.
|
|
527
562
|
|
|
528
563
|
```tsx
|
|
529
564
|
// ❌ Will fail — base class is in a separate file
|
|
@@ -533,7 +568,7 @@ import { FormBase } from './form-base.js';
|
|
|
533
568
|
export class MyInput extends FormBase { ... }
|
|
534
569
|
```
|
|
535
570
|
|
|
536
|
-
> This limitation is specific to v4. Stencil v5's
|
|
571
|
+
> This limitation is specific to v4. Stencil v5's `transpile()` can resolve multi-file inheritance chains.
|
|
537
572
|
|
|
538
573
|
## Snapshots
|
|
539
574
|
|
|
@@ -646,17 +681,6 @@ const element = await waitForExist('#dynamic-content', 10000);
|
|
|
646
681
|
|
|
647
682
|
The `stencil-test` CLI wraps both Stencil builds with Vitest testing.
|
|
648
683
|
|
|
649
|
-
### Add to package.json
|
|
650
|
-
|
|
651
|
-
```json
|
|
652
|
-
{
|
|
653
|
-
"scripts": {
|
|
654
|
-
"test": "stencil-test",
|
|
655
|
-
"test:watch": "stencil-test --watch"
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
```
|
|
659
|
-
|
|
660
684
|
### Usage
|
|
661
685
|
|
|
662
686
|
```bash
|
|
@@ -682,12 +706,14 @@ stencil-test button.spec.ts
|
|
|
682
706
|
stencil-test --project browser
|
|
683
707
|
```
|
|
684
708
|
|
|
685
|
-
###
|
|
709
|
+
### Flags
|
|
710
|
+
|
|
711
|
+
The `stencil-test` CLI supports most of Stencil's CLI flags and all of Vitest CLI flags
|
|
686
712
|
|
|
687
|
-
|
|
713
|
+
- For full Stencil CLI flags, see [Stencil CLI docs](https://stenciljs.com/docs/cli).
|
|
714
|
+
- For full Vitest CLI flags, see [Vitest CLI docs](https://vitest.dev/guide/cli.html).
|
|
688
715
|
|
|
689
|
-
-
|
|
690
|
-
- For full Vitest CLI options, see [Vitest CLI docs](https://vitest.dev/guide/cli.html).
|
|
716
|
+
Note: unlike a normal `stencil build` `stencil-vitest` runs in development mode by default for faster builds. Use `--prod` to test against a production build.
|
|
691
717
|
|
|
692
718
|
### Global Variables
|
|
693
719
|
|
|
@@ -721,6 +747,16 @@ Add to your `tsconfig.json` for type definitions:
|
|
|
721
747
|
}
|
|
722
748
|
```
|
|
723
749
|
|
|
750
|
+
## Limitations / Gotchas
|
|
751
|
+
|
|
752
|
+
### `vi.mock()` doesn't work?
|
|
753
|
+
|
|
754
|
+
Modules can only be mocked if they are imported in a way that Vitest can intercept. If your components are importing dependencies that you want to mock, you must use the `stencilVitestPlugin` to compile components on-the-fly and allow Vitest to mock their imports. See the [Stencil Vitest Plugin section](#stencil-vitest-plugin) for details.
|
|
755
|
+
|
|
756
|
+
### Coverage reports are empty or inaccurate?
|
|
757
|
+
|
|
758
|
+
When testing against pre-built dist outputs, source maps (`sourceMap: true` in `stencil.config.ts`) and [3rd party tools](https://github.com/cenfun/vitest-monocart-coverage) are required for coverage reports. Alternatively, consider using the `stencilVitestPlugin` which compiles components on-the-fly and provides better coverage support.
|
|
759
|
+
|
|
724
760
|
## License
|
|
725
761
|
|
|
726
762
|
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stencil.d.ts","sourceRoot":"","sources":["../../src/environments/stencil.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAiBvD,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,cAAc,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;CACrD;AAQD;;;;;;;;;;;;;;;;GAgBG;wBACa,WAAW;AAA3B,
|
|
1
|
+
{"version":3,"file":"stencil.d.ts","sourceRoot":"","sources":["../../src/environments/stencil.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAiBvD,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,cAAc,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;CACrD;AAQD;;;;;;;;;;;;;;;;GAgBG;wBACa,WAAW;AAA3B,wBA6FE"}
|
|
@@ -77,6 +77,26 @@ export default {
|
|
|
77
77
|
if (originals.has('Event')) {
|
|
78
78
|
global.Event = originals.get('Event');
|
|
79
79
|
}
|
|
80
|
+
// Create HTMLElement wrapper that captures ownerDocument
|
|
81
|
+
// In Stencil 4.43+, win.HTMLElement returns MockHTMLElement directly which expects
|
|
82
|
+
// ownerDocument as the first constructor arg. But Stencil-compiled components call
|
|
83
|
+
// super() with no args (standard browser behavior). This wrapper bridges the gap.
|
|
84
|
+
const MockHTMLElementBase = global.HTMLElement;
|
|
85
|
+
const doc = win.document;
|
|
86
|
+
global.HTMLElement = class extends MockHTMLElementBase {
|
|
87
|
+
constructor() {
|
|
88
|
+
super(doc, '');
|
|
89
|
+
const observedAttributes = this.constructor.observedAttributes;
|
|
90
|
+
if (Array.isArray(observedAttributes) && typeof this.attributeChangedCallback === 'function') {
|
|
91
|
+
observedAttributes.forEach((attrName) => {
|
|
92
|
+
const attrValue = this.getAttribute(attrName);
|
|
93
|
+
if (attrValue != null) {
|
|
94
|
+
this.attributeChangedCallback(attrName, null, attrValue);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
80
100
|
// Remove undefined properties that shadow native globals
|
|
81
101
|
keys.forEach((key) => {
|
|
82
102
|
if (global[key] === undefined && originals.has(key)) {
|
package/dist/plugin.d.ts
CHANGED
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAyF5C"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { transpile } from '@stencil/core/compiler';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
1
4
|
/**
|
|
2
5
|
* A Vite/Vitest plugin that transforms Stencil component source files (.tsx) on-the-fly,
|
|
3
6
|
* enabling module mocking and direct source imports during tests.
|
|
@@ -29,14 +32,43 @@
|
|
|
29
32
|
* },
|
|
30
33
|
* });
|
|
31
34
|
* ```
|
|
32
|
-
*
|
|
35
|
+
* @param opts Optional configuration for the plugin
|
|
33
36
|
* @returns a Vite plugin configuration object
|
|
34
37
|
*/
|
|
35
38
|
export function stencilVitestPlugin() {
|
|
36
39
|
return {
|
|
37
40
|
name: 'stencil-vitest-transform',
|
|
38
41
|
enforce: 'pre',
|
|
42
|
+
resolveId(id, importer) {
|
|
43
|
+
if (id.includes('.css') && id.includes('tag=')) {
|
|
44
|
+
const [relPath] = id.split('?');
|
|
45
|
+
const query = id.slice(id.indexOf('?'));
|
|
46
|
+
const resolved = resolve(dirname(importer), relPath);
|
|
47
|
+
// Remove .css from virtual ID to prevent Vite's CSS plugin from hijacking the output
|
|
48
|
+
return '\0stencil-style:' + resolved.replace(/\.css$/, '') + query;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
},
|
|
52
|
+
load(id) {
|
|
53
|
+
if (id.startsWith('\0stencil-style:')) {
|
|
54
|
+
// Add .css back to get the real file path
|
|
55
|
+
const realPath = id.slice('\0stencil-style:'.length).split('?')[0] + '.css';
|
|
56
|
+
return readFileSync(realPath, 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
},
|
|
39
60
|
async transform(code, id) {
|
|
61
|
+
if (id.startsWith('\0stencil-style:')) {
|
|
62
|
+
// Reconstruct the original .css path for Stencil's transpiler (it uses extension to detect file type)
|
|
63
|
+
const pathWithoutPrefix = id.slice('\0stencil-style:'.length);
|
|
64
|
+
const [basePath, query] = pathWithoutPrefix.split('?');
|
|
65
|
+
const originalPath = basePath + '.css' + (query ? '?' + query : '');
|
|
66
|
+
const result = await transpile(code, { file: originalPath });
|
|
67
|
+
return {
|
|
68
|
+
code: result.code,
|
|
69
|
+
map: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
40
72
|
// Only transform .tsx files
|
|
41
73
|
if (!id.endsWith('.tsx')) {
|
|
42
74
|
return null;
|
|
@@ -52,31 +84,35 @@ export function stencilVitestPlugin() {
|
|
|
52
84
|
if (!hasStencilDecorator) {
|
|
53
85
|
return null;
|
|
54
86
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
try {
|
|
88
|
+
const result = await transpile(code, {
|
|
89
|
+
file: id,
|
|
90
|
+
// 'customelement' appends a customElements.define() call so the component
|
|
91
|
+
// self-registers the moment this module is imported — no loader needed.
|
|
92
|
+
componentExport: 'customelement',
|
|
93
|
+
componentMetadata: 'compilerstatic',
|
|
94
|
+
currentDirectory: process.cwd(),
|
|
95
|
+
module: 'esm',
|
|
96
|
+
proxy: null,
|
|
97
|
+
sourceMap: false,
|
|
98
|
+
style: 'static',
|
|
99
|
+
styleImportData: 'queryparams',
|
|
100
|
+
target: 'es2022',
|
|
101
|
+
transformAliasedImportPaths: false,
|
|
102
|
+
});
|
|
103
|
+
const errors = result.diagnostics?.filter((d) => d.level === 'error') ?? [];
|
|
104
|
+
if (errors.length > 0) {
|
|
105
|
+
const messages = errors.map((d) => d.messageText).join('\n');
|
|
106
|
+
throw new Error(`[stencil-vitest-plugin] Transform error in ${id}:\n${messages}`);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
code: result.code,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.error(`[stencil-vitest-plugin] Failed to transform ${id}:`, err);
|
|
114
|
+
throw err;
|
|
76
115
|
}
|
|
77
|
-
return {
|
|
78
|
-
code: result.code,
|
|
79
|
-
};
|
|
80
116
|
},
|
|
81
117
|
};
|
|
82
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-doc-setup.d.ts","sourceRoot":"","sources":["../../src/setup/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;
|
|
1
|
+
{"version":3,"file":"mock-doc-setup.d.ts","sourceRoot":"","sources":["../../src/setup/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"}
|
|
@@ -80,6 +80,7 @@ else {
|
|
|
80
80
|
globalThis.document = doc;
|
|
81
81
|
globalThis.HTMLElement = win.HTMLElement;
|
|
82
82
|
globalThis.CustomEvent = win.CustomEvent;
|
|
83
|
+
globalThis.Event = win.Event;
|
|
83
84
|
globalThis.Element = win.Element;
|
|
84
85
|
globalThis.Node = win.Node;
|
|
85
86
|
globalThis.DocumentFragment = win.DocumentFragment;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "https://github.com/stenciljs/vitest"
|
|
6
6
|
},
|
|
7
|
-
"version": "1.11.
|
|
7
|
+
"version": "1.11.4",
|
|
8
8
|
"description": "First-class testing utilities for Stencil design systems with Vitest",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"type": "module",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"dependencies": {
|
|
105
105
|
"jiti": "^2.6.1",
|
|
106
106
|
"local-pkg": "^1.1.2",
|
|
107
|
-
"vitest-environment-stencil": "1.11.
|
|
107
|
+
"vitest-environment-stencil": "1.11.4"
|
|
108
108
|
},
|
|
109
109
|
"devDependencies": {
|
|
110
110
|
"@eslint/js": "^9.39.2",
|