@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
@@ -0,0 +1,118 @@
1
+ import { render as stencilRender } from '@stencil/core';
2
+ // Track event spies
3
+ const eventSpies = new WeakMap();
4
+ /**
5
+ * Render using Stencil's render
6
+ */
7
+ export async function render(vnode, options = {
8
+ clearStage: true,
9
+ stageAttrs: { class: 'stencil-component-stage' },
10
+ }) {
11
+ // Use Stencil's render which handles VNodes properly in the browser
12
+ const container = document.createElement('div');
13
+ Object.entries(options.stageAttrs || {}).forEach(([key, value]) => {
14
+ container.setAttribute(key, value);
15
+ });
16
+ if (options.clearStage) {
17
+ // Clear existing stage containers
18
+ const existingStages = document.body.querySelectorAll('div');
19
+ existingStages.forEach((stage) => stage.remove());
20
+ }
21
+ document.body.appendChild(container);
22
+ await stencilRender(vnode, container);
23
+ // Get the rendered element
24
+ const element = container.firstElementChild;
25
+ if (!element) {
26
+ throw new Error('Failed to render component');
27
+ }
28
+ // Wait for component to be ready
29
+ if (typeof element.componentOnReady === 'function') {
30
+ await element.componentOnReady();
31
+ }
32
+ function waitForChanges(documentElement = element) {
33
+ return new Promise((resolve) => {
34
+ // Wait for Stencil's RAF-based update cycle
35
+ // Use multiple RAF cycles to ensure all batched updates complete
36
+ requestAnimationFrame(() => {
37
+ requestAnimationFrame(() => {
38
+ const promiseChain = [];
39
+ const waitComponentOnReady = (elm, promises) => {
40
+ if (!elm)
41
+ return;
42
+ if ('shadowRoot' in elm) {
43
+ waitComponentOnReady(elm.shadowRoot, promises);
44
+ }
45
+ const children = elm.children;
46
+ const len = children.length;
47
+ for (let i = 0; i < len; i++) {
48
+ const childElm = children[i];
49
+ const childStencilElm = childElm;
50
+ if (childElm.tagName.includes('-') && typeof childStencilElm.componentOnReady === 'function') {
51
+ promises.push(childStencilElm.componentOnReady().then(() => { }));
52
+ }
53
+ waitComponentOnReady(childElm, promises);
54
+ }
55
+ };
56
+ waitComponentOnReady(documentElement, promiseChain);
57
+ Promise.all(promiseChain)
58
+ .then(() => resolve())
59
+ .catch(() => resolve());
60
+ });
61
+ });
62
+ });
63
+ }
64
+ const setProps = async (newProps) => {
65
+ Object.entries(newProps).forEach(([key, value]) => {
66
+ element[key] = value;
67
+ });
68
+ // Wait for multiple RAF cycles to ensure Stencil's batched updates complete
69
+ // Stencil batches updates using requestAnimationFrame for performance
70
+ await waitForChanges();
71
+ // Additional RAF cycle to ensure rendering is complete
72
+ await new Promise((resolve) => requestAnimationFrame(() => resolve(undefined)));
73
+ };
74
+ const unmount = () => {
75
+ container.remove();
76
+ };
77
+ const spyOnEvent = (eventName) => {
78
+ // Return existing spy if already created
79
+ if (eventSpies.has(container)) {
80
+ return eventSpies.get(container).find((spy) => spy.eventName === eventName);
81
+ }
82
+ const spy = {
83
+ eventName,
84
+ events: [],
85
+ firstEvent: undefined,
86
+ lastEvent: undefined,
87
+ length: 0,
88
+ };
89
+ // Store listener so we can remove it later
90
+ const listener = (event) => {
91
+ const customEvent = event;
92
+ spy.events.push(customEvent);
93
+ spy.length = spy.events.length;
94
+ spy.lastEvent = customEvent;
95
+ if (spy.length === 1) {
96
+ spy.firstEvent = customEvent;
97
+ }
98
+ };
99
+ spy._listener = listener;
100
+ element.addEventListener(eventName, listener);
101
+ // Store the spy
102
+ let spiesForElement = eventSpies.get(container);
103
+ if (!spiesForElement) {
104
+ spiesForElement = [];
105
+ eventSpies.set(container, spiesForElement);
106
+ }
107
+ spiesForElement.push(spy);
108
+ return spy;
109
+ };
110
+ return {
111
+ root: element,
112
+ waitForChanges,
113
+ instance: element,
114
+ setProps,
115
+ unmount,
116
+ spyOnEvent,
117
+ };
118
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Custom snapshot serializer for Stencil components
3
+ *
4
+ * Formats HTMLElements with shadow DOM using the same serialization
5
+ * as our toEqualHtml matcher, ensuring consistent <mock:shadow-root> output
6
+ */
7
+ import type { SnapshotSerializer } from 'vitest';
8
+ /**
9
+ * Vitest snapshot serializer for Stencil components
10
+ */
11
+ export declare const StencilSnapshotSerializer: SnapshotSerializer;
12
+ /**
13
+ * Default export for convenience
14
+ */
15
+ export default StencilSnapshotSerializer;
16
+ export declare function installMatchers(): void;
17
+ //# sourceMappingURL=snapshot-serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-serializer.d.ts","sourceRoot":"","sources":["../../src/testing/snapshot-serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAIjD;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,kBAiCvC,CAAC;AAEF;;GAEG;AACH,eAAe,yBAAyB,CAAC;AAEzC,wBAAgB,eAAe,SAE9B"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Custom snapshot serializer for Stencil components
3
+ *
4
+ * Formats HTMLElements with shadow DOM using the same serialization
5
+ * as our toEqualHtml matcher, ensuring consistent <mock:shadow-root> output
6
+ */
7
+ import { expect } from 'vitest';
8
+ import { serializeHtml } from './html-serializer.js';
9
+ /**
10
+ * Vitest snapshot serializer for Stencil components
11
+ */
12
+ export const StencilSnapshotSerializer = {
13
+ /**
14
+ * Test if this serializer should handle the value
15
+ */
16
+ test(val) {
17
+ // Handle HTMLElement with shadow DOM (Stencil components)
18
+ if (val && typeof val === 'object') {
19
+ // Check if it's an HTMLElement with shadowRoot
20
+ if ('shadowRoot' in val && val.shadowRoot) {
21
+ return true;
22
+ }
23
+ // Check if it's an HTMLElement (could be Stencil component that hasn't hydrated yet)
24
+ if ('nodeType' in val && val.nodeType === 1 && 'tagName' in val) {
25
+ // Only serialize custom elements (Stencil components typically have hyphens)
26
+ const tagName = val.tagName?.toLowerCase();
27
+ return tagName && tagName.includes('-');
28
+ }
29
+ }
30
+ return false;
31
+ },
32
+ /**
33
+ * Serialize the value for snapshot
34
+ */
35
+ serialize(val) {
36
+ return serializeHtml(val, {
37
+ serializeShadowRoot: true,
38
+ pretty: true,
39
+ excludeStyles: true,
40
+ });
41
+ },
42
+ };
43
+ /**
44
+ * Default export for convenience
45
+ */
46
+ export default StencilSnapshotSerializer;
47
+ export function installMatchers() {
48
+ expect.addSnapshotSerializer(StencilSnapshotSerializer);
49
+ }
50
+ installMatchers();
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Event spy for testing custom events
3
+ */
4
+ export interface EventSpy {
5
+ /**
6
+ * Name of the event being spied on
7
+ */
8
+ eventName: string;
9
+ /**
10
+ * All events that have been received
11
+ */
12
+ events: CustomEvent[];
13
+ /**
14
+ * First event received (if any)
15
+ */
16
+ firstEvent: CustomEvent | undefined;
17
+ /**
18
+ * Last event received (if any)
19
+ */
20
+ lastEvent: CustomEvent | undefined;
21
+ /**
22
+ * Number of events received
23
+ */
24
+ length: number;
25
+ }
26
+ /**
27
+ * Component render options
28
+ */
29
+ export interface RenderOptions {
30
+ /**
31
+ * Props to pass to the component
32
+ */
33
+ props?: Record<string, any>;
34
+ /**
35
+ * Slots content (for shadow DOM components)
36
+ */
37
+ slots?: Record<string, string | HTMLElement>;
38
+ /**
39
+ * HTML content to place inside the component
40
+ */
41
+ html?: string;
42
+ /**
43
+ * Wait for component to be loaded before returning
44
+ * @default true
45
+ */
46
+ waitForLoad?: boolean;
47
+ /**
48
+ * Additional HTML attributes
49
+ */
50
+ attributes?: Record<string, string>;
51
+ }
52
+ /**
53
+ * Render result for component testing
54
+ */
55
+ export interface RenderResult<T = HTMLElement> {
56
+ /**
57
+ * The rendered component element
58
+ */
59
+ root: T;
60
+ /**
61
+ * Wait for changes to be applied
62
+ */
63
+ waitForChanges: () => Promise<void>;
64
+ /**
65
+ * Get the component instance (if available)
66
+ */
67
+ instance?: any;
68
+ /**
69
+ * Update component props
70
+ */
71
+ setProps: (props: Record<string, any>) => Promise<void>;
72
+ /**
73
+ * Unmount/cleanup the component
74
+ */
75
+ unmount: () => void;
76
+ /**
77
+ * Spy on a custom event
78
+ */
79
+ spyOnEvent: (eventName: string) => EventSpy;
80
+ }
81
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,EAAE,WAAW,EAAE,CAAC;IAEtB;;OAEG;IACH,UAAU,EAAE,WAAW,GAAG,SAAS,CAAC;IAEpC;;OAEG;IACH,SAAS,EAAE,WAAW,GAAG,SAAS,CAAC;IAEnC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;IAE7C;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,WAAW;IAC3C;;OAEG;IACH,IAAI,EAAE,CAAC,CAAC;IAER;;OAEG;IACH,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC;IAEf;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,QAAQ,CAAC;CAC7C"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,133 @@
1
+ {
2
+ "name": "@stencil/vitest",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/stenciljs/vitest"
6
+ },
7
+ "version": "0.0.1-dev.20260109124515.90fb962",
8
+ "description": "First-class testing utilities for Stencil design systems with Vitest",
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "bin": {
14
+ "stencil-test": "./dist/bin/stencil-test.js"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.js"
21
+ },
22
+ "./config": {
23
+ "types": "./dist/config.d.ts",
24
+ "import": "./dist/config.js",
25
+ "require": "./dist/config.js"
26
+ },
27
+ "./mock-doc-setup": {
28
+ "types": "./dist/setup/mock-doc-setup.d.ts",
29
+ "import": "./dist/setup/mock-doc-setup.js"
30
+ },
31
+ "./jsdom-setup": {
32
+ "types": "./dist/setup/jsdom-setup.d.ts",
33
+ "import": "./dist/setup/jsdom-setup.js"
34
+ },
35
+ "./happy-dom-setup": {
36
+ "types": "./dist/setup/happy-dom-setup.d.ts",
37
+ "import": "./dist/setup/happy-dom-setup.js"
38
+ },
39
+ "./vitest": {
40
+ "types": "./dist/environments/stencil.d.ts",
41
+ "import": "./dist/environments/stencil.js"
42
+ },
43
+ "./environments/stencil": {
44
+ "types": "./dist/environments/stencil.d.ts",
45
+ "import": "./dist/environments/stencil.js"
46
+ }
47
+ },
48
+ "files": [
49
+ "dist/",
50
+ "README.md"
51
+ ],
52
+ "keywords": [
53
+ "stencil",
54
+ "testing",
55
+ "vitest",
56
+ "web components",
57
+ "component testing"
58
+ ],
59
+ "scripts": {
60
+ "build": "tsc && tsc -p tsconfig.bin.json",
61
+ "dev": "tsc --watch",
62
+ "test": "pnpm --filter test-project test",
63
+ "test:engines": "pnpm installed-check --no-workspaces --ignore-dev",
64
+ "lint": "eslint .",
65
+ "lint:fix": "eslint . --fix",
66
+ "format": "prettier --check .",
67
+ "format:fix": "prettier --write .",
68
+ "knip": "knip",
69
+ "quality": "pnpm format && pnpm lint && pnpm knip",
70
+ "quality:fix": "pnpm format:fix && pnpm lint:fix && pnpm knip",
71
+ "release": "semantic-release"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ },
76
+ "peerDependencies": {
77
+ "@stencil/core": "^4.0.0",
78
+ "vitest": "^4.0.0 || ^3.0.0 || ^2.0.0"
79
+ },
80
+ "peerDependenciesMeta": {
81
+ "@playwright/test": {
82
+ "optional": true
83
+ },
84
+ "@wdio/globals": {
85
+ "optional": true
86
+ },
87
+ "jsdom": {
88
+ "optional": true
89
+ },
90
+ "happy-dom": {
91
+ "optional": true
92
+ },
93
+ "playwright": {
94
+ "optional": true
95
+ },
96
+ "@vitest/browser-webdriverio": {
97
+ "optional": true
98
+ },
99
+ "@vitest/browser-playwright": {
100
+ "optional": true
101
+ },
102
+ "@vitest/browser-preview": {
103
+ "optional": true
104
+ }
105
+ },
106
+ "dependencies": {
107
+ "jiti": "^2.6.1",
108
+ "local-pkg": "^1.1.2",
109
+ "vitest-environment-stencil": "latest"
110
+ },
111
+ "devDependencies": {
112
+ "@eslint/js": "^9.39.2",
113
+ "@semantic-release/changelog": "^6.0.3",
114
+ "@semantic-release/git": "^10.0.1",
115
+ "@stencil/core": "^4.0.0",
116
+ "@types/node": "24.10.4",
117
+ "@typescript-eslint/eslint-plugin": "^8.50.0",
118
+ "@typescript-eslint/parser": "^8.50.0",
119
+ "@vitest/coverage-v8": "^4.0.0",
120
+ "eslint": "^9.39.2",
121
+ "installed-check": "^9.3.0",
122
+ "knip": "^5.76.3",
123
+ "playwright-core": "^1.57.0",
124
+ "prettier": "^3.7.4",
125
+ "semantic-release": "^25.0.2",
126
+ "typescript": "~5.8.3",
127
+ "vitest": "^4.0.0"
128
+ },
129
+ "engines": {
130
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
131
+ },
132
+ "packageManager": "pnpm@10.26.0"
133
+ }