nuxt-vitest-shared-app 0.1.0 → 0.1.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 CHANGED
@@ -15,7 +15,7 @@ Tests that need plugins re-initialized or a different startup config belong in a
15
15
  - [The problem](#the-problem)
16
16
  - [Install](#install)
17
17
  - [Quick start](#quick-start)
18
- - [Migrating an existing suite](#migrating-an-existing-suite)
18
+ - [Switching from per-file startup](#switching-from-per-file-startup)
19
19
  - [Per-test overrides](#per-test-overrides)
20
20
  - [Project resets](#project-resets)
21
21
  - [Worker state](#worker-state)
@@ -64,9 +64,8 @@ import { defineSharedNuxtVitestConfig } from "nuxt-vitest-shared-app/config"
64
64
 
65
65
  export default defineSharedNuxtVitestConfig({
66
66
  test: {
67
- include: ["**/*.vitest.ts"],
67
+ include: ["test/nuxt/**/*.test.ts"],
68
68
  environment: "nuxt",
69
- pool: "threads",
70
69
  isolate: false,
71
70
  },
72
71
  })
@@ -92,24 +91,23 @@ defineSharedNuxtVitestConfig(config, { silenceSuspenseInfo: true })
92
91
 
93
92
  With `silenceSuspenseInfo: true`, Vue's `<Suspense> is an experimental feature and its API will likely change.` message, which every `mountSuspended` call prints, is filtered through `test.onConsoleLog`; your own `onConsoleLog` still receives all other messages.
94
93
 
95
- ## Migrating an existing suite
94
+ ## Switching from per-file startup
96
95
 
97
- Remove what the library now does:
96
+ With plain `@nuxt/test-utils`, every test file starts from fresh modules and globals.
97
+ With a shared app, modules stay cached for the following files of the worker, so objects created at module level live for the whole environment.
98
+ Tests that passed with per-file startup may rely on that implicitly and start to depend on the order of files.
98
99
 
99
- - `enableAutoUnmount` calls in project setup files: the library installs its own, and `@vue/test-utils` rejects a second call;
100
- - hooks that clean up what the library [cleans up](#cleanup), such as Nuxt state, storage, cookies, timers, and stubbed globals;
101
- - local patches or workarounds that start Nuxt once per worker.
102
-
103
- Keep project-specific cleanup, and move hooks that must run after every test into [project resets](#project-resets).
104
-
105
- Because modules stay cached, objects created at module level live for the whole environment, not for one file.
106
- A global replaced by one test file affects every file that runs after it in the worker, including objects that other modules already created from the previous global.
100
+ A typical case is a global replaced by one test file.
101
+ It affects every file that runs after it in the worker, including objects that other modules already created from the previous global.
107
102
  For example, a test file that imports a forced `Intl` polyfill replaces the global constructor, while a formatter created earlier at module level still belongs to the previous implementation.
108
103
 
109
104
  - Install global polyfills once in a project setup file, before components are imported, and prefer polyfills that keep an existing implementation.
110
105
  - Do not replace global constructors with side-effect imports in individual test files.
111
106
  - Review module-level objects that depend on such globals, such as formatters, clients, and caches.
112
- - After the migration, run the suite on one worker with shuffled files, so all files share one environment in a different order, e.g. `vitest run --maxWorkers=1 --sequence.shuffle.files --sequence.seed=1`.
107
+ - Run the suite on one worker with shuffled files, so all files share one environment in a different order, e.g. `vitest run --maxWorkers=1 --sequence.shuffle.files --sequence.seed=1`.
108
+
109
+ Remove `enableAutoUnmount` calls from project setup files: the library installs its own, and `@vue/test-utils` throws on a second call.
110
+ Hooks that clean up what the library [cleans up](#cleanup) keep working, and you can remove them.
113
111
 
114
112
  ## Per-test overrides
115
113
 
@@ -123,13 +121,13 @@ Register overridable imports in a project setup file:
123
121
  export default defineSharedNuxtVitestConfig({
124
122
  test: {
125
123
  // ...
126
- setupFiles: ["./testing/nuxt/setup.ts"],
124
+ setupFiles: ["./test/nuxt/setup.ts"],
127
125
  },
128
126
  })
129
127
  ```
130
128
 
131
129
  ```ts
132
- // testing/nuxt/setup.ts
130
+ // test/nuxt/setup.ts
133
131
  import { mockNuxtImport } from "@nuxt/test-utils/runtime"
134
132
  import { overridableNuxtImport } from "nuxt-vitest-shared-app"
135
133
 
@@ -174,7 +172,7 @@ Use a fallback for behavior that the app needs while it starts.
174
172
  First add `useUser` and its type to `TestNuxtImports`, as in the setup file above, then:
175
173
 
176
174
  ```ts
177
- // testing/nuxt/setup.ts
175
+ // test/nuxt/setup.ts
178
176
  mockNuxtImport(
179
177
  "useUser",
180
178
  overridableNuxtImport("useUser", () => ref(null)),
@@ -218,11 +216,11 @@ It requires `useRoute` in `TestNuxtImports` and an overridable `useRoute` mock.
218
216
  registerNuxtTestReset(key: string, reset: () => unknown): () => void
219
217
  ```
220
218
 
221
- Registers a reset that runs after each test, see step 6 of the [cleanup](#cleanup).
219
+ Registers a reset that runs after each test, see step 7 of the [cleanup](#cleanup).
222
220
  Use it for application resources that the library does not know about, such as test API clients, subscriptions, and caches:
223
221
 
224
222
  ```ts
225
- // testing/nuxt/setup.ts
223
+ // test/nuxt/setup.ts
226
224
  import { registerNuxtTestReset } from "nuxt-vitest-shared-app"
227
225
 
228
226
  import { testApiClient } from "./api-client"
@@ -303,20 +301,21 @@ In such files, `overridableNuxtImport` factories return the fallback or the orig
303
301
  After each test, the library runs these steps in order.
304
302
  They run after all `afterEach` hooks of the project, also when one of these hooks fails.
305
303
 
306
- 1. unmounts wrappers mounted in the test through `@vue/test-utils` (including `mountSuspended`), using `enableAutoUnmount`;
307
- 2. `await nextTick()` and `await flushPromises()`;
308
- 3. navigates the app's own router back to the route baseline with `router.replace` if the route differs, and verifies the resulting route;
309
- 4. clears import overrides;
310
- 5. `await clearError()`, `clearNuxtData()`, `clearNuxtState(undefined, { reset: false })`;
311
- 6. runs project resets registered with `registerNuxtTestReset`, in registration order, awaiting each;
312
- 7. `localStorage.clear()`, `sessionStorage.clear()`, and expires every cookie visible in `document.cookie` for path `/` of the current origin;
313
- 8. `vi.clearAllTimers()`, `vi.useRealTimers()`, `vi.unstubAllEnvs()`, `vi.unstubAllGlobals()`;
314
- 9. removes direct children of `document.body` that were not present at startup.
304
+ 1. `vi.clearAllTimers()` and `vi.useRealTimers()`, so the following steps and project resets run under real timers and the test does not need to restore them;
305
+ 2. unmounts wrappers mounted in the test through `@vue/test-utils` (including `mountSuspended`), using `enableAutoUnmount`;
306
+ 3. `await nextTick()` and `await flushPromises()`;
307
+ 4. navigates the app's own router back to the route baseline with `router.replace` if the route differs, and verifies the resulting route;
308
+ 5. clears import overrides;
309
+ 6. `await clearError()`, `clearNuxtData()`, `clearNuxtState(undefined, { reset: false })`;
310
+ 7. runs project resets registered with `registerNuxtTestReset`, in registration order, awaiting each;
311
+ 8. `localStorage.clear()`, `sessionStorage.clear()`, and expires every cookie visible in `document.cookie` for path `/` of the current origin;
312
+ 9. `vi.unstubAllEnvs()` and `vi.unstubAllGlobals()`;
313
+ 10. removes direct children of `document.body` that were not present at startup.
315
314
 
316
315
  The exact scope:
317
316
 
318
317
  - `flushPromises` only settles handlers of promises that are already resolved; subscriptions, requests, and other background work need their own project resets;
319
- - step 3 uses the router instance of the app, never the overridable `useRoute`/`navigateTo`;
318
+ - step 4 uses the router instance of the app, never the overridable `useRoute`/`navigateTo`;
320
319
  - DOM cleanup removes only new direct children of `document.body`; changes to nodes that existed at startup are not reverted;
321
320
  - cookies are expired only as listed above; `HttpOnly` cookies and cookies of other paths or domains are not touched;
322
321
  - state created by startup plugins is not re-created.
@@ -355,7 +354,7 @@ import { defineVitestConfig } from "@nuxt/test-utils/config"
355
354
 
356
355
  export default defineVitestConfig({
357
356
  test: {
358
- include: ["**/*.isolated.vitest.ts"],
357
+ include: ["test/nuxt-isolated/**/*.test.ts"],
359
358
  environment: "nuxt",
360
359
  },
361
360
  })
@@ -366,7 +365,7 @@ vitest run
366
365
  vitest run --config vitest.isolated.config.ts
367
366
  ```
368
367
 
369
- Exclude the isolated files from the shared-app config.
368
+ Keep the `include` patterns of the two configs from overlapping, so each file runs in one of them.
370
369
 
371
370
  ## Compatibility and upstream
372
371
 
@@ -42,6 +42,7 @@ async function cleanupAfterTest(store, baseline, unmountWrappers) {
42
42
  });
43
43
  }
44
44
  }
45
+ for (const [name, run] of TIMER_STEPS) await step(name, run);
45
46
  await step("unmount wrappers", () => unmountWrappers?.());
46
47
  await step("flush promises", async () => {
47
48
  await nextTick();
@@ -73,7 +74,7 @@ async function cleanupAfterTest(store, baseline, unmountWrappers) {
73
74
  await step("clear localStorage", () => localStorage.clear());
74
75
  await step("clear sessionStorage", () => sessionStorage.clear());
75
76
  await step("expire cookies", expireCookies);
76
- for (const [name, run] of RUNNER_STEPS) await step(name, run);
77
+ for (const [name, run] of STUB_STEPS) await step(name, run);
77
78
  await step("remove body children", () => {
78
79
  for (const child of Array.from(document.body.children)) if (!baseline.bodyChildren.has(child)) child.remove();
79
80
  });
@@ -82,12 +83,9 @@ async function cleanupAfterTest(store, baseline, unmountWrappers) {
82
83
  function importNuxt() {
83
84
  return import("nuxt/app");
84
85
  }
85
- const RUNNER_STEPS = [
86
- ["clear timers", () => vi.clearAllTimers()],
87
- ["use real timers", () => vi.useRealTimers()],
88
- ["unstub envs", () => vi.unstubAllEnvs()],
89
- ["unstub globals", () => vi.unstubAllGlobals()]
90
- ];
86
+ const TIMER_STEPS = [["clear timers", () => vi.clearAllTimers()], ["use real timers", () => vi.useRealTimers()]];
87
+ const STUB_STEPS = [["unstub envs", () => vi.unstubAllEnvs()], ["unstub globals", () => vi.unstubAllGlobals()]];
88
+ const RUNNER_STEPS = [...TIMER_STEPS, ...STUB_STEPS];
91
89
  /** Restores timers, envs and globals as far as possible, ignoring failures, for an environment that is already contaminated. */
92
90
  function restoreTestRunner() {
93
91
  for (const [, run] of RUNNER_STEPS) try {
@@ -132,9 +130,9 @@ function installLifecycle() {
132
130
  beforeAll(async () => {
133
131
  const store = getEnvironmentStore();
134
132
  assertNotContaminated(store);
133
+ if (store.startupFailure) throw new Error("nuxt-vitest-shared-app: Nuxt startup failed earlier in this environment.", { cause: store.startupFailure });
135
134
  const { tryUseNuxtApp } = await import("nuxt/app");
136
135
  const nuxtApp = assertAppRunning(tryUseNuxtApp(), document);
137
- if (store.startupFailure) throw new Error("nuxt-vitest-shared-app: Nuxt startup failed earlier in this environment.", { cause: store.startupFailure });
138
136
  if (!store.baseline) {
139
137
  const startupError = getStartupError(nuxtApp);
140
138
  if (startupError) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-vitest-shared-app",
3
- "version": "0.1.0",
4
- "description": "Run Nuxt component tests in Vitest on one Nuxt app per worker, with deterministic per-test cleanup and typed auto-import overrides.",
3
+ "version": "0.1.1",
4
+ "description": "Run Nuxt component tests in Vitest on one shared app per worker, with per-test cleanup.",
5
5
  "keywords": [
6
6
  "nuxt",
7
7
  "nuxt-test-utils",