@mpcore/vitest 0.2.7 → 1.1.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/README.md +32 -4
- package/dist/config-1SyUfssv.mjs +77 -0
- package/dist/config-D3NKpUBr.d.mts +43 -0
- package/dist/config.d.mts +2 -0
- package/dist/config.mjs +1 -0
- package/dist/index.d.mts +6 -16
- package/dist/index.mjs +9 -14
- package/dist/setup.mjs +2 -1
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -1,16 +1,44 @@
|
|
|
1
1
|
# @mpcore/vitest
|
|
2
2
|
|
|
3
|
-
Vitest 5 integration for `@mpcore/test`.
|
|
3
|
+
Vitest 5 integration for `@mpcore/test`. Import the configuration plugin from `@mpcore/vitest/config`; this entry does not load the test worker's `expect` or `test`. Test files import fixtures from `@mpcore/vitest`, using the same Vitest peer instance as setup and matchers.
|
|
4
|
+
|
|
5
|
+
Configure an explicit artifact factory. The integration does not depend on a particular compiler; for weapp-vite:
|
|
4
6
|
|
|
5
7
|
```ts
|
|
6
|
-
|
|
8
|
+
// vitest.config.ts
|
|
9
|
+
import { mpcoreTest } from '@mpcore/vitest/config'
|
|
10
|
+
import { buildWeappViteTestArtifact, watchWeappViteTestArtifact } from '@mpcore/weapp-vite'
|
|
11
|
+
import { defineConfig } from 'vitest/config'
|
|
7
12
|
|
|
8
|
-
const
|
|
9
|
-
|
|
13
|
+
const options = { cwd: import.meta.dirname, platform: 'weapp' as const }
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
plugins: [mpcoreTest({
|
|
17
|
+
artifact: {
|
|
18
|
+
build: () => buildWeappViteTestArtifact(options),
|
|
19
|
+
watch: callbacks => watchWeappViteTestArtifact({ ...options, ...callbacks }),
|
|
20
|
+
},
|
|
21
|
+
})],
|
|
10
22
|
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run mode calls `build` once per project. Watch mode calls `watch` instead; the returned watcher owns its initial build and exposes that artifact. After a successful update, the plugin provides the new artifact and asks the current runner to rerun only that project's specifications. Build and scheduling errors are recorded and printed by the runner. Closing waits for startup, pending updates and the owned watcher; it does not start or stop another test process.
|
|
26
|
+
|
|
27
|
+
Vite+ uses the same plugin and `vp test`; import `defineConfig` from `vite-plus` in that host's configuration and keep its Vitest peer aligned with the runner.
|
|
28
|
+
|
|
29
|
+
Each test receives an isolated mini-program runtime from the injected artifact:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createMpcoreTest } from '@mpcore/vitest'
|
|
33
|
+
|
|
34
|
+
const test = createMpcoreTest()
|
|
11
35
|
|
|
12
36
|
test('renders a page', async ({ mpcore, expect }) => {
|
|
13
37
|
const result = await mpcore.renderPage('/pages/index/index')
|
|
14
38
|
expect(result.screen.getByText('ready')).toBeInTheMiniProgram()
|
|
15
39
|
})
|
|
16
40
|
```
|
|
41
|
+
|
|
42
|
+
`createMpcoreTest({ artifact: { projectPath: process.cwd() } })` still accepts an existing artifact directly. `createVitestProject()` also uses the configured artifact when omitted, and closes its runtime with the current test. Optional host mocks and `failOnConsoleError` can be passed without replacing the injected artifact. Calling either helper without a configured or explicit artifact reports an actionable error.
|
|
43
|
+
|
|
44
|
+
Setup also registers the semantic tree snapshot serializer from `@mpcore/test`. Use `expect(result.screen).toMatchSnapshot()` or snapshot an individual node. Snapshots preserve public tree and event-binding semantics while removing runtime IDs, style scope markers and generated handler names. Full diagnostic snapshots returned by `page.snapshot()` remain unchanged; use `toMiniProgramSnapshot()` explicitly to serialize one.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
//#region src/artifact.ts
|
|
3
|
+
const MPCORE_ARTIFACT_KEY = "mpcoreArtifact";
|
|
4
|
+
/** 每次 configureVitest 调用只持有当前 project 的 watcher 和重跑队列。 */
|
|
5
|
+
async function configureArtifact(factory, { project, vitest }) {
|
|
6
|
+
let closed = false;
|
|
7
|
+
let ready = false;
|
|
8
|
+
let earlyArtifact;
|
|
9
|
+
let watcher;
|
|
10
|
+
let pending = Promise.resolve();
|
|
11
|
+
let starting = Promise.resolve();
|
|
12
|
+
let closing;
|
|
13
|
+
const onError = (error) => {
|
|
14
|
+
if (!closed) {
|
|
15
|
+
vitest.state.catchError(error, "Mpcore Artifact Error");
|
|
16
|
+
vitest.logger.printError(error);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const onRebuilt = (artifact) => {
|
|
20
|
+
if (closed) return Promise.resolve();
|
|
21
|
+
if (!ready) {
|
|
22
|
+
earlyArtifact = artifact;
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
pending = pending.then(async () => {
|
|
26
|
+
if (closed) return;
|
|
27
|
+
project.provide(MPCORE_ARTIFACT_KEY, artifact);
|
|
28
|
+
const specifications = await vitest.globTestSpecifications();
|
|
29
|
+
if (!closed) await vitest.rerunTestSpecifications(specifications.filter((specification) => specification.project === project));
|
|
30
|
+
}).catch(onError);
|
|
31
|
+
return pending;
|
|
32
|
+
};
|
|
33
|
+
vitest.onClose(() => {
|
|
34
|
+
return closing ??= (async () => {
|
|
35
|
+
closed = true;
|
|
36
|
+
await starting.catch(() => {});
|
|
37
|
+
const ownedWatcher = watcher;
|
|
38
|
+
watcher = void 0;
|
|
39
|
+
const errors = (await Promise.allSettled([Promise.resolve().then(() => ownedWatcher?.close()), pending])).filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
40
|
+
if (errors.length) throw new AggregateError(errors, "Mpcore artifact cleanup failed");
|
|
41
|
+
})();
|
|
42
|
+
});
|
|
43
|
+
starting = (async () => {
|
|
44
|
+
if (closed) return;
|
|
45
|
+
let artifact;
|
|
46
|
+
if (vitest.config.watch && factory.watch) {
|
|
47
|
+
watcher = await factory.watch({
|
|
48
|
+
onRebuilt,
|
|
49
|
+
onError
|
|
50
|
+
});
|
|
51
|
+
artifact = watcher.artifact;
|
|
52
|
+
} else artifact = await factory.build();
|
|
53
|
+
if (!closed) {
|
|
54
|
+
project.provide(MPCORE_ARTIFACT_KEY, earlyArtifact ?? artifact);
|
|
55
|
+
ready = true;
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
await starting;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/config.ts
|
|
62
|
+
/** 配置入口仅依赖 runner 的公开类型,不提前加载测试线程的 expect/test。 */
|
|
63
|
+
function mpcoreTest(options = {}) {
|
|
64
|
+
const setupFile = fileURLToPath(new URL("./setup.mjs", import.meta.url));
|
|
65
|
+
return {
|
|
66
|
+
name: "mpcore:vitest",
|
|
67
|
+
config(config) {
|
|
68
|
+
const existing = config.test?.setupFiles;
|
|
69
|
+
return { test: { setupFiles: (typeof existing === "string" ? [existing] : existing ?? []).includes(setupFile) ? [] : [setupFile] } };
|
|
70
|
+
},
|
|
71
|
+
async configureVitest(context) {
|
|
72
|
+
if (options.artifact) await configureArtifact(options.artifact, context);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { MPCORE_ARTIFACT_KEY as n, mpcoreTest as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MiniProgramArtifact } from "@mpcore/test";
|
|
2
|
+
import { VitestPluginContext } from "vitest/node";
|
|
3
|
+
//#region src/artifact.d.ts
|
|
4
|
+
interface MpcoreArtifactWatcher {
|
|
5
|
+
artifact: MiniProgramArtifact;
|
|
6
|
+
close: () => void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
interface MpcoreArtifactWatchCallbacks {
|
|
9
|
+
onRebuilt: (artifact: MiniProgramArtifact) => Promise<void>;
|
|
10
|
+
onError: (error: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
interface MpcoreArtifactFactory {
|
|
13
|
+
build: () => MiniProgramArtifact | Promise<MiniProgramArtifact>;
|
|
14
|
+
/** watcher 负责首次构建并返回初始产物,避免 run 与 watch 重复构建。 */
|
|
15
|
+
watch?: (callbacks: MpcoreArtifactWatchCallbacks) => MpcoreArtifactWatcher | Promise<MpcoreArtifactWatcher>;
|
|
16
|
+
}
|
|
17
|
+
declare module 'vitest' {
|
|
18
|
+
interface ProvidedContext {
|
|
19
|
+
mpcoreArtifact: MiniProgramArtifact;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/config.d.ts
|
|
24
|
+
interface MpcoreVitestOptions {
|
|
25
|
+
artifact?: MpcoreArtifactFactory;
|
|
26
|
+
}
|
|
27
|
+
interface MpcoreVitestPlugin {
|
|
28
|
+
config: (config: {
|
|
29
|
+
test?: {
|
|
30
|
+
setupFiles?: string | string[];
|
|
31
|
+
};
|
|
32
|
+
}) => {
|
|
33
|
+
test: {
|
|
34
|
+
setupFiles: string[];
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
configureVitest: (context: VitestPluginContext) => Promise<void>;
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
/** 配置入口仅依赖 runner 的公开类型,不提前加载测试线程的 expect/test。 */
|
|
41
|
+
declare function mpcoreTest(options?: MpcoreVitestOptions): MpcoreVitestPlugin;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { MpcoreArtifactWatchCallbacks as a, MpcoreArtifactFactory as i, MpcoreVitestPlugin as n, MpcoreArtifactWatcher as o, mpcoreTest as r, MpcoreVitestOptions as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as MpcoreArtifactWatchCallbacks, i as MpcoreArtifactFactory, n as MpcoreVitestPlugin, o as MpcoreArtifactWatcher, r as mpcoreTest, t as MpcoreVitestOptions } from "./config-D3NKpUBr.mjs";
|
|
2
|
+
export { type MpcoreArtifactFactory, type MpcoreArtifactWatchCallbacks, type MpcoreArtifactWatcher, MpcoreVitestOptions, MpcoreVitestPlugin, mpcoreTest };
|
package/dist/config.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as mpcoreTest } from "./config-1SyUfssv.mjs";
|
package/dist/index.d.mts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
+
import { a as MpcoreArtifactWatchCallbacks, i as MpcoreArtifactFactory, n as MpcoreVitestPlugin, o as MpcoreArtifactWatcher, r as mpcoreTest, t as MpcoreVitestOptions } from "./config-D3NKpUBr.mjs";
|
|
1
2
|
import { CreateTestProjectOptions, MiniProgramEmissionSource, MiniProgramNode, MiniProgramTestProject } from "@mpcore/test";
|
|
2
3
|
export * from "@mpcore/test";
|
|
3
4
|
//#region src/index.d.ts
|
|
4
5
|
export interface MpcoreVitestFixture {
|
|
5
6
|
mpcore: MiniProgramTestProject;
|
|
6
7
|
}
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
test?: {
|
|
10
|
-
setupFiles?: string | string[];
|
|
11
|
-
};
|
|
12
|
-
}) => {
|
|
13
|
-
test: {
|
|
14
|
-
setupFiles: string[];
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
name: string;
|
|
18
|
-
}
|
|
19
|
-
export declare function mpcoreTest(): MpcoreVitestPlugin;
|
|
20
|
-
export declare function createMpcoreTest(options: CreateTestProjectOptions): import("vitest").TestAPI<{
|
|
8
|
+
export type MpcoreTestProjectOptions = Omit<CreateTestProjectOptions, 'artifact'> & Partial<Pick<CreateTestProjectOptions, 'artifact'>>;
|
|
9
|
+
export declare function createMpcoreTest(options?: MpcoreTestProjectOptions): import("vitest").TestAPI<{
|
|
21
10
|
mpcore: MiniProgramTestProject;
|
|
22
11
|
}>;
|
|
23
|
-
export declare function createVitestProject(options
|
|
12
|
+
export declare function createVitestProject(options?: MpcoreTestProjectOptions): MiniProgramTestProject;
|
|
24
13
|
export declare function registerMpcoreMatchers(): void;
|
|
25
14
|
declare module 'vitest' {
|
|
26
15
|
interface Matchers<R, T> {
|
|
@@ -31,4 +20,5 @@ declare module 'vitest' {
|
|
|
31
20
|
toHaveTextContent: T extends MiniProgramNode ? (value: string | RegExp) => R : never;
|
|
32
21
|
}
|
|
33
22
|
}
|
|
34
|
-
//#endregion
|
|
23
|
+
//#endregion
|
|
24
|
+
export { type MpcoreArtifactFactory, type MpcoreArtifactWatchCallbacks, type MpcoreArtifactWatcher, type MpcoreVitestOptions, type MpcoreVitestPlugin, mpcoreTest };
|
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as mpcoreTest } from "./config-1SyUfssv.mjs";
|
|
2
2
|
import { createTestProject, mpcoreMatchers } from "@mpcore/test";
|
|
3
|
-
import { expect, onTestFinished, test } from "vitest";
|
|
3
|
+
import { expect, inject, onTestFinished, test } from "vitest";
|
|
4
4
|
export * from "@mpcore/test";
|
|
5
5
|
//#region src/index.ts
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
function mpcoreTest() {
|
|
11
|
-
const setupFile = fileURLToPath(new URL("./setup.mjs", import.meta.url));
|
|
6
|
+
function projectOptions(options = {}) {
|
|
7
|
+
const artifact = options.artifact ?? inject("mpcoreArtifact");
|
|
8
|
+
if (!artifact) throw new Error("No mpcore artifact was provided. Configure mpcoreTest({ artifact }) from @mpcore/vitest/config or pass an explicit artifact.");
|
|
12
9
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return { test: { setupFiles: [...toList(config.test?.setupFiles), setupFile] } };
|
|
16
|
-
}
|
|
10
|
+
...options,
|
|
11
|
+
artifact
|
|
17
12
|
};
|
|
18
13
|
}
|
|
19
14
|
function createMpcoreTest(options) {
|
|
20
15
|
return test.extend({ mpcore: async ({}, use) => {
|
|
21
|
-
const project = createTestProject(options);
|
|
16
|
+
const project = createTestProject(projectOptions(options));
|
|
22
17
|
try {
|
|
23
18
|
await use(project);
|
|
24
19
|
} finally {
|
|
@@ -27,7 +22,7 @@ function createMpcoreTest(options) {
|
|
|
27
22
|
} });
|
|
28
23
|
}
|
|
29
24
|
function createVitestProject(options) {
|
|
30
|
-
const project = createTestProject(options);
|
|
25
|
+
const project = createTestProject(projectOptions(options));
|
|
31
26
|
onTestFinished(async () => {
|
|
32
27
|
await project.close();
|
|
33
28
|
});
|
package/dist/setup.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mpcoreMatchers } from "@mpcore/test";
|
|
1
|
+
import { mpcoreMatchers, mpcoreSnapshotSerializer } from "@mpcore/test";
|
|
2
2
|
import { expect } from "vitest";
|
|
3
3
|
//#region src/setup.ts
|
|
4
4
|
expect.extend(mpcoreMatchers);
|
|
5
|
+
expect.addSnapshotSerializer(mpcoreSnapshotSerializer);
|
|
5
6
|
//#endregion
|
|
6
7
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpcore/vitest",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "Vitest integration for @mpcore/test",
|
|
6
6
|
"author": "ice breaker <1324318532@qq.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./setup": {
|
|
32
32
|
"types": "./dist/setup.d.mts",
|
|
33
33
|
"import": "./dist/setup.mjs"
|
|
34
|
+
},
|
|
35
|
+
"./config": {
|
|
36
|
+
"types": "./dist/config.d.mts",
|
|
37
|
+
"import": "./dist/config.mjs"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"main": "./dist/index.mjs",
|
|
@@ -41,26 +45,33 @@
|
|
|
41
45
|
"dist"
|
|
42
46
|
],
|
|
43
47
|
"engines": {
|
|
44
|
-
"node": "^
|
|
48
|
+
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
|
|
45
49
|
},
|
|
46
50
|
"peerDependencies": {
|
|
47
51
|
"vitest": ">=5.0.0"
|
|
48
52
|
},
|
|
49
53
|
"dependencies": {
|
|
50
|
-
"@mpcore/test": "
|
|
54
|
+
"@mpcore/test": "1.1.0"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
|
-
"vitest": "~5.0.
|
|
57
|
+
"vitest": "~5.0.5"
|
|
54
58
|
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"access": "public",
|
|
57
61
|
"registry": "https://registry.npmjs.org"
|
|
58
62
|
},
|
|
63
|
+
"tsd": {
|
|
64
|
+
"compilerOptions": {
|
|
65
|
+
"module": "NodeNext",
|
|
66
|
+
"moduleResolution": "NodeNext"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
59
69
|
"scripts": {
|
|
60
70
|
"build": "tsdown",
|
|
61
71
|
"dev": "tsdown -w",
|
|
62
72
|
"test": "vitest run",
|
|
63
|
-
"test:types": "pnpm build &&
|
|
73
|
+
"test:types": "pnpm build && pnpm run test:types:check",
|
|
74
|
+
"test:types:check": "tsd --files \"test-d/**/*.test-d.ts\"",
|
|
64
75
|
"typecheck": "tsc --noEmit"
|
|
65
76
|
}
|
|
66
77
|
}
|