@stencil/vitest 1.1.4 → 1.1.6
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/dist/bin/__tests__/stencil-test.spec.d.ts +2 -0
- package/dist/bin/__tests__/stencil-test.spec.d.ts.map +1 -0
- package/dist/bin/__tests__/stencil-test.spec.js +338 -0
- package/dist/bin/stencil-test.js +18 -13
- package/dist/environments/env/happy-dom.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/setup/mock-doc-setup.js +1 -0
- package/dist/testing/matchers.d.ts +0 -134
- package/dist/testing/matchers.d.ts.map +1 -1
- package/dist/testing/matchers.js +19 -19
- package/dist/testing/snapshot-serializer.d.ts +1 -10
- package/dist/testing/snapshot-serializer.d.ts.map +1 -1
- package/dist/testing/snapshot-serializer.js +2 -6
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stencil-test.spec.d.ts","sourceRoot":"","sources":["../../../bin/__tests__/stencil-test.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
const BIN_PATH = join(__dirname, '../../dist/bin/stencil-test.js');
|
|
7
|
+
describe('stencil-test CLI', () => {
|
|
8
|
+
let testDir;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Create a temporary test directory
|
|
11
|
+
testDir = mkdtempSync(join(tmpdir(), 'stencil-test-'));
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
// Clean up temp directory
|
|
15
|
+
try {
|
|
16
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Ignore cleanup errors
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
describe('argument parsing', () => {
|
|
23
|
+
it('should parse --watch flag', async () => {
|
|
24
|
+
const result = await runCLI(['--help']);
|
|
25
|
+
expect(result.stdout).toContain('--watch');
|
|
26
|
+
});
|
|
27
|
+
it('should parse --stencil-config flag', async () => {
|
|
28
|
+
const result = await runCLI(['--help']);
|
|
29
|
+
expect(result.stdout).toContain('--stencil-config');
|
|
30
|
+
});
|
|
31
|
+
it('should parse --verbose flag', async () => {
|
|
32
|
+
const result = await runCLI(['--help']);
|
|
33
|
+
expect(result.stdout).toContain('--verbose');
|
|
34
|
+
});
|
|
35
|
+
it('should parse --prod flag', async () => {
|
|
36
|
+
const result = await runCLI(['--help']);
|
|
37
|
+
expect(result.stdout).toContain('--prod');
|
|
38
|
+
});
|
|
39
|
+
it('should parse --serve flag', async () => {
|
|
40
|
+
const result = await runCLI(['--help']);
|
|
41
|
+
expect(result.stdout).toContain('--serve');
|
|
42
|
+
});
|
|
43
|
+
it('should parse --port flag', async () => {
|
|
44
|
+
const result = await runCLI(['--help']);
|
|
45
|
+
expect(result.stdout).toContain('--port');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('help text', () => {
|
|
49
|
+
it('should display help with --help', async () => {
|
|
50
|
+
const result = await runCLI(['--help']);
|
|
51
|
+
expect(result.stdout).toContain('stencil-test - Integrated Stencil build and Vitest testing');
|
|
52
|
+
expect(result.stdout).toContain('Usage:');
|
|
53
|
+
expect(result.stdout).toContain('Examples:');
|
|
54
|
+
});
|
|
55
|
+
it('should display help with -h', async () => {
|
|
56
|
+
const result = await runCLI(['-h']);
|
|
57
|
+
expect(result.stdout).toContain('stencil-test - Integrated Stencil build and Vitest testing');
|
|
58
|
+
});
|
|
59
|
+
it('should include Stencil options in help', async () => {
|
|
60
|
+
const result = await runCLI(['--help']);
|
|
61
|
+
expect(result.stdout).toContain('Stencil Options:');
|
|
62
|
+
expect(result.stdout).toContain('--watch');
|
|
63
|
+
expect(result.stdout).toContain('--prod');
|
|
64
|
+
expect(result.stdout).toContain('--stencil-config');
|
|
65
|
+
});
|
|
66
|
+
it('should include Vitest options in help', async () => {
|
|
67
|
+
const result = await runCLI(['--help']);
|
|
68
|
+
expect(result.stdout).toContain('Vitest Options:');
|
|
69
|
+
expect(result.stdout).toContain('--project');
|
|
70
|
+
expect(result.stdout).toContain('--coverage');
|
|
71
|
+
});
|
|
72
|
+
it('should include interactive watch mode instructions', async () => {
|
|
73
|
+
const result = await runCLI(['--help']);
|
|
74
|
+
expect(result.stdout).toContain('Interactive Watch Mode');
|
|
75
|
+
expect(result.stdout).toContain("Press 'p' to filter by filename");
|
|
76
|
+
expect(result.stdout).toContain("Press 'q' to quit");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('config file handling', () => {
|
|
80
|
+
it('should find default stencil.config.ts', async () => {
|
|
81
|
+
// Create a minimal stencil config
|
|
82
|
+
const stencilConfig = `
|
|
83
|
+
export const config = {
|
|
84
|
+
namespace: 'test',
|
|
85
|
+
outputTargets: [{ type: 'dist' }]
|
|
86
|
+
};
|
|
87
|
+
`;
|
|
88
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
89
|
+
// Create a minimal vitest config
|
|
90
|
+
const vitestConfig = `
|
|
91
|
+
export default {
|
|
92
|
+
test: {
|
|
93
|
+
projects: []
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
`;
|
|
97
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
98
|
+
// Create package.json
|
|
99
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
100
|
+
// Run with verbose to see config loading
|
|
101
|
+
const result = await runCLIInDir(testDir, ['--watch', '--verbose'], 2000);
|
|
102
|
+
// Should mention loading the config
|
|
103
|
+
expect(result.stdout + result.stderr).toMatch(/stencil\.config|Created temporary/i);
|
|
104
|
+
});
|
|
105
|
+
it('should use custom config with --stencil-config', async () => {
|
|
106
|
+
// Create a custom-named stencil config
|
|
107
|
+
const stencilConfig = `
|
|
108
|
+
export const config = {
|
|
109
|
+
namespace: 'custom-test',
|
|
110
|
+
outputTargets: [{ type: 'dist' }]
|
|
111
|
+
};
|
|
112
|
+
`;
|
|
113
|
+
writeFileSync(join(testDir, 'custom.config.ts'), stencilConfig);
|
|
114
|
+
const vitestConfig = `
|
|
115
|
+
export default {
|
|
116
|
+
test: {
|
|
117
|
+
projects: []
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
`;
|
|
121
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
122
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
123
|
+
const result = await runCLIInDir(testDir, ['--watch', '--stencil-config', './custom.config.ts', '--verbose'], 2000);
|
|
124
|
+
// Should load the custom config
|
|
125
|
+
expect(result.stdout + result.stderr).toMatch(/custom\.config|Created temporary/i);
|
|
126
|
+
});
|
|
127
|
+
it('should create temporary config in watch mode', async () => {
|
|
128
|
+
const stencilConfig = `
|
|
129
|
+
export const config = {
|
|
130
|
+
namespace: 'test',
|
|
131
|
+
outputTargets: [{ type: 'dist' }]
|
|
132
|
+
};
|
|
133
|
+
`;
|
|
134
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
135
|
+
const vitestConfig = `
|
|
136
|
+
export default {
|
|
137
|
+
test: {
|
|
138
|
+
projects: []
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
`;
|
|
142
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
143
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
144
|
+
const result = await runCLIInDir(testDir, ['--watch', '--verbose'], 2000);
|
|
145
|
+
// Should create a temporary config
|
|
146
|
+
expect(result.stdout).toMatch(/Created temporary stencil config/);
|
|
147
|
+
});
|
|
148
|
+
it('should not create temporary config in non-watch mode', async () => {
|
|
149
|
+
const stencilConfig = `
|
|
150
|
+
export const config = {
|
|
151
|
+
namespace: 'test',
|
|
152
|
+
outputTargets: [{ type: 'dist' }]
|
|
153
|
+
};
|
|
154
|
+
`;
|
|
155
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
156
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
157
|
+
// Run for a short time then kill
|
|
158
|
+
const result = await runCLIInDir(testDir, ['--verbose'], 2000);
|
|
159
|
+
// Should NOT create a temporary config in non-watch mode
|
|
160
|
+
expect(result.stdout).not.toMatch(/Created temporary stencil config/);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('screenshot ignore patterns', () => {
|
|
164
|
+
it('should extract screenshot patterns from vitest config', async () => {
|
|
165
|
+
const stencilConfig = `
|
|
166
|
+
export const config = {
|
|
167
|
+
namespace: 'test',
|
|
168
|
+
outputTargets: [{ type: 'dist' }]
|
|
169
|
+
};
|
|
170
|
+
`;
|
|
171
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
172
|
+
const vitestConfig = `
|
|
173
|
+
export default {
|
|
174
|
+
test: {
|
|
175
|
+
projects: [
|
|
176
|
+
{
|
|
177
|
+
test: {
|
|
178
|
+
browser: {
|
|
179
|
+
enabled: true,
|
|
180
|
+
expect: {
|
|
181
|
+
toMatchScreenshot: {
|
|
182
|
+
screenshotDirectory: 'custom-screenshots'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
`;
|
|
192
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
193
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
194
|
+
const result = await runCLIInDir(testDir, ['--watch', '--verbose'], 2000);
|
|
195
|
+
// Should extract screenshot patterns
|
|
196
|
+
expect(result.stdout).toMatch(/Extracted.*screenshot patterns/);
|
|
197
|
+
});
|
|
198
|
+
it('should include default screenshot patterns', async () => {
|
|
199
|
+
const stencilConfig = `
|
|
200
|
+
export const config = {
|
|
201
|
+
namespace: 'test',
|
|
202
|
+
outputTargets: [{ type: 'dist' }]
|
|
203
|
+
};
|
|
204
|
+
`;
|
|
205
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
206
|
+
const vitestConfig = `
|
|
207
|
+
export default {
|
|
208
|
+
test: {
|
|
209
|
+
projects: []
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
`;
|
|
213
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
214
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
215
|
+
const result = await runCLIInDir(testDir, ['--watch', '--verbose'], 2000);
|
|
216
|
+
// Should add watch ignore patterns
|
|
217
|
+
expect(result.stdout).toMatch(/Added.*watch ignore patterns/);
|
|
218
|
+
});
|
|
219
|
+
it('should merge user watchIgnoredRegex with screenshot patterns', async () => {
|
|
220
|
+
const stencilConfig = `
|
|
221
|
+
export const config = {
|
|
222
|
+
namespace: 'test',
|
|
223
|
+
outputTargets: [{ type: 'dist' }],
|
|
224
|
+
watchIgnoredRegex: [/custom-pattern/]
|
|
225
|
+
};
|
|
226
|
+
`;
|
|
227
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
228
|
+
const vitestConfig = `
|
|
229
|
+
export default {
|
|
230
|
+
test: {
|
|
231
|
+
projects: []
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
`;
|
|
235
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
236
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
237
|
+
const result = await runCLIInDir(testDir, ['--watch', '--verbose'], 3000);
|
|
238
|
+
// Check that temp config was created
|
|
239
|
+
expect(result.stdout).toMatch(/Created temporary/);
|
|
240
|
+
// Check that temp config exists and contains both patterns
|
|
241
|
+
const tempConfigs = require('fs')
|
|
242
|
+
.readdirSync(testDir)
|
|
243
|
+
.filter((f) => f.startsWith('.stencil-test-'));
|
|
244
|
+
if (tempConfigs.length > 0) {
|
|
245
|
+
const tempConfigContent = require('fs').readFileSync(join(testDir, tempConfigs[0]), 'utf-8');
|
|
246
|
+
// Should have user's custom pattern
|
|
247
|
+
expect(tempConfigContent).toContain('custom-pattern');
|
|
248
|
+
// Should have screenshot patterns
|
|
249
|
+
expect(tempConfigContent).toMatch(/__screenshots__|\.png/);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
describe('temp file cleanup', () => {
|
|
254
|
+
it('should cleanup temporary config on exit', async () => {
|
|
255
|
+
const stencilConfig = `
|
|
256
|
+
export const config = {
|
|
257
|
+
namespace: 'test',
|
|
258
|
+
outputTargets: [{ type: 'dist' }]
|
|
259
|
+
};
|
|
260
|
+
`;
|
|
261
|
+
writeFileSync(join(testDir, 'stencil.config.ts'), stencilConfig);
|
|
262
|
+
const vitestConfig = `
|
|
263
|
+
export default {
|
|
264
|
+
test: {
|
|
265
|
+
projects: []
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
`;
|
|
269
|
+
writeFileSync(join(testDir, 'vitest.config.ts'), vitestConfig);
|
|
270
|
+
writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
|
|
271
|
+
// Run and then kill
|
|
272
|
+
await runCLIInDir(testDir, ['--watch', '--verbose'], 2000);
|
|
273
|
+
// Check that temp config was cleaned up
|
|
274
|
+
const tempConfigs = require('fs')
|
|
275
|
+
.readdirSync(testDir)
|
|
276
|
+
.filter((f) => f.startsWith('.stencil-test-'));
|
|
277
|
+
expect(tempConfigs).toHaveLength(0);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
/**
|
|
282
|
+
* Helper function to run the CLI and capture output
|
|
283
|
+
*/
|
|
284
|
+
function runCLI(args, timeout = 1000) {
|
|
285
|
+
return new Promise((resolve) => {
|
|
286
|
+
const proc = spawn('node', [BIN_PATH, ...args], {
|
|
287
|
+
env: { ...process.env, NODE_ENV: 'test' },
|
|
288
|
+
});
|
|
289
|
+
let stdout = '';
|
|
290
|
+
let stderr = '';
|
|
291
|
+
proc.stdout?.on('data', (data) => {
|
|
292
|
+
stdout += data.toString();
|
|
293
|
+
});
|
|
294
|
+
proc.stderr?.on('data', (data) => {
|
|
295
|
+
stderr += data.toString();
|
|
296
|
+
});
|
|
297
|
+
// Kill after timeout
|
|
298
|
+
const timer = setTimeout(() => {
|
|
299
|
+
proc.kill('SIGTERM');
|
|
300
|
+
}, timeout);
|
|
301
|
+
proc.on('exit', (code) => {
|
|
302
|
+
clearTimeout(timer);
|
|
303
|
+
resolve({ stdout, stderr, code });
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Helper function to run CLI in a specific directory
|
|
309
|
+
*/
|
|
310
|
+
function runCLIInDir(cwd, args, timeout = 1000) {
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
const proc = spawn('node', [BIN_PATH, ...args], {
|
|
313
|
+
cwd,
|
|
314
|
+
env: { ...process.env, NODE_ENV: 'test' },
|
|
315
|
+
});
|
|
316
|
+
let stdout = '';
|
|
317
|
+
let stderr = '';
|
|
318
|
+
proc.stdout?.on('data', (data) => {
|
|
319
|
+
stdout += data.toString();
|
|
320
|
+
});
|
|
321
|
+
proc.stderr?.on('data', (data) => {
|
|
322
|
+
stderr += data.toString();
|
|
323
|
+
});
|
|
324
|
+
// Kill after timeout
|
|
325
|
+
const timer = setTimeout(() => {
|
|
326
|
+
proc.kill('SIGTERM');
|
|
327
|
+
setTimeout(() => {
|
|
328
|
+
if (!proc.killed) {
|
|
329
|
+
proc.kill('SIGKILL');
|
|
330
|
+
}
|
|
331
|
+
}, 500);
|
|
332
|
+
}, timeout);
|
|
333
|
+
proc.on('exit', (code) => {
|
|
334
|
+
clearTimeout(timer);
|
|
335
|
+
resolve({ stdout, stderr, code });
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
package/dist/bin/stencil-test.js
CHANGED
|
@@ -391,19 +391,20 @@ async function createTemporaryStencilConfig(userSpecifiedConfig, vitestConfigPat
|
|
|
391
391
|
const tempConfigPath = join(cwd, `.stencil-test-${Date.now()}.config.mjs`);
|
|
392
392
|
// Serialize the config - convert RegExp objects to strings for the output
|
|
393
393
|
const patternsArray = mergedConfig.watchIgnoredRegex.map((pattern) => pattern.toString()).join(',\n ');
|
|
394
|
-
//
|
|
395
|
-
const { _watchIgnoredRegex, ...configWithoutWatch } = mergedConfig;
|
|
396
|
-
// Generate a simple config that doesn't need imports
|
|
394
|
+
// Generate a simple config with user config import
|
|
397
395
|
const tempConfigContent = `
|
|
398
|
-
// Auto-generated temporary config by stencil-test
|
|
399
|
-
// This extends your stencil config and adds watchIgnoredRegex for screenshot files
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
396
|
+
// Auto-generated temporary config by stencil-test
|
|
397
|
+
// This extends your stencil config and adds watchIgnoredRegex for screenshot files
|
|
398
|
+
|
|
399
|
+
import baseConfig from '${userConfigPath}';
|
|
400
|
+
|
|
401
|
+
export const config = {
|
|
402
|
+
...baseConfig,
|
|
403
|
+
"watchIgnoredRegex": [
|
|
404
|
+
${patternsArray}
|
|
405
|
+
]
|
|
406
|
+
};
|
|
407
|
+
`;
|
|
407
408
|
writeFileSync(tempConfigPath, tempConfigContent, 'utf-8');
|
|
408
409
|
if (verbose) {
|
|
409
410
|
log(`Created temporary stencil config at ${tempConfigPath}`);
|
|
@@ -526,9 +527,13 @@ process.on('SIGTERM', () => cleanup());
|
|
|
526
527
|
log(`Stencil exited with code ${code}`);
|
|
527
528
|
}
|
|
528
529
|
// In one-time build mode, stencil exits after build
|
|
529
|
-
//
|
|
530
|
+
// If build failed, exit with the failure code
|
|
530
531
|
if (!args.watch) {
|
|
531
532
|
stencilProcess = null;
|
|
533
|
+
if (code !== 0) {
|
|
534
|
+
log(`Stencil build failed with code ${code}`);
|
|
535
|
+
cleanup(code || 1);
|
|
536
|
+
}
|
|
532
537
|
}
|
|
533
538
|
else {
|
|
534
539
|
// In watch mode, stencil shouldn't exit - if it does, something went wrong
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { importModule } from 'local-pkg';
|
|
2
|
-
export default (async function (
|
|
2
|
+
export default (async function (_global, options) {
|
|
3
3
|
const { Window, GlobalWindow } = (await importModule('happy-dom'));
|
|
4
4
|
const happyDomOptions = {
|
|
5
5
|
url: 'http://localhost:3000',
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from 'vitest';
|
|
2
|
-
export * from './core';
|
|
2
|
+
export * from './core.js';
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* These extend Vitest's expect with Stencil-specific assertions
|
|
5
5
|
*/
|
|
6
|
-
import type { EventSpy } from '../types.js';
|
|
7
6
|
/**
|
|
8
7
|
* Custom matchers interface
|
|
9
8
|
*/
|
|
@@ -51,138 +50,5 @@ declare module 'vitest' {
|
|
|
51
50
|
interface AsymmetricMatchersContaining extends CustomMatchers {
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Check if element has a class
|
|
56
|
-
*/
|
|
57
|
-
export declare function toHaveClass(received: HTMLElement, className: string): {
|
|
58
|
-
pass: boolean;
|
|
59
|
-
message: () => string;
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Check if element has multiple classes
|
|
63
|
-
* Checks if element has all of the specified CSS classes (order doesn't matter)
|
|
64
|
-
*/
|
|
65
|
-
export declare function toHaveClasses(received: HTMLElement, classNames: string[]): {
|
|
66
|
-
pass: boolean;
|
|
67
|
-
message: () => string;
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* Check if element has exactly the specified CSS classes (no more, no less)
|
|
71
|
-
* Order doesn't matter, but the element must have exactly these classes
|
|
72
|
-
*/
|
|
73
|
-
export declare function toMatchClasses(received: HTMLElement, classNames: string[]): {
|
|
74
|
-
pass: boolean;
|
|
75
|
-
message: () => string;
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Check if element has an attribute
|
|
79
|
-
*/
|
|
80
|
-
export declare function toHaveAttribute(received: HTMLElement, attribute: string, value?: string): {
|
|
81
|
-
pass: boolean;
|
|
82
|
-
message: () => string;
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* Check if element has a specific attribute with an exact value
|
|
86
|
-
*/
|
|
87
|
-
export declare function toEqualAttribute(received: HTMLElement, attribute: string, value: string): {
|
|
88
|
-
pass: boolean;
|
|
89
|
-
message: () => string;
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Check if element has all expected attributes with exact values
|
|
93
|
-
*/
|
|
94
|
-
export declare function toEqualAttributes(received: HTMLElement, expectedAttrs: Record<string, string>): {
|
|
95
|
-
pass: boolean;
|
|
96
|
-
message: () => string;
|
|
97
|
-
};
|
|
98
|
-
/**
|
|
99
|
-
* Check if element has a property
|
|
100
|
-
*/
|
|
101
|
-
export declare function toHaveProperty(received: any, property: string, value?: any): {
|
|
102
|
-
pass: boolean;
|
|
103
|
-
message: () => string;
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Check if element has text content
|
|
107
|
-
*/
|
|
108
|
-
export declare function toHaveTextContent(received: HTMLElement, text: string): {
|
|
109
|
-
pass: boolean;
|
|
110
|
-
message: () => string;
|
|
111
|
-
};
|
|
112
|
-
/**
|
|
113
|
-
* Check if element's text content exactly matches (after trimming)
|
|
114
|
-
*/
|
|
115
|
-
export declare function toEqualText(received: HTMLElement, expectedText: string): {
|
|
116
|
-
pass: boolean;
|
|
117
|
-
message: () => string;
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* Check if element has shadow root
|
|
121
|
-
*/
|
|
122
|
-
export declare function toHaveShadowRoot(received: HTMLElement): {
|
|
123
|
-
pass: boolean;
|
|
124
|
-
message: () => string;
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Custom matcher to check if an element's HTML matches the expected HTML
|
|
128
|
-
* Serializes the entire component tree including shadow DOM
|
|
129
|
-
*/
|
|
130
|
-
export declare function toEqualHtml(received: string | HTMLElement | ShadowRoot, expected: string): {
|
|
131
|
-
pass: boolean;
|
|
132
|
-
message: () => string;
|
|
133
|
-
}; /**
|
|
134
|
-
* Custom matcher to check if an element's Light DOM matches the expected HTML
|
|
135
|
-
* Does not serialize shadow DOM
|
|
136
|
-
*/
|
|
137
|
-
export declare function toEqualLightHtml(received: string | HTMLElement | ShadowRoot, expected: string): {
|
|
138
|
-
pass: boolean;
|
|
139
|
-
message: () => string;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* Check if an EventSpy has received at least one event
|
|
143
|
-
*/
|
|
144
|
-
export declare function toHaveReceivedEvent(received: EventSpy): {
|
|
145
|
-
pass: boolean;
|
|
146
|
-
message: () => string;
|
|
147
|
-
};
|
|
148
|
-
/**
|
|
149
|
-
* Check if an EventSpy has received an event a specific number of times
|
|
150
|
-
*/
|
|
151
|
-
export declare function toHaveReceivedEventTimes(received: EventSpy, count: number): {
|
|
152
|
-
pass: boolean;
|
|
153
|
-
message: () => string;
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Check if the last received event has the expected detail
|
|
157
|
-
*/
|
|
158
|
-
export declare function toHaveReceivedEventDetail(received: EventSpy, detail: any): {
|
|
159
|
-
pass: boolean;
|
|
160
|
-
message: () => string;
|
|
161
|
-
};
|
|
162
|
-
/**
|
|
163
|
-
* Check if the first received event has the expected detail
|
|
164
|
-
*/
|
|
165
|
-
export declare function toHaveFirstReceivedEventDetail(received: EventSpy, detail: any): {
|
|
166
|
-
pass: boolean;
|
|
167
|
-
message: () => string;
|
|
168
|
-
};
|
|
169
|
-
/**
|
|
170
|
-
* Check if the last received event has the expected detail (alias for toHaveReceivedEventDetail)
|
|
171
|
-
*/
|
|
172
|
-
export declare function toHaveLastReceivedEventDetail(received: EventSpy, detail: any): {
|
|
173
|
-
pass: boolean;
|
|
174
|
-
message: () => string;
|
|
175
|
-
};
|
|
176
|
-
/**
|
|
177
|
-
* Check if the event at a specific index has the expected detail
|
|
178
|
-
*/
|
|
179
|
-
export declare function toHaveNthReceivedEventDetail(received: EventSpy, index: number, detail: any): {
|
|
180
|
-
pass: boolean;
|
|
181
|
-
message: () => string;
|
|
182
|
-
};
|
|
183
|
-
/**
|
|
184
|
-
* Install custom matchers
|
|
185
|
-
*/
|
|
186
|
-
export declare function installMatchers(): void;
|
|
187
53
|
export {};
|
|
188
54
|
//# sourceMappingURL=matchers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/testing/matchers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/testing/matchers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,UAAU,cAAc,CAAC,CAAC,GAAG,OAAO;IAClC,mDAAmD;IACnD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC;IAClC,qDAAqD;IACrD,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,gFAAgF;IAChF,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IACxC,2EAA2E;IAC3E,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;IACtD,mEAAmE;IACnE,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;IACtD,sEAAsE;IACtE,iBAAiB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5D,0EAA0E;IAC1E,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;IACjD,kEAAkE;IAClE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;IACnC,+EAA+E;IAC/E,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC;IACrC,mDAAmD;IACnD,gBAAgB,IAAI,CAAC,CAAC;IACtB,sFAAsF;IACtF,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC;IACrC,+FAA+F;IAC/F,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC;IAC1C,yDAAyD;IACzD,mBAAmB,IAAI,CAAC,CAAC;IACzB,6EAA6E;IAC7E,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;IAC3C,uEAAuE;IACvE,yBAAyB,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC1C,wEAAwE;IACxE,8BAA8B,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC/C,uEAAuE;IACvE,6BAA6B,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC9C,kFAAkF;IAClF,4BAA4B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;CAC7D;AAGD,OAAO,QAAQ,QAAQ,CAAC;IACtB,UAAU,SAAS,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;KAAG;IACzD,UAAU,4BAA6B,SAAQ,cAAc;KAAG;CACjE;AAkhBD,OAAO,EAAE,CAAC"}
|
package/dist/testing/matchers.js
CHANGED
|
@@ -8,7 +8,7 @@ import { serializeHtml, normalizeHtml, prettifyHtml } from './html-serializer.js
|
|
|
8
8
|
/**
|
|
9
9
|
* Check if element has a class
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
function toHaveClass(received, className) {
|
|
12
12
|
const pass = received.classList.contains(className);
|
|
13
13
|
return {
|
|
14
14
|
pass,
|
|
@@ -19,7 +19,7 @@ export function toHaveClass(received, className) {
|
|
|
19
19
|
* Check if element has multiple classes
|
|
20
20
|
* Checks if element has all of the specified CSS classes (order doesn't matter)
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
function toHaveClasses(received, classNames) {
|
|
23
23
|
const missingClasses = [];
|
|
24
24
|
for (const className of classNames) {
|
|
25
25
|
if (!received.classList.contains(className)) {
|
|
@@ -39,7 +39,7 @@ export function toHaveClasses(received, classNames) {
|
|
|
39
39
|
* Check if element has exactly the specified CSS classes (no more, no less)
|
|
40
40
|
* Order doesn't matter, but the element must have exactly these classes
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
function toMatchClasses(received, classNames) {
|
|
43
43
|
// Get classes from the class attribute to support mock-doc
|
|
44
44
|
const classAttr = received.getAttribute('class') || '';
|
|
45
45
|
const actualClasses = classAttr.split(/\s+/).filter(Boolean).sort();
|
|
@@ -55,7 +55,7 @@ export function toMatchClasses(received, classNames) {
|
|
|
55
55
|
/**
|
|
56
56
|
* Check if element has an attribute
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
function toHaveAttribute(received, attribute, value) {
|
|
59
59
|
const hasAttribute = received.hasAttribute(attribute);
|
|
60
60
|
if (!hasAttribute) {
|
|
61
61
|
return {
|
|
@@ -81,7 +81,7 @@ export function toHaveAttribute(received, attribute, value) {
|
|
|
81
81
|
/**
|
|
82
82
|
* Check if element has a specific attribute with an exact value
|
|
83
83
|
*/
|
|
84
|
-
|
|
84
|
+
function toEqualAttribute(received, attribute, value) {
|
|
85
85
|
const actualValue = received.getAttribute(attribute);
|
|
86
86
|
const pass = actualValue === value;
|
|
87
87
|
return {
|
|
@@ -94,7 +94,7 @@ export function toEqualAttribute(received, attribute, value) {
|
|
|
94
94
|
/**
|
|
95
95
|
* Check if element has all expected attributes with exact values
|
|
96
96
|
*/
|
|
97
|
-
|
|
97
|
+
function toEqualAttributes(received, expectedAttrs) {
|
|
98
98
|
const mismatches = [];
|
|
99
99
|
const actualAttrs = {};
|
|
100
100
|
// Collect all actual attributes
|
|
@@ -123,7 +123,7 @@ export function toEqualAttributes(received, expectedAttrs) {
|
|
|
123
123
|
/**
|
|
124
124
|
* Check if element has a property
|
|
125
125
|
*/
|
|
126
|
-
|
|
126
|
+
function toHaveProperty(received, property, value) {
|
|
127
127
|
const hasProperty = property in received;
|
|
128
128
|
if (!hasProperty) {
|
|
129
129
|
return {
|
|
@@ -149,7 +149,7 @@ export function toHaveProperty(received, property, value) {
|
|
|
149
149
|
/**
|
|
150
150
|
* Check if element has text content
|
|
151
151
|
*/
|
|
152
|
-
|
|
152
|
+
function toHaveTextContent(received, text) {
|
|
153
153
|
const actualText = received.textContent || '';
|
|
154
154
|
const pass = actualText.includes(text);
|
|
155
155
|
return {
|
|
@@ -162,7 +162,7 @@ export function toHaveTextContent(received, text) {
|
|
|
162
162
|
/**
|
|
163
163
|
* Check if element's text content exactly matches (after trimming)
|
|
164
164
|
*/
|
|
165
|
-
|
|
165
|
+
function toEqualText(received, expectedText) {
|
|
166
166
|
const actualText = (received.textContent || '').trim();
|
|
167
167
|
const trimmedExpected = expectedText.trim();
|
|
168
168
|
const pass = actualText === trimmedExpected;
|
|
@@ -176,7 +176,7 @@ export function toEqualText(received, expectedText) {
|
|
|
176
176
|
/**
|
|
177
177
|
* Check if element has shadow root
|
|
178
178
|
*/
|
|
179
|
-
|
|
179
|
+
function toHaveShadowRoot(received) {
|
|
180
180
|
const pass = !!received.shadowRoot;
|
|
181
181
|
return {
|
|
182
182
|
pass,
|
|
@@ -206,7 +206,7 @@ function parseHtmlFragment(html) {
|
|
|
206
206
|
* Custom matcher to check if an element's HTML matches the expected HTML
|
|
207
207
|
* Serializes the entire component tree including shadow DOM
|
|
208
208
|
*/
|
|
209
|
-
|
|
209
|
+
function toEqualHtml(received, expected) {
|
|
210
210
|
if (received == null) {
|
|
211
211
|
throw new Error(`expect.toEqualHtml() received value is "${received}"`);
|
|
212
212
|
}
|
|
@@ -247,7 +247,7 @@ export function toEqualHtml(received, expected) {
|
|
|
247
247
|
* Custom matcher to check if an element's Light DOM matches the expected HTML
|
|
248
248
|
* Does not serialize shadow DOM
|
|
249
249
|
*/
|
|
250
|
-
|
|
250
|
+
function toEqualLightHtml(received, expected) {
|
|
251
251
|
if (received == null) {
|
|
252
252
|
throw new Error(`expect.toEqualLightHtml() received value is "${received}"`);
|
|
253
253
|
}
|
|
@@ -287,7 +287,7 @@ export function toEqualLightHtml(received, expected) {
|
|
|
287
287
|
/**
|
|
288
288
|
* Check if an EventSpy has received at least one event
|
|
289
289
|
*/
|
|
290
|
-
|
|
290
|
+
function toHaveReceivedEvent(received) {
|
|
291
291
|
const pass = received.length > 0;
|
|
292
292
|
return {
|
|
293
293
|
pass,
|
|
@@ -299,7 +299,7 @@ export function toHaveReceivedEvent(received) {
|
|
|
299
299
|
/**
|
|
300
300
|
* Check if an EventSpy has received an event a specific number of times
|
|
301
301
|
*/
|
|
302
|
-
|
|
302
|
+
function toHaveReceivedEventTimes(received, count) {
|
|
303
303
|
const pass = received.length === count;
|
|
304
304
|
return {
|
|
305
305
|
pass,
|
|
@@ -343,7 +343,7 @@ function safeEquals(a, b) {
|
|
|
343
343
|
/**
|
|
344
344
|
* Check if the last received event has the expected detail
|
|
345
345
|
*/
|
|
346
|
-
|
|
346
|
+
function toHaveReceivedEventDetail(received, detail) {
|
|
347
347
|
if (received.length === 0) {
|
|
348
348
|
return {
|
|
349
349
|
pass: false,
|
|
@@ -362,7 +362,7 @@ export function toHaveReceivedEventDetail(received, detail) {
|
|
|
362
362
|
/**
|
|
363
363
|
* Check if the first received event has the expected detail
|
|
364
364
|
*/
|
|
365
|
-
|
|
365
|
+
function toHaveFirstReceivedEventDetail(received, detail) {
|
|
366
366
|
if (received.length === 0) {
|
|
367
367
|
return {
|
|
368
368
|
pass: false,
|
|
@@ -381,13 +381,13 @@ export function toHaveFirstReceivedEventDetail(received, detail) {
|
|
|
381
381
|
/**
|
|
382
382
|
* Check if the last received event has the expected detail (alias for toHaveReceivedEventDetail)
|
|
383
383
|
*/
|
|
384
|
-
|
|
384
|
+
function toHaveLastReceivedEventDetail(received, detail) {
|
|
385
385
|
return toHaveReceivedEventDetail(received, detail);
|
|
386
386
|
}
|
|
387
387
|
/**
|
|
388
388
|
* Check if the event at a specific index has the expected detail
|
|
389
389
|
*/
|
|
390
|
-
|
|
390
|
+
function toHaveNthReceivedEventDetail(received, index, detail) {
|
|
391
391
|
if (received.length === 0) {
|
|
392
392
|
return {
|
|
393
393
|
pass: false,
|
|
@@ -412,7 +412,7 @@ export function toHaveNthReceivedEventDetail(received, index, detail) {
|
|
|
412
412
|
/**
|
|
413
413
|
* Install custom matchers
|
|
414
414
|
*/
|
|
415
|
-
|
|
415
|
+
function installMatchers() {
|
|
416
416
|
expect.extend({
|
|
417
417
|
toHaveClass,
|
|
418
418
|
toHaveClasses,
|
|
@@ -4,14 +4,5 @@
|
|
|
4
4
|
* Formats HTMLElements with shadow DOM using the same serialization
|
|
5
5
|
* as our toEqualHtml matcher, ensuring consistent <mock:shadow-root> output
|
|
6
6
|
*/
|
|
7
|
-
|
|
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;
|
|
7
|
+
export {};
|
|
17
8
|
//# sourceMappingURL=snapshot-serializer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-serializer.d.ts","sourceRoot":"","sources":["../../src/testing/snapshot-serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"snapshot-serializer.d.ts","sourceRoot":"","sources":["../../src/testing/snapshot-serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkDH,OAAO,EAAE,CAAC"}
|
|
@@ -9,7 +9,7 @@ import { serializeHtml } from './html-serializer.js';
|
|
|
9
9
|
/**
|
|
10
10
|
* Vitest snapshot serializer for Stencil components
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const StencilSnapshotSerializer = {
|
|
13
13
|
/**
|
|
14
14
|
* Test if this serializer should handle the value
|
|
15
15
|
*/
|
|
@@ -40,11 +40,7 @@ export const StencilSnapshotSerializer = {
|
|
|
40
40
|
});
|
|
41
41
|
},
|
|
42
42
|
};
|
|
43
|
-
|
|
44
|
-
* Default export for convenience
|
|
45
|
-
*/
|
|
46
|
-
export default StencilSnapshotSerializer;
|
|
47
|
-
export function installMatchers() {
|
|
43
|
+
function installMatchers() {
|
|
48
44
|
expect.addSnapshotSerializer(StencilSnapshotSerializer);
|
|
49
45
|
}
|
|
50
46
|
installMatchers();
|
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.1.
|
|
7
|
+
"version": "1.1.6",
|
|
8
8
|
"description": "First-class testing utilities for Stencil design systems with Vitest",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"type": "module",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"dependencies": {
|
|
93
93
|
"jiti": "^2.6.1",
|
|
94
94
|
"local-pkg": "^1.1.2",
|
|
95
|
-
"vitest-environment-stencil": "1.1.
|
|
95
|
+
"vitest-environment-stencil": "1.1.6"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@eslint/js": "^9.39.2",
|