@describe-me/vitest 0.6.1 → 0.7.0
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/check-tags-supported.d.ts +7 -0
- package/dist/check-tags-supported.js +16 -0
- package/dist/docs-tag-definitions.d.ts +9 -0
- package/dist/docs-tag-definitions.js +14 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.js +14 -1
- package/dist/read-live-vitest-major.d.ts +2 -2
- package/dist/read-live-vitest-major.js +4 -14
- package/dist/read-runtime-options.d.ts +4 -1
- package/dist/read-runtime-options.js +12 -6
- package/dist/read-vitest-version.d.ts +10 -0
- package/dist/read-vitest-version.js +21 -0
- package/dist/register-recording-hooks.d.ts +2 -1
- package/dist/register-recording-hooks.js +6 -2
- package/dist/reporter.d.ts +11 -3
- package/dist/reporter.js +27 -5
- package/dist/runtime-options.d.ts +2 -0
- package/dist/stories-mode.d.ts +9 -0
- package/dist/stories-mode.js +7 -0
- package/dist/tag-filter.d.ts +7 -0
- package/dist/tag-filter.js +14 -0
- package/dist/validate-stories.d.ts +3 -0
- package/dist/validate-stories.js +11 -0
- package/package.json +3 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StoriesMode } from './stories-mode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test tags arrived in Vitest 4.1. Without them `stories: 'tagged'` would
|
|
4
|
+
* record nothing, so an older Vitest fails before any test runs. A version
|
|
5
|
+
* that cannot be read passes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function checkTagsSupported(stories: StoriesMode, root: string): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readVitestVersion } from './read-vitest-version.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test tags arrived in Vitest 4.1. Without them `stories: 'tagged'` would
|
|
4
|
+
* record nothing, so an older Vitest fails before any test runs. A version
|
|
5
|
+
* that cannot be read passes.
|
|
6
|
+
*/
|
|
7
|
+
export function checkTagsSupported(stories, root) {
|
|
8
|
+
if (stories !== 'tagged') {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const version = readVitestVersion(root);
|
|
12
|
+
if (version === null || version.major > 4 || (version.major === 4 && version.minor >= 1)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
throw new Error(`describe-me: stories: 'tagged' needs test tags, which arrived in Vitest 4.1. This project resolves Vitest ${version.major}.${version.minor}.`);
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ViteUserConfig } from 'vitest/config';
|
|
2
|
+
type TagDefinition = NonNullable<NonNullable<ViteUserConfig['test']>['tags']>[number];
|
|
3
|
+
/**
|
|
4
|
+
* The definitions of `docs` and `no-docs` that the user's config lacks.
|
|
5
|
+
* Vitest throws on a tag it has no definition for, and Vite concatenates this
|
|
6
|
+
* list with the user's, so a tag the user defines is left to them.
|
|
7
|
+
*/
|
|
8
|
+
export declare function docsTagDefinitions(userConfig: ViteUserConfig): TagDefinition[];
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DOCS_TAG, NO_DOCS_TAG } from './stories-mode.js';
|
|
2
|
+
const DEFINITIONS = [
|
|
3
|
+
{ name: DOCS_TAG, description: "describe-me: a story, when stories is 'tagged'" },
|
|
4
|
+
{ name: NO_DOCS_TAG, description: 'describe-me: never a story' },
|
|
5
|
+
];
|
|
6
|
+
/**
|
|
7
|
+
* The definitions of `docs` and `no-docs` that the user's config lacks.
|
|
8
|
+
* Vitest throws on a tag it has no definition for, and Vite concatenates this
|
|
9
|
+
* list with the user's, so a tag the user defines is left to them.
|
|
10
|
+
*/
|
|
11
|
+
export function docsTagDefinitions(userConfig) {
|
|
12
|
+
const defined = new Set((userConfig.test?.tags ?? []).map((tag) => tag.name));
|
|
13
|
+
return DEFINITIONS.filter((tag) => !defined.has(tag.name));
|
|
14
|
+
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ViteUserConfig } from 'vitest/config';
|
|
2
2
|
import { type RenderFrameMode } from '@describe-me/core/types';
|
|
3
|
+
import type { StoriesMode } from './stories-mode.js';
|
|
3
4
|
/** Where the tests run: Vitest browser mode, or a simulated DOM such as jsdom. */
|
|
4
5
|
export type DescribeMeEnvironment = 'browser' | 'dom';
|
|
5
6
|
export interface DescribeMeOptions {
|
|
@@ -39,6 +40,13 @@ export interface DescribeMeOptions {
|
|
|
39
40
|
include?: string | string[];
|
|
40
41
|
/** Test files never to record, as globs like `include`. Wins over `include`. Default: none. */
|
|
41
42
|
exclude?: string | string[];
|
|
43
|
+
/**
|
|
44
|
+
* Which tests of the recorded files become stories. `'all'` records every
|
|
45
|
+
* test, `'tagged'` only those tagged `docs`, on the test, a `describe` or the
|
|
46
|
+
* file (`@module-tag docs`). A `no-docs` tag leaves a test out in both modes.
|
|
47
|
+
* The plugin defines both tags, which need Vitest 4.1. Default: `'all'`.
|
|
48
|
+
*/
|
|
49
|
+
stories?: StoriesMode;
|
|
42
50
|
/**
|
|
43
51
|
* HTML the viewer adds to the start of the `<head>` of every replayed frame,
|
|
44
52
|
* like Storybook's `preview-head.html`. It never reaches the test page.
|
package/dist/plugin.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
1
2
|
import { LIVE_PROTOCOL } from '@describe-me/core/types';
|
|
2
3
|
import { adapterPackageRoot } from './adapter-package-root.js';
|
|
4
|
+
import { checkTagsSupported } from './check-tags-supported.js';
|
|
3
5
|
import { compileGlobs } from './compile-globs.js';
|
|
4
6
|
import { compileInclude } from './compile-include.js';
|
|
5
7
|
import { defaultReporters } from './default-reporters.js';
|
|
6
8
|
import { detectEnvironment } from './detect-environment.js';
|
|
9
|
+
import { docsTagDefinitions } from './docs-tag-definitions.js';
|
|
7
10
|
import { isAdapterModule } from './is-adapter-module.js';
|
|
8
11
|
import { LIVE_ENV } from './live-env.js';
|
|
9
12
|
import { livePlugin } from './live-plugin.js';
|
|
@@ -15,6 +18,7 @@ import DescribeMeReporter from './reporter.js';
|
|
|
15
18
|
import { RUNTIME_OPTIONS_KEY } from './runtime-options.js';
|
|
16
19
|
import { setUpProject } from './set-up-project.js';
|
|
17
20
|
import { validateRenderFrame } from './validate-render-frame.js';
|
|
21
|
+
import { validateStories } from './validate-stories.js';
|
|
18
22
|
/**
|
|
19
23
|
* One-line integration: `plugins: [describeMe()]`.
|
|
20
24
|
*
|
|
@@ -58,7 +62,9 @@ export function describeMe(options = {}) {
|
|
|
58
62
|
include: compileInclude(options.include),
|
|
59
63
|
exclude: compileGlobs(options.exclude),
|
|
60
64
|
renderFrame: validateRenderFrame(options.renderFrame),
|
|
65
|
+
stories: validateStories(options.stories),
|
|
61
66
|
};
|
|
67
|
+
checkTagsSupported(runtimeOptions.stories, resolve(userConfig.root ?? process.cwd()));
|
|
62
68
|
const provide = { [RUNTIME_OPTIONS_KEY]: runtimeOptions };
|
|
63
69
|
const profile = await loadProfile(environment);
|
|
64
70
|
renderModule = profile.renderModules[framework];
|
|
@@ -68,6 +74,7 @@ export function describeMe(options = {}) {
|
|
|
68
74
|
outDir,
|
|
69
75
|
include: options.include,
|
|
70
76
|
exclude: options.exclude,
|
|
77
|
+
stories: runtimeOptions.stories,
|
|
71
78
|
previewHead: options.previewHead,
|
|
72
79
|
setupWarnings,
|
|
73
80
|
});
|
|
@@ -77,7 +84,13 @@ export function describeMe(options = {}) {
|
|
|
77
84
|
const reporters = userConfig.test?.reporters ? [reporter] : [...defaultReporters(), reporter];
|
|
78
85
|
return {
|
|
79
86
|
...config,
|
|
80
|
-
test: {
|
|
87
|
+
test: {
|
|
88
|
+
setupFiles: [profile.setupFile],
|
|
89
|
+
...config.test,
|
|
90
|
+
reporters,
|
|
91
|
+
provide,
|
|
92
|
+
tags: docsTagDefinitions(userConfig),
|
|
93
|
+
},
|
|
81
94
|
};
|
|
82
95
|
},
|
|
83
96
|
// Runs for every project the plugin's Vite server serves, also those that
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The major version of the `vitest` that the root resolves, whose rules the
|
|
3
|
-
* live page names `each` and `for` rows by.
|
|
4
|
-
*
|
|
3
|
+
* live page names `each` and `for` rows by. Falls back to 5 when it cannot be
|
|
4
|
+
* read.
|
|
5
5
|
*/
|
|
6
6
|
export declare function readLiveVitestMajor(root: string): number;
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import { join } from 'node:path';
|
|
1
|
+
import { readVitestVersion } from './read-vitest-version.js';
|
|
4
2
|
const DEFAULT_MAJOR = 5;
|
|
5
3
|
/**
|
|
6
4
|
* The major version of the `vitest` that the root resolves, whose rules the
|
|
7
|
-
* live page names `each` and `for` rows by.
|
|
8
|
-
*
|
|
5
|
+
* live page names `each` and `for` rows by. Falls back to 5 when it cannot be
|
|
6
|
+
* read.
|
|
9
7
|
*/
|
|
10
8
|
export function readLiveVitestMajor(root) {
|
|
11
|
-
|
|
12
|
-
const file = createRequire(join(root, 'package.json')).resolve('vitest/package.json');
|
|
13
|
-
const { version } = JSON.parse(readFileSync(file, 'utf8'));
|
|
14
|
-
const major = typeof version === 'string' ? Number.parseInt(version, 10) : Number.NaN;
|
|
15
|
-
return Number.isInteger(major) && major > 0 ? major : DEFAULT_MAJOR;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return DEFAULT_MAJOR;
|
|
19
|
-
}
|
|
9
|
+
return readVitestVersion(root)?.major ?? DEFAULT_MAJOR;
|
|
20
10
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { type RuntimeOptions } from './runtime-options.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Without the plugin nothing is provided, so a hand-wired setup keeps recording
|
|
4
|
+
* every test. The defaults also fill what an older plugin does not provide.
|
|
5
|
+
*/
|
|
3
6
|
export declare function readRuntimeOptions(): RuntimeOptions;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { inject } from 'vitest';
|
|
2
2
|
import { RUNTIME_OPTIONS_KEY } from './runtime-options.js';
|
|
3
|
-
|
|
3
|
+
const DEFAULTS = {
|
|
4
|
+
include: null,
|
|
5
|
+
exclude: [],
|
|
6
|
+
renderFrame: 'eager',
|
|
7
|
+
stories: 'all',
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Without the plugin nothing is provided, so a hand-wired setup keeps recording
|
|
11
|
+
* every test. The defaults also fill what an older plugin does not provide.
|
|
12
|
+
*/
|
|
4
13
|
export function readRuntimeOptions() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exclude: [],
|
|
8
|
-
renderFrame: 'eager',
|
|
9
|
-
});
|
|
14
|
+
const provided = inject(RUNTIME_OPTIONS_KEY);
|
|
15
|
+
return { ...DEFAULTS, ...provided };
|
|
10
16
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A Vitest release, as far as describe-me needs to tell them apart. */
|
|
2
|
+
export interface VitestVersion {
|
|
3
|
+
major: number;
|
|
4
|
+
minor: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The version of the `vitest` that the root resolves, or `null` when it cannot
|
|
8
|
+
* be read. Vitest exports its `package.json`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readVitestVersion(root: string): VitestVersion | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* The version of the `vitest` that the root resolves, or `null` when it cannot
|
|
6
|
+
* be read. Vitest exports its `package.json`.
|
|
7
|
+
*/
|
|
8
|
+
export function readVitestVersion(root) {
|
|
9
|
+
try {
|
|
10
|
+
const file = createRequire(join(root, 'package.json')).resolve('vitest/package.json');
|
|
11
|
+
const { version } = JSON.parse(readFileSync(file, 'utf8'));
|
|
12
|
+
const [major, minor] = typeof version === 'string' ? version.split('.').map(Number) : [];
|
|
13
|
+
if (!Number.isInteger(major) || major <= 0 || !Number.isInteger(minor)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return { major, minor };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -3,7 +3,8 @@ import type { RuntimeOptions } from './runtime-options.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* The per-test lifecycle shared by every environment: begin a recording, take
|
|
5
5
|
* a deferred render frame or else the closing frame, hand the frames to the
|
|
6
|
-
* reporter via task.meta, and only then let adapters unmount.
|
|
6
|
+
* reporter via task.meta, and only then let adapters unmount. Which tests
|
|
7
|
+
* record follows the file globs and then the test's tags. Teardown runs
|
|
7
8
|
* for unrecorded tests too, because Testing Library's auto-cleanup is off in
|
|
8
9
|
* jsdom. Concurrent tests would share the one recorder, so they are not
|
|
9
10
|
* recorded. The hooks destructure the context, because Vitest rejects any
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { afterEach, beforeEach } from 'vitest';
|
|
2
2
|
import { META_KEY, recorder } from '@describe-me/core';
|
|
3
3
|
import { fileFilter } from './file-filter.js';
|
|
4
|
+
import { tagFilter } from './tag-filter.js';
|
|
4
5
|
/**
|
|
5
6
|
* The per-test lifecycle shared by every environment: begin a recording, take
|
|
6
7
|
* a deferred render frame or else the closing frame, hand the frames to the
|
|
7
|
-
* reporter via task.meta, and only then let adapters unmount.
|
|
8
|
+
* reporter via task.meta, and only then let adapters unmount. Which tests
|
|
9
|
+
* record follows the file globs and then the test's tags. Teardown runs
|
|
8
10
|
* for unrecorded tests too, because Testing Library's auto-cleanup is off in
|
|
9
11
|
* jsdom. Concurrent tests would share the one recorder, so they are not
|
|
10
12
|
* recorded. The hooks destructure the context, because Vitest rejects any
|
|
@@ -12,6 +14,7 @@ import { fileFilter } from './file-filter.js';
|
|
|
12
14
|
*/
|
|
13
15
|
export function registerRecordingHooks(options, aroundWait) {
|
|
14
16
|
const isRecordedFile = fileFilter(options);
|
|
17
|
+
const isRecordedTags = tagFilter(options.stories);
|
|
15
18
|
let warnedConcurrent = false;
|
|
16
19
|
recorder.configure({ renderFrame: options.renderFrame, aroundWait });
|
|
17
20
|
beforeEach(({ task }) => {
|
|
@@ -22,7 +25,8 @@ export function registerRecordingHooks(options, aroundWait) {
|
|
|
22
25
|
}
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
// Vitest before 4.1 has no tags, so every test counts as untagged.
|
|
29
|
+
if (isRecordedFile(task.file.name) && isRecordedTags(task.tags ?? [])) {
|
|
26
30
|
recorder.begin();
|
|
27
31
|
}
|
|
28
32
|
});
|
package/dist/reporter.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Reporter, TestModule, Vitest } from 'vitest/node';
|
|
2
|
+
import type { StoriesMode } from './stories-mode.js';
|
|
2
3
|
export interface DescribeMeReporterOptions {
|
|
3
4
|
/** Output directory, relative to the Vitest root. Default: `.describe-me` */
|
|
4
5
|
outDir?: string;
|
|
@@ -13,6 +14,11 @@ export interface DescribeMeReporterOptions {
|
|
|
13
14
|
* `include`. Default: none.
|
|
14
15
|
*/
|
|
15
16
|
exclude?: string | string[];
|
|
17
|
+
/**
|
|
18
|
+
* Which tests appear in the manifest: `'all'`, or only those tagged `docs`.
|
|
19
|
+
* A test tagged `no-docs` never does. Default: `'all'`.
|
|
20
|
+
*/
|
|
21
|
+
stories?: StoriesMode;
|
|
16
22
|
/**
|
|
17
23
|
* HTML the viewer adds to the start of the `<head>` of every replayed frame,
|
|
18
24
|
* like Storybook's `preview-head.html`. It never reaches the test page.
|
|
@@ -25,6 +31,7 @@ export interface DescribeMeReporterOptions {
|
|
|
25
31
|
export default class DescribeMeReporter implements Reporter {
|
|
26
32
|
private readonly outDirOption;
|
|
27
33
|
private readonly isRecordedFile;
|
|
34
|
+
private readonly isRecordedTags;
|
|
28
35
|
private readonly emptyInclude;
|
|
29
36
|
private readonly previewHead;
|
|
30
37
|
private readonly generator;
|
|
@@ -44,7 +51,8 @@ export default class DescribeMeReporter implements Reporter {
|
|
|
44
51
|
* A filtered run (`vitest run Button.test.tsx`) must not wipe the other
|
|
45
52
|
* modules from the manifest, so start from whatever the last run wrote.
|
|
46
53
|
* Modules whose file is gone or no longer selected by `include` / `exclude`
|
|
47
|
-
* are dropped, and so are
|
|
54
|
+
* are dropped, and so are tests that `stories` no longer selects and the
|
|
55
|
+
* empty tests that 0.4 still wrote.
|
|
48
56
|
*/
|
|
49
57
|
private seedFromPreviousRun;
|
|
50
58
|
/**
|
|
@@ -54,8 +62,8 @@ export default class DescribeMeReporter implements Reporter {
|
|
|
54
62
|
*/
|
|
55
63
|
private withStableIds;
|
|
56
64
|
/**
|
|
57
|
-
* A test skipped in this run keeps its previous entry
|
|
58
|
-
* not erase documentation.
|
|
65
|
+
* A test skipped in this run keeps its previous entry with its current tags,
|
|
66
|
+
* so a filtered run does not erase documentation.
|
|
59
67
|
*/
|
|
60
68
|
onTestModuleEnd(module: TestModule): void;
|
|
61
69
|
onTestRunEnd(): Promise<void>;
|
package/dist/reporter.js
CHANGED
|
@@ -18,6 +18,7 @@ import { readGenerator } from './read-generator.js';
|
|
|
18
18
|
import { SnapshotStore } from './snapshot-store.js';
|
|
19
19
|
import { stableTestIds } from './stable-test-ids.js';
|
|
20
20
|
import { StyleStore } from './style-store.js';
|
|
21
|
+
import { tagFilter } from './tag-filter.js';
|
|
21
22
|
import { testPath } from './test-path.js';
|
|
22
23
|
function toPosix(moduleId) {
|
|
23
24
|
return moduleId.split(sep).join('/');
|
|
@@ -25,9 +26,16 @@ function toPosix(moduleId) {
|
|
|
25
26
|
function recordOf(tc) {
|
|
26
27
|
return tc.meta()[META_KEY];
|
|
27
28
|
}
|
|
29
|
+
/** The test's tags for the manifest, where `undefined` leaves them out of the JSON. */
|
|
30
|
+
function tagsOf(tc) {
|
|
31
|
+
// Vitest before 4.1 has no tags.
|
|
32
|
+
const tags = tc.tags ?? [];
|
|
33
|
+
return tags.length > 0 ? tags : undefined;
|
|
34
|
+
}
|
|
28
35
|
export default class DescribeMeReporter {
|
|
29
36
|
outDirOption;
|
|
30
37
|
isRecordedFile;
|
|
38
|
+
isRecordedTags;
|
|
31
39
|
emptyInclude;
|
|
32
40
|
previewHead;
|
|
33
41
|
generator;
|
|
@@ -44,6 +52,7 @@ export default class DescribeMeReporter {
|
|
|
44
52
|
this.outDirOption = options.outDir ?? '.describe-me';
|
|
45
53
|
const include = compileInclude(options.include);
|
|
46
54
|
this.isRecordedFile = fileFilter({ include, exclude: compileGlobs(options.exclude) });
|
|
55
|
+
this.isRecordedTags = tagFilter(options.stories ?? 'all');
|
|
47
56
|
this.emptyInclude = include !== null && include.length === 0;
|
|
48
57
|
// A blank head, such as an empty preview-head.html, is the same as none.
|
|
49
58
|
this.previewHead = options.previewHead?.trim() ? options.previewHead : undefined;
|
|
@@ -76,7 +85,8 @@ export default class DescribeMeReporter {
|
|
|
76
85
|
* A filtered run (`vitest run Button.test.tsx`) must not wipe the other
|
|
77
86
|
* modules from the manifest, so start from whatever the last run wrote.
|
|
78
87
|
* Modules whose file is gone or no longer selected by `include` / `exclude`
|
|
79
|
-
* are dropped, and so are
|
|
88
|
+
* are dropped, and so are tests that `stories` no longer selects and the
|
|
89
|
+
* empty tests that 0.4 still wrote.
|
|
80
90
|
*/
|
|
81
91
|
seedFromPreviousRun() {
|
|
82
92
|
const file = join(this.outDir, 'manifest.json');
|
|
@@ -86,7 +96,7 @@ export default class DescribeMeReporter {
|
|
|
86
96
|
try {
|
|
87
97
|
const previous = JSON.parse(readFileSync(file, 'utf8'));
|
|
88
98
|
for (const mod of previous.modules ?? []) {
|
|
89
|
-
const tests = this.withStableIds(mod).filter((test) => isRecordedTest(test));
|
|
99
|
+
const tests = this.withStableIds(mod).filter((test) => isRecordedTest(test) && this.isRecordedTags(test.tags ?? []));
|
|
90
100
|
if (tests.length > 0 &&
|
|
91
101
|
this.isRecordedModule(mod.id) &&
|
|
92
102
|
existsSync(resolve(this.root, mod.id))) {
|
|
@@ -113,8 +123,8 @@ export default class DescribeMeReporter {
|
|
|
113
123
|
});
|
|
114
124
|
}
|
|
115
125
|
/**
|
|
116
|
-
* A test skipped in this run keeps its previous entry
|
|
117
|
-
* not erase documentation.
|
|
126
|
+
* A test skipped in this run keeps its previous entry with its current tags,
|
|
127
|
+
* so a filtered run does not erase documentation.
|
|
118
128
|
*/
|
|
119
129
|
onTestModuleEnd(module) {
|
|
120
130
|
const id = relative(this.root, module.moduleId);
|
|
@@ -198,6 +208,11 @@ export default class DescribeMeReporter {
|
|
|
198
208
|
}
|
|
199
209
|
/** Decided before any snapshot is written, so a dropped test writes no file. */
|
|
200
210
|
manifestTestOf(tc, stableId, previous) {
|
|
211
|
+
// Before the skipped branch, so a test tagged out since the last run does
|
|
212
|
+
// not survive a filtered run on its old frames. Vitest before 4.1 has no tags.
|
|
213
|
+
if (!this.isRecordedTags(tc.tags ?? [])) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
201
216
|
const record = recordOf(tc);
|
|
202
217
|
if (record && isRecordedTest(record)) {
|
|
203
218
|
return this.toManifestTest(tc, stableId, record);
|
|
@@ -205,7 +220,13 @@ export default class DescribeMeReporter {
|
|
|
205
220
|
if (tc.result().state !== 'skipped') {
|
|
206
221
|
return undefined;
|
|
207
222
|
}
|
|
208
|
-
|
|
223
|
+
const kept = previous?.tests.find((test) => test.id === stableId && isRecordedTest(test));
|
|
224
|
+
if (kept === undefined) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
// Tags belong to the test, not to the run, and Vitest knows them for a
|
|
228
|
+
// skipped test too. The viewer shows and filters by them.
|
|
229
|
+
return { ...kept, tags: tagsOf(tc) };
|
|
209
230
|
}
|
|
210
231
|
toManifestTest(tc, stableId, record) {
|
|
211
232
|
const result = tc.result();
|
|
@@ -223,6 +244,7 @@ export default class DescribeMeReporter {
|
|
|
223
244
|
name: tc.name,
|
|
224
245
|
path: testPath(tc),
|
|
225
246
|
fullName: tc.fullName,
|
|
247
|
+
tags: tagsOf(tc),
|
|
226
248
|
state: result.state ?? 'pending',
|
|
227
249
|
duration: tc.diagnostic()?.duration,
|
|
228
250
|
errors: result.errors?.map((error) => ({ message: error.message, stack: error.stack })),
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* only types.
|
|
6
6
|
*/
|
|
7
7
|
import type { RenderFrameMode } from '@describe-me/core/types';
|
|
8
|
+
import type { StoriesMode } from './stories-mode.js';
|
|
8
9
|
export interface CompiledGlob {
|
|
9
10
|
source: string;
|
|
10
11
|
flags: string;
|
|
@@ -18,6 +19,7 @@ export interface RuntimeOptions {
|
|
|
18
19
|
/** Wins over `include`. */
|
|
19
20
|
exclude: CompiledGlob[];
|
|
20
21
|
renderFrame: RenderFrameMode;
|
|
22
|
+
stories: StoriesMode;
|
|
21
23
|
}
|
|
22
24
|
export declare const RUNTIME_OPTIONS_KEY: "describe-me";
|
|
23
25
|
declare module 'vitest' {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `stories` option and its two tags. Kept apart from `runtime-options.ts`,
|
|
3
|
+
* because the public types import this file and must not carry that file's
|
|
4
|
+
* augmentation of Vitest's `ProvidedContext`. The test runtime imports it, so
|
|
5
|
+
* no Node imports. The tags live in core, which the viewer reads too.
|
|
6
|
+
*/
|
|
7
|
+
export { DOCS_TAG, NO_DOCS_TAG } from '@describe-me/core/types';
|
|
8
|
+
/** Which tests of the recorded files become stories: every one, or only those tagged `docs`. */
|
|
9
|
+
export type StoriesMode = 'all' | 'tagged';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `stories` option and its two tags. Kept apart from `runtime-options.ts`,
|
|
3
|
+
* because the public types import this file and must not carry that file's
|
|
4
|
+
* augmentation of Vitest's `ProvidedContext`. The test runtime imports it, so
|
|
5
|
+
* no Node imports. The tags live in core, which the viewer reads too.
|
|
6
|
+
*/
|
|
7
|
+
export { DOCS_TAG, NO_DOCS_TAG } from '@describe-me/core/types';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type StoriesMode } from './stories-mode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decides from a test's tags, its own and those it inherits from its suites
|
|
4
|
+
* and its file, whether it becomes a story. `no-docs` wins over `docs`. The
|
|
5
|
+
* test runtime imports this file, so no Node imports.
|
|
6
|
+
*/
|
|
7
|
+
export declare function tagFilter(stories: StoriesMode): (tags: readonly string[]) => boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DOCS_TAG, NO_DOCS_TAG } from './stories-mode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decides from a test's tags, its own and those it inherits from its suites
|
|
4
|
+
* and its file, whether it becomes a story. `no-docs` wins over `docs`. The
|
|
5
|
+
* test runtime imports this file, so no Node imports.
|
|
6
|
+
*/
|
|
7
|
+
export function tagFilter(stories) {
|
|
8
|
+
return (tags) => {
|
|
9
|
+
if (tags.includes(NO_DOCS_TAG)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return stories === 'all' || tags.includes(DOCS_TAG);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
/** The plugin's `stories`, checked before any test runs. */
|
|
3
|
+
export function validateStories(value) {
|
|
4
|
+
if (value === undefined) {
|
|
5
|
+
return 'all';
|
|
6
|
+
}
|
|
7
|
+
if (value === 'all' || value === 'tagged') {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`describe-me: stories expects 'all' or 'tagged', got ${inspect(value)}`);
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@describe-me/vitest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Living component documentation from your Vitest tests: the Vitest plugin, setup files and reporter of describe-me",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vitest",
|
|
@@ -71,13 +71,13 @@
|
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"picomatch": "^4.0.3",
|
|
73
73
|
"std-env": "^4.0.0",
|
|
74
|
-
"@describe-me/core": "^0.
|
|
74
|
+
"@describe-me/core": "^0.7.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"vitest": "^4.0.0 || ^5.0.0",
|
|
78
78
|
"typescript": "^5.0.0 || ^6.0.0",
|
|
79
79
|
"@testing-library/user-event": "^14.0.0",
|
|
80
|
-
"describe-me": "^0.
|
|
80
|
+
"describe-me": "^0.7.0"
|
|
81
81
|
},
|
|
82
82
|
"peerDependenciesMeta": {
|
|
83
83
|
"typescript": {
|