@testforgejs/vue-test-core 1.0.0-beta.0 → 1.0.0-beta.1
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 +92 -27
- package/dist/index.cjs +114 -7
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +113 -7
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
# @testforgejs/vue-test-core
|
|
2
2
|
|
|
3
|
-
> Core runtime
|
|
3
|
+
> Core runtime and declarative testing infrastructure for Vue 3.
|
|
4
4
|
|
|
5
5
|
`@testforgejs/vue-test-core` is the core runtime of **TestForge**. It provides the framework for creating reusable, type-safe component test factories and coordinating managed Vue ecosystem plugins.
|
|
6
6
|
|
|
7
7
|
The core package provides:
|
|
8
8
|
|
|
9
|
-
- type-safe `testComponentFactory` instances
|
|
10
|
-
- plugin registration and lifecycle management
|
|
11
|
-
-
|
|
12
|
-
- reusable component test factories
|
|
13
|
-
- a hierarchical configuration
|
|
9
|
+
- type-safe `testComponentFactory` instances;
|
|
10
|
+
- plugin registration and lifecycle management;
|
|
11
|
+
- preset-based runtime configuration;
|
|
12
|
+
- reusable component test factories;
|
|
13
|
+
- a hierarchical configuration resolution pipeline:
|
|
14
|
+
**Preset → Factory → Test → Extra Options**.
|
|
14
15
|
|
|
15
|
-
The core package itself does **not** include or configure
|
|
16
|
+
The core package itself does **not** include or configure Vue ecosystem plugins. Plugin integrations are provided by separate TestForge packages and become available to the framework through presets.
|
|
17
|
+
|
|
18
|
+
> Core defines the runtime. Plugins define integrations. Base presets define shared environments. Runner-specific recommended presets add runner-specific behavior. The host application provides Vue ecosystem dependencies.
|
|
16
19
|
|
|
17
20
|
## Installation
|
|
18
21
|
|
|
@@ -36,35 +39,83 @@ npm install -D @testforgejs/vue-test-core
|
|
|
36
39
|
yarn add -D @testforgejs/vue-test-core
|
|
37
40
|
```
|
|
38
41
|
|
|
42
|
+
> [!NOTE]
|
|
43
|
+
> TestForge requires **[Vue](https://vuejs.org/) 3.3.0 or higher**.
|
|
44
|
+
>
|
|
45
|
+
> `vue` and `@vue/test-utils` are peer dependencies of `@testforgejs/vue-test-core`. Make sure they are installed in your project.
|
|
46
|
+
|
|
47
|
+
For most projects, you will also want to install a preset package that defines the managed Vue plugins available to your tests.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
39
51
|
## Presets
|
|
40
52
|
|
|
41
|
-
A **preset** defines
|
|
53
|
+
A **preset** defines a TestForge runtime environment.
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
A preset controls:
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
- which managed plugins are available;
|
|
58
|
+
- which plugins are enabled by default;
|
|
59
|
+
- the default configuration for those plugins.
|
|
60
|
+
|
|
61
|
+
The preset system keeps the TestForge core independent from specific Vue ecosystem integrations. The core runtime does not need built-in knowledge of Pinia, Vue Router, Vue I18n, Vuetify, PrimeVue, or other integrations.
|
|
62
|
+
|
|
63
|
+
### Official preset packages
|
|
64
|
+
|
|
65
|
+
TestForge provides a layered preset architecture:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
@testforgejs/vue-test-preset-base
|
|
69
|
+
│
|
|
70
|
+
├── @testforgejs/vue-test-preset-recommended
|
|
71
|
+
│ Vitest defaults
|
|
72
|
+
│
|
|
73
|
+
└── @testforgejs/vue-test-preset-recommended-jest
|
|
74
|
+
Jest defaults
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`@testforgejs/vue-test-preset-base` provides shared Vue plugin configuration.
|
|
78
|
+
|
|
79
|
+
Runner-specific recommended presets build on top of the base presets and add configuration required by a particular test runner.
|
|
80
|
+
|
|
81
|
+
For example, the recommended Vitest preset provides `vi.fn` as the Pinia `createSpy` implementation.
|
|
82
|
+
|
|
83
|
+
### Recommended presets
|
|
84
|
+
|
|
85
|
+
For Vitest:
|
|
46
86
|
|
|
47
87
|
```bash
|
|
48
88
|
pnpm add -D @testforgejs/vue-test-preset-recommended
|
|
49
89
|
```
|
|
50
90
|
|
|
51
|
-
|
|
91
|
+
For Jest:
|
|
52
92
|
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
93
|
+
```bash
|
|
94
|
+
pnpm add -D @testforgejs/vue-test-preset-recommended-jest
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The recommended presets include configurations for commonly used managed integrations such as:
|
|
56
98
|
|
|
57
|
-
|
|
99
|
+
- Pinia;
|
|
100
|
+
- Vue I18n;
|
|
101
|
+
- Vue Router.
|
|
102
|
+
|
|
103
|
+
The recommended preset packages are optional. You can also use the base presets directly or create and compose your own presets.
|
|
58
104
|
|
|
59
105
|
> [!TIP]
|
|
60
|
-
>
|
|
106
|
+
> If you are getting started with TestForge, use the recommended preset that matches your test runner.
|
|
107
|
+
|
|
108
|
+
See the [Getting Started Guide](https://github.com/testforgejs/testforge/blob/main/docs/getting-started.md) for the recommended setup and the [Preset Authoring Guide](https://github.com/testforgejs/testforge/blob/main/docs/preset-authoring-guide.md) for creating and composing custom presets.
|
|
61
109
|
|
|
62
|
-
|
|
110
|
+
---
|
|
63
111
|
|
|
64
112
|
## Quick Usage
|
|
65
113
|
|
|
114
|
+
The following example uses Vitest and the recommended Vitest preset.
|
|
115
|
+
|
|
66
116
|
```typescript
|
|
67
|
-
// tests/setup.ts
|
|
117
|
+
// tests/setup.ts
|
|
118
|
+
|
|
68
119
|
import { createTestFramework } from "@testforgejs/vue-test-core";
|
|
69
120
|
import { presets } from "@testforgejs/vue-test-preset-recommended";
|
|
70
121
|
|
|
@@ -78,41 +129,55 @@ export { testComponentFactory };
|
|
|
78
129
|
The resulting `testComponentFactory` can then be imported and reused throughout your component tests.
|
|
79
130
|
|
|
80
131
|
```typescript
|
|
81
|
-
// MyComponent.spec.ts
|
|
132
|
+
// MyComponent.spec.ts
|
|
133
|
+
|
|
134
|
+
import { describe, expect, it } from "vitest";
|
|
135
|
+
|
|
82
136
|
import { testComponentFactory } from "@/tests/setup";
|
|
83
137
|
import MyComponent from "@/components/MyComponent.vue";
|
|
84
138
|
|
|
85
139
|
const factory = testComponentFactory(MyComponent);
|
|
86
140
|
|
|
87
|
-
|
|
88
|
-
|
|
141
|
+
describe("MyComponent.vue", () => {
|
|
142
|
+
it("renders correctly", () => {
|
|
143
|
+
const wrapper = factory();
|
|
89
144
|
|
|
90
|
-
|
|
145
|
+
expect(wrapper.exists()).toBe(true);
|
|
146
|
+
});
|
|
91
147
|
});
|
|
92
148
|
```
|
|
93
149
|
|
|
150
|
+
For Jest projects, use `@testforgejs/vue-test-preset-recommended-jest` instead.
|
|
151
|
+
|
|
94
152
|
👉 For a complete walkthrough, continue with the [Getting Started Guide](https://github.com/testforgejs/testforge/blob/main/docs/getting-started.md).
|
|
95
153
|
|
|
96
154
|
## Documentation
|
|
97
155
|
|
|
98
156
|
- [Getting Started Guide](https://github.com/testforgejs/testforge/blob/main/docs/getting-started.md) — Set up TestForge in a project and create reusable component test factories.
|
|
99
|
-
- [Configuration & Advanced Usage](https://github.com/testforgejs/testforge/blob/main/docs/configuration.md) — Learn about configuration layers, merge strategies, execution
|
|
157
|
+
- [Configuration & Advanced Usage](https://github.com/testforgejs/testforge/blob/main/docs/configuration.md) — Learn about configuration layers, merge strategies, execution controls, and plugin lifecycle behavior.
|
|
158
|
+
- [Preset Authoring Guide](https://github.com/testforgejs/testforge/blob/main/docs/preset-authoring-guide.md) — Create and compose custom presets.
|
|
100
159
|
- [Plugin Authoring Guide](https://github.com/testforgejs/testforge/blob/main/docs/plugin-authoring-guide.md) — Create custom TestForge plugins.
|
|
101
|
-
- [Preset Authoring Guide](https://github.com/testforgejs/testforge/blob/main/docs/preset-authoring-guide.md) — Create custom presets for your project or organization.
|
|
102
160
|
|
|
103
161
|
## Project
|
|
104
162
|
|
|
105
163
|
This package is part of the **TestForge** monorepo.
|
|
106
164
|
|
|
107
|
-
- **[TestForge Project Overview](https://github.com/testforgejs/testforge#readme)** —
|
|
165
|
+
- **[TestForge Project Overview](https://github.com/testforgejs/testforge#readme)** — Project overview, package ecosystem, roadmap, and repository information.
|
|
108
166
|
- **Repository:** https://github.com/testforgejs/testforge
|
|
109
167
|
|
|
110
|
-
## Related
|
|
168
|
+
## Related Packages
|
|
111
169
|
|
|
170
|
+
### Presets
|
|
171
|
+
|
|
172
|
+
- `@testforgejs/vue-test-preset-base`
|
|
112
173
|
- `@testforgejs/vue-test-preset-recommended`
|
|
113
|
-
- `@testforgejs/vue-test-
|
|
174
|
+
- `@testforgejs/vue-test-preset-recommended-jest`
|
|
175
|
+
|
|
176
|
+
### Managed Plugins
|
|
177
|
+
|
|
114
178
|
- `@testforgejs/vue-test-plugin-pinia`
|
|
115
179
|
- `@testforgejs/vue-test-plugin-i18n`
|
|
180
|
+
- `@testforgejs/vue-test-plugin-router`
|
|
116
181
|
- `@testforgejs/vue-test-plugin-vuetify`
|
|
117
182
|
- `@testforgejs/vue-test-plugin-primevue`
|
|
118
183
|
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
createPluginInstance: () => createPluginInstance,
|
|
26
26
|
createTestFramework: () => createTestFramework,
|
|
27
27
|
createVuePlugin: () => createVuePlugin,
|
|
28
|
+
extendPreset: () => extendPreset,
|
|
28
29
|
validatePreset: () => validatePreset,
|
|
29
30
|
validatePresets: () => validatePresets
|
|
30
31
|
});
|
|
@@ -55,7 +56,7 @@ var FRAMEWORK_NAME = "TestForge";
|
|
|
55
56
|
var ERROR_PREFIX = `[${FRAMEWORK_NAME}]`;
|
|
56
57
|
var DEFAULT_PRESET_NAME = "default";
|
|
57
58
|
|
|
58
|
-
// src/validators/validatePreset.ts
|
|
59
|
+
// src/presets/validators/validatePreset.ts
|
|
59
60
|
function validatePreset(name, preset) {
|
|
60
61
|
if (!preset) {
|
|
61
62
|
throw new Error(`${ERROR_PREFIX} Preset "${name}" is null or undefined.`);
|
|
@@ -91,17 +92,16 @@ function validatePreset(name, preset) {
|
|
|
91
92
|
);
|
|
92
93
|
}
|
|
93
94
|
const value = preset.defaults[key];
|
|
94
|
-
|
|
95
|
-
if (!isObject) {
|
|
95
|
+
if (typeof value !== "function") {
|
|
96
96
|
throw new Error(
|
|
97
|
-
`${ERROR_PREFIX} Invalid default configuration for plugin "${key}" in preset "${name}". Expected
|
|
97
|
+
`${ERROR_PREFIX} Invalid default configuration for plugin "${key}" in preset "${name}". Expected a plugin options factory function, but received ${typeof value}.`
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// src/
|
|
104
|
+
// src/presets/getActivePreset.ts
|
|
105
105
|
function getActivePreset(presets = {}, extraOptions) {
|
|
106
106
|
const requestedPresetName = extraOptions?.preset?.trim();
|
|
107
107
|
const activeName = requestedPresetName || DEFAULT_PRESET_NAME;
|
|
@@ -416,12 +416,29 @@ var withPluginsManifest = (ctx) => {
|
|
|
416
416
|
});
|
|
417
417
|
};
|
|
418
418
|
|
|
419
|
+
// src/presets/utils/buildPluginOptions.ts
|
|
420
|
+
function buildPluginOptions(pluginName, factory) {
|
|
421
|
+
const options = factory();
|
|
422
|
+
if (!isPlainObject(options)) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`${ERROR_PREFIX} Plugin options factory for "${pluginName}" must return a plain object.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
return options;
|
|
428
|
+
}
|
|
429
|
+
|
|
419
430
|
// src/pipeline/middleware/transformers/withPreset.ts
|
|
420
431
|
var withPreset = (ctx) => {
|
|
421
432
|
const { preset } = ctx;
|
|
422
433
|
if (!preset?.defaults) return ctx;
|
|
434
|
+
const pluginDefaultsState = Object.fromEntries(
|
|
435
|
+
Object.entries(preset.defaults).map(([pluginName, factory]) => [
|
|
436
|
+
pluginName,
|
|
437
|
+
buildPluginOptions(pluginName, factory)
|
|
438
|
+
])
|
|
439
|
+
);
|
|
423
440
|
return patchResultState(ctx, {
|
|
424
|
-
pluginDefaultsState
|
|
441
|
+
pluginDefaultsState
|
|
425
442
|
});
|
|
426
443
|
};
|
|
427
444
|
|
|
@@ -625,7 +642,7 @@ function mountWithPlugins(component, ctx, overrides = {}, runtimeOptions = {
|
|
|
625
642
|
});
|
|
626
643
|
}
|
|
627
644
|
|
|
628
|
-
// src/validators/validatePresets.ts
|
|
645
|
+
// src/presets/validators/validatePresets.ts
|
|
629
646
|
function validatePresets(presets = {}) {
|
|
630
647
|
if (!isPlainObject(presets)) {
|
|
631
648
|
throw new Error(`${ERROR_PREFIX} Presets must be a plain object.`);
|
|
@@ -850,6 +867,95 @@ function captureInstance() {
|
|
|
850
867
|
};
|
|
851
868
|
}
|
|
852
869
|
|
|
870
|
+
// src/presets/validators/validatePluginDefaults.ts
|
|
871
|
+
function validatePluginDefaults(pluginName, value) {
|
|
872
|
+
if (typeof value !== "function") {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`${ERROR_PREFIX} Invalid defaults for plugin "${pluginName}". Preset defaults must be provided as a plugin options factory function.`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/presets/validators/validatePresetExtension.ts
|
|
880
|
+
function validatePresetExtension(basePreset, extension) {
|
|
881
|
+
if (!basePreset) {
|
|
882
|
+
throw new Error(`${ERROR_PREFIX} Cannot extend a null or undefined preset.`);
|
|
883
|
+
}
|
|
884
|
+
if (!extension) {
|
|
885
|
+
throw new Error(`${ERROR_PREFIX} Preset extension is null or undefined.`);
|
|
886
|
+
}
|
|
887
|
+
const basePluginNames = new Set(basePreset.manifest.map((entry) => entry.module.getName()));
|
|
888
|
+
const extensionManifest = extension.manifest ?? [];
|
|
889
|
+
const extensionManifestPlugins = /* @__PURE__ */ new Set();
|
|
890
|
+
for (const entry of extensionManifest) {
|
|
891
|
+
const pluginName = entry.module.getName();
|
|
892
|
+
if (extensionManifestPlugins.has(pluginName)) {
|
|
893
|
+
throw new Error(
|
|
894
|
+
`${ERROR_PREFIX} Duplicate plugin "${pluginName}" in preset extension manifest.`
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
extensionManifestPlugins.add(pluginName);
|
|
898
|
+
const isNewPlugin = !basePluginNames.has(pluginName);
|
|
899
|
+
if (isNewPlugin) {
|
|
900
|
+
if (entry.enabled === void 0) {
|
|
901
|
+
throw new Error(
|
|
902
|
+
`${ERROR_PREFIX} Cannot add plugin "${pluginName}" to an extended preset without specifying "enabled".`
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
const pluginDefaults = extension.defaults?.[pluginName];
|
|
906
|
+
if (pluginDefaults === void 0) {
|
|
907
|
+
throw new Error(
|
|
908
|
+
`${ERROR_PREFIX} Cannot add plugin "${pluginName}" to an extended preset without defining its defaults.`
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (extension.defaults) {
|
|
914
|
+
for (const [pluginName, pluginDefaults] of Object.entries(extension.defaults)) {
|
|
915
|
+
validatePluginDefaults(pluginName, pluginDefaults);
|
|
916
|
+
const isKnownPlugin = basePluginNames.has(pluginName) || extensionManifestPlugins.has(pluginName);
|
|
917
|
+
if (!isKnownPlugin) {
|
|
918
|
+
throw new Error(
|
|
919
|
+
`${ERROR_PREFIX} Cannot define defaults for unknown plugin "${pluginName}" in a preset extension. The plugin must be declared in the manifest.`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/presets/extendPreset.ts
|
|
927
|
+
function extendPreset(basePreset, extension) {
|
|
928
|
+
validatePresetExtension(basePreset, extension);
|
|
929
|
+
const manifest = basePreset.manifest.map((entry) => ({ ...entry }));
|
|
930
|
+
const defaults = { ...basePreset.defaults };
|
|
931
|
+
const manifestByName = new Map(manifest.map((entry) => [entry.module.getName(), entry]));
|
|
932
|
+
const extensionManifest = extension.manifest ?? [];
|
|
933
|
+
for (const entry of extensionManifest) {
|
|
934
|
+
const pluginName = entry.module.getName();
|
|
935
|
+
const existingEntry = manifestByName.get(pluginName);
|
|
936
|
+
if (existingEntry) {
|
|
937
|
+
if (entry.enabled !== void 0) {
|
|
938
|
+
existingEntry.enabled = entry.enabled;
|
|
939
|
+
}
|
|
940
|
+
} else {
|
|
941
|
+
manifest.push({
|
|
942
|
+
module: entry.module,
|
|
943
|
+
enabled: entry.enabled
|
|
944
|
+
});
|
|
945
|
+
manifestByName.set(pluginName, manifest[manifest.length - 1]);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (extension.defaults) {
|
|
949
|
+
for (const [pluginName, pluginDefaults] of Object.entries(extension.defaults)) {
|
|
950
|
+
defaults[pluginName] = pluginDefaults;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return {
|
|
954
|
+
manifest,
|
|
955
|
+
defaults
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
853
959
|
// src/index.ts
|
|
854
960
|
var Types = {};
|
|
855
961
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -859,6 +965,7 @@ var Types = {};
|
|
|
859
965
|
createPluginInstance,
|
|
860
966
|
createTestFramework,
|
|
861
967
|
createVuePlugin,
|
|
968
|
+
extendPreset,
|
|
862
969
|
validatePreset,
|
|
863
970
|
validatePresets
|
|
864
971
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -29,7 +29,8 @@ type SupportedPluginsMap = Record<PluginName, SupportedPluginState>;
|
|
|
29
29
|
type RuntimePluginConfig = Record<string, any>;
|
|
30
30
|
type RuntimePluginOption = RuntimePluginConfig | false;
|
|
31
31
|
type ResolvedPluginOptions = Record<PluginName, RuntimePluginOption>;
|
|
32
|
-
type PluginConfigDefaults = Record<PluginName, RuntimePluginConfig
|
|
32
|
+
type PluginConfigDefaults = Record<PluginName, PluginOptionsFactory<RuntimePluginConfig>>;
|
|
33
|
+
type ResolvedPluginDefaults = Record<PluginName, RuntimePluginConfig>;
|
|
33
34
|
interface PluginRuntimeMeta<TInstance> {
|
|
34
35
|
__sharedInstance?: TInstance;
|
|
35
36
|
expose?: (instance: TInstance) => void;
|
|
@@ -101,6 +102,11 @@ interface PresetDefinition {
|
|
|
101
102
|
defaults: PluginConfigDefaults;
|
|
102
103
|
}
|
|
103
104
|
type TestFrameworkPresets = Record<string, PresetDefinition>;
|
|
105
|
+
interface PresetExtension {
|
|
106
|
+
manifest?: PluginManifestEntry<any, any>[];
|
|
107
|
+
defaults?: PluginConfigDefaults;
|
|
108
|
+
}
|
|
109
|
+
type PluginOptionsFactory<TOptions> = () => TOptions;
|
|
104
110
|
interface PipelineContext {
|
|
105
111
|
defaultMountOptions: ComponentFactoryOptions;
|
|
106
112
|
mountOptions: ComponentFactoryOptions;
|
|
@@ -119,7 +125,7 @@ type MountOptionsState = Partial<MountingOptions<any, any>>;
|
|
|
119
125
|
interface PipelineContextResult {
|
|
120
126
|
mountOptions: MountOptionsState;
|
|
121
127
|
global: NonNullable<MountingOptions<any, any>["global"]>;
|
|
122
|
-
pluginDefaultsState:
|
|
128
|
+
pluginDefaultsState: ResolvedPluginDefaults;
|
|
123
129
|
plugins: ResolvedPluginOptions;
|
|
124
130
|
}
|
|
125
131
|
/**
|
|
@@ -185,10 +191,12 @@ declare function createVuePlugin<TPlugin extends Plugin, TOptions extends object
|
|
|
185
191
|
|
|
186
192
|
declare function captureInstance<T = unknown>(): InstanceCapture<T>;
|
|
187
193
|
|
|
194
|
+
declare function extendPreset(basePreset: PresetDefinition, extension: PresetExtension): PresetDefinition;
|
|
195
|
+
|
|
188
196
|
declare function validatePreset(name: PluginName, preset: PresetDefinition): void;
|
|
189
197
|
|
|
190
198
|
declare function validatePresets(presets?: TestFrameworkPresets): void;
|
|
191
199
|
|
|
192
200
|
declare const Types: {};
|
|
193
201
|
|
|
194
|
-
export { type ComponentFactory, type ComponentFactoryCreator, type ComponentFactoryExtraOptions, type ComponentFactoryOptions, type MountPlugin, type PluginControlOptions, type PluginModule, type PluginOptionsInput, type PluginOptionsMap, type PluginOverridesInput, type PresetDefinition, type TestFramework, type TestFrameworkPresets, Types, captureInstance, createPluginInstance, createTestFramework, createVuePlugin, validatePreset, validatePresets };
|
|
202
|
+
export { type ComponentFactory, type ComponentFactoryCreator, type ComponentFactoryExtraOptions, type ComponentFactoryOptions, type MountPlugin, type PluginControlOptions, type PluginModule, type PluginOptionsFactory, type PluginOptionsInput, type PluginOptionsMap, type PluginOverridesInput, type PresetDefinition, type TestFramework, type TestFrameworkPresets, Types, captureInstance, createPluginInstance, createTestFramework, createVuePlugin, extendPreset, validatePreset, validatePresets };
|
package/dist/index.d.ts
CHANGED
|
@@ -29,7 +29,8 @@ type SupportedPluginsMap = Record<PluginName, SupportedPluginState>;
|
|
|
29
29
|
type RuntimePluginConfig = Record<string, any>;
|
|
30
30
|
type RuntimePluginOption = RuntimePluginConfig | false;
|
|
31
31
|
type ResolvedPluginOptions = Record<PluginName, RuntimePluginOption>;
|
|
32
|
-
type PluginConfigDefaults = Record<PluginName, RuntimePluginConfig
|
|
32
|
+
type PluginConfigDefaults = Record<PluginName, PluginOptionsFactory<RuntimePluginConfig>>;
|
|
33
|
+
type ResolvedPluginDefaults = Record<PluginName, RuntimePluginConfig>;
|
|
33
34
|
interface PluginRuntimeMeta<TInstance> {
|
|
34
35
|
__sharedInstance?: TInstance;
|
|
35
36
|
expose?: (instance: TInstance) => void;
|
|
@@ -101,6 +102,11 @@ interface PresetDefinition {
|
|
|
101
102
|
defaults: PluginConfigDefaults;
|
|
102
103
|
}
|
|
103
104
|
type TestFrameworkPresets = Record<string, PresetDefinition>;
|
|
105
|
+
interface PresetExtension {
|
|
106
|
+
manifest?: PluginManifestEntry<any, any>[];
|
|
107
|
+
defaults?: PluginConfigDefaults;
|
|
108
|
+
}
|
|
109
|
+
type PluginOptionsFactory<TOptions> = () => TOptions;
|
|
104
110
|
interface PipelineContext {
|
|
105
111
|
defaultMountOptions: ComponentFactoryOptions;
|
|
106
112
|
mountOptions: ComponentFactoryOptions;
|
|
@@ -119,7 +125,7 @@ type MountOptionsState = Partial<MountingOptions<any, any>>;
|
|
|
119
125
|
interface PipelineContextResult {
|
|
120
126
|
mountOptions: MountOptionsState;
|
|
121
127
|
global: NonNullable<MountingOptions<any, any>["global"]>;
|
|
122
|
-
pluginDefaultsState:
|
|
128
|
+
pluginDefaultsState: ResolvedPluginDefaults;
|
|
123
129
|
plugins: ResolvedPluginOptions;
|
|
124
130
|
}
|
|
125
131
|
/**
|
|
@@ -185,10 +191,12 @@ declare function createVuePlugin<TPlugin extends Plugin, TOptions extends object
|
|
|
185
191
|
|
|
186
192
|
declare function captureInstance<T = unknown>(): InstanceCapture<T>;
|
|
187
193
|
|
|
194
|
+
declare function extendPreset(basePreset: PresetDefinition, extension: PresetExtension): PresetDefinition;
|
|
195
|
+
|
|
188
196
|
declare function validatePreset(name: PluginName, preset: PresetDefinition): void;
|
|
189
197
|
|
|
190
198
|
declare function validatePresets(presets?: TestFrameworkPresets): void;
|
|
191
199
|
|
|
192
200
|
declare const Types: {};
|
|
193
201
|
|
|
194
|
-
export { type ComponentFactory, type ComponentFactoryCreator, type ComponentFactoryExtraOptions, type ComponentFactoryOptions, type MountPlugin, type PluginControlOptions, type PluginModule, type PluginOptionsInput, type PluginOptionsMap, type PluginOverridesInput, type PresetDefinition, type TestFramework, type TestFrameworkPresets, Types, captureInstance, createPluginInstance, createTestFramework, createVuePlugin, validatePreset, validatePresets };
|
|
202
|
+
export { type ComponentFactory, type ComponentFactoryCreator, type ComponentFactoryExtraOptions, type ComponentFactoryOptions, type MountPlugin, type PluginControlOptions, type PluginModule, type PluginOptionsFactory, type PluginOptionsInput, type PluginOptionsMap, type PluginOverridesInput, type PresetDefinition, type TestFramework, type TestFrameworkPresets, Types, captureInstance, createPluginInstance, createTestFramework, createVuePlugin, extendPreset, validatePreset, validatePresets };
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var FRAMEWORK_NAME = "TestForge";
|
|
|
23
23
|
var ERROR_PREFIX = `[${FRAMEWORK_NAME}]`;
|
|
24
24
|
var DEFAULT_PRESET_NAME = "default";
|
|
25
25
|
|
|
26
|
-
// src/validators/validatePreset.ts
|
|
26
|
+
// src/presets/validators/validatePreset.ts
|
|
27
27
|
function validatePreset(name, preset) {
|
|
28
28
|
if (!preset) {
|
|
29
29
|
throw new Error(`${ERROR_PREFIX} Preset "${name}" is null or undefined.`);
|
|
@@ -59,17 +59,16 @@ function validatePreset(name, preset) {
|
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
const value = preset.defaults[key];
|
|
62
|
-
|
|
63
|
-
if (!isObject) {
|
|
62
|
+
if (typeof value !== "function") {
|
|
64
63
|
throw new Error(
|
|
65
|
-
`${ERROR_PREFIX} Invalid default configuration for plugin "${key}" in preset "${name}". Expected
|
|
64
|
+
`${ERROR_PREFIX} Invalid default configuration for plugin "${key}" in preset "${name}". Expected a plugin options factory function, but received ${typeof value}.`
|
|
66
65
|
);
|
|
67
66
|
}
|
|
68
67
|
});
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
// src/
|
|
71
|
+
// src/presets/getActivePreset.ts
|
|
73
72
|
function getActivePreset(presets = {}, extraOptions) {
|
|
74
73
|
const requestedPresetName = extraOptions?.preset?.trim();
|
|
75
74
|
const activeName = requestedPresetName || DEFAULT_PRESET_NAME;
|
|
@@ -384,12 +383,29 @@ var withPluginsManifest = (ctx) => {
|
|
|
384
383
|
});
|
|
385
384
|
};
|
|
386
385
|
|
|
386
|
+
// src/presets/utils/buildPluginOptions.ts
|
|
387
|
+
function buildPluginOptions(pluginName, factory) {
|
|
388
|
+
const options = factory();
|
|
389
|
+
if (!isPlainObject(options)) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
`${ERROR_PREFIX} Plugin options factory for "${pluginName}" must return a plain object.`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
return options;
|
|
395
|
+
}
|
|
396
|
+
|
|
387
397
|
// src/pipeline/middleware/transformers/withPreset.ts
|
|
388
398
|
var withPreset = (ctx) => {
|
|
389
399
|
const { preset } = ctx;
|
|
390
400
|
if (!preset?.defaults) return ctx;
|
|
401
|
+
const pluginDefaultsState = Object.fromEntries(
|
|
402
|
+
Object.entries(preset.defaults).map(([pluginName, factory]) => [
|
|
403
|
+
pluginName,
|
|
404
|
+
buildPluginOptions(pluginName, factory)
|
|
405
|
+
])
|
|
406
|
+
);
|
|
391
407
|
return patchResultState(ctx, {
|
|
392
|
-
pluginDefaultsState
|
|
408
|
+
pluginDefaultsState
|
|
393
409
|
});
|
|
394
410
|
};
|
|
395
411
|
|
|
@@ -593,7 +609,7 @@ function mountWithPlugins(component, ctx, overrides = {}, runtimeOptions = {
|
|
|
593
609
|
});
|
|
594
610
|
}
|
|
595
611
|
|
|
596
|
-
// src/validators/validatePresets.ts
|
|
612
|
+
// src/presets/validators/validatePresets.ts
|
|
597
613
|
function validatePresets(presets = {}) {
|
|
598
614
|
if (!isPlainObject(presets)) {
|
|
599
615
|
throw new Error(`${ERROR_PREFIX} Presets must be a plain object.`);
|
|
@@ -818,6 +834,95 @@ function captureInstance() {
|
|
|
818
834
|
};
|
|
819
835
|
}
|
|
820
836
|
|
|
837
|
+
// src/presets/validators/validatePluginDefaults.ts
|
|
838
|
+
function validatePluginDefaults(pluginName, value) {
|
|
839
|
+
if (typeof value !== "function") {
|
|
840
|
+
throw new Error(
|
|
841
|
+
`${ERROR_PREFIX} Invalid defaults for plugin "${pluginName}". Preset defaults must be provided as a plugin options factory function.`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/presets/validators/validatePresetExtension.ts
|
|
847
|
+
function validatePresetExtension(basePreset, extension) {
|
|
848
|
+
if (!basePreset) {
|
|
849
|
+
throw new Error(`${ERROR_PREFIX} Cannot extend a null or undefined preset.`);
|
|
850
|
+
}
|
|
851
|
+
if (!extension) {
|
|
852
|
+
throw new Error(`${ERROR_PREFIX} Preset extension is null or undefined.`);
|
|
853
|
+
}
|
|
854
|
+
const basePluginNames = new Set(basePreset.manifest.map((entry) => entry.module.getName()));
|
|
855
|
+
const extensionManifest = extension.manifest ?? [];
|
|
856
|
+
const extensionManifestPlugins = /* @__PURE__ */ new Set();
|
|
857
|
+
for (const entry of extensionManifest) {
|
|
858
|
+
const pluginName = entry.module.getName();
|
|
859
|
+
if (extensionManifestPlugins.has(pluginName)) {
|
|
860
|
+
throw new Error(
|
|
861
|
+
`${ERROR_PREFIX} Duplicate plugin "${pluginName}" in preset extension manifest.`
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
extensionManifestPlugins.add(pluginName);
|
|
865
|
+
const isNewPlugin = !basePluginNames.has(pluginName);
|
|
866
|
+
if (isNewPlugin) {
|
|
867
|
+
if (entry.enabled === void 0) {
|
|
868
|
+
throw new Error(
|
|
869
|
+
`${ERROR_PREFIX} Cannot add plugin "${pluginName}" to an extended preset without specifying "enabled".`
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
const pluginDefaults = extension.defaults?.[pluginName];
|
|
873
|
+
if (pluginDefaults === void 0) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
`${ERROR_PREFIX} Cannot add plugin "${pluginName}" to an extended preset without defining its defaults.`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (extension.defaults) {
|
|
881
|
+
for (const [pluginName, pluginDefaults] of Object.entries(extension.defaults)) {
|
|
882
|
+
validatePluginDefaults(pluginName, pluginDefaults);
|
|
883
|
+
const isKnownPlugin = basePluginNames.has(pluginName) || extensionManifestPlugins.has(pluginName);
|
|
884
|
+
if (!isKnownPlugin) {
|
|
885
|
+
throw new Error(
|
|
886
|
+
`${ERROR_PREFIX} Cannot define defaults for unknown plugin "${pluginName}" in a preset extension. The plugin must be declared in the manifest.`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// src/presets/extendPreset.ts
|
|
894
|
+
function extendPreset(basePreset, extension) {
|
|
895
|
+
validatePresetExtension(basePreset, extension);
|
|
896
|
+
const manifest = basePreset.manifest.map((entry) => ({ ...entry }));
|
|
897
|
+
const defaults = { ...basePreset.defaults };
|
|
898
|
+
const manifestByName = new Map(manifest.map((entry) => [entry.module.getName(), entry]));
|
|
899
|
+
const extensionManifest = extension.manifest ?? [];
|
|
900
|
+
for (const entry of extensionManifest) {
|
|
901
|
+
const pluginName = entry.module.getName();
|
|
902
|
+
const existingEntry = manifestByName.get(pluginName);
|
|
903
|
+
if (existingEntry) {
|
|
904
|
+
if (entry.enabled !== void 0) {
|
|
905
|
+
existingEntry.enabled = entry.enabled;
|
|
906
|
+
}
|
|
907
|
+
} else {
|
|
908
|
+
manifest.push({
|
|
909
|
+
module: entry.module,
|
|
910
|
+
enabled: entry.enabled
|
|
911
|
+
});
|
|
912
|
+
manifestByName.set(pluginName, manifest[manifest.length - 1]);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
if (extension.defaults) {
|
|
916
|
+
for (const [pluginName, pluginDefaults] of Object.entries(extension.defaults)) {
|
|
917
|
+
defaults[pluginName] = pluginDefaults;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
manifest,
|
|
922
|
+
defaults
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
821
926
|
// src/index.ts
|
|
822
927
|
var Types = {};
|
|
823
928
|
export {
|
|
@@ -826,6 +931,7 @@ export {
|
|
|
826
931
|
createPluginInstance,
|
|
827
932
|
createTestFramework,
|
|
828
933
|
createVuePlugin,
|
|
934
|
+
extendPreset,
|
|
829
935
|
validatePreset,
|
|
830
936
|
validatePresets
|
|
831
937
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testforgejs/vue-test-core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core testing utilities and pipeline orchestration for the TestForge component testing framework",
|
|
6
6
|
"homepage": "https://github.com/testforgejs/testforge#readme",
|
|
@@ -51,10 +51,12 @@
|
|
|
51
51
|
"vue": "^3.3.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
+
"@vue/test-utils": "^2.0.0",
|
|
55
|
+
"vue": "^3.3.0",
|
|
54
56
|
"vue-router": "^5.0.0",
|
|
55
|
-
"@testforgejs/vue-test-plugin-i18n": "1.0.0-beta.
|
|
56
|
-
"@testforgejs/vue-test-plugin-
|
|
57
|
-
"@testforgejs/vue-test-plugin-
|
|
57
|
+
"@testforgejs/vue-test-plugin-i18n": "1.0.0-beta.1",
|
|
58
|
+
"@testforgejs/vue-test-plugin-router": "1.0.0-beta.1",
|
|
59
|
+
"@testforgejs/vue-test-plugin-pinia": "1.0.0-beta.1"
|
|
58
60
|
},
|
|
59
61
|
"tsd": {
|
|
60
62
|
"directory": "test-d"
|