@equinor/fusion-framework-vitest-plugin-react-app 1.0.0-next.6 → 1.0.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.
Files changed (51) hide show
  1. package/dist/esm/index.js +9 -4
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/resolve-app-test-env.js +8 -2
  4. package/dist/esm/resolve-app-test-env.js.map +1 -1
  5. package/dist/esm/scope/resolve-fusion.js +22 -24
  6. package/dist/esm/scope/resolve-fusion.js.map +1 -1
  7. package/dist/esm/test-app.js +5 -8
  8. package/dist/esm/test-app.js.map +1 -1
  9. package/dist/esm/test.js +2 -1
  10. package/dist/esm/test.js.map +1 -1
  11. package/dist/esm/version.js +1 -1
  12. package/dist/esm/version.js.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/types/index.d.ts +4 -3
  15. package/dist/types/resolve-app-test-env.d.ts +3 -1
  16. package/dist/types/scope/resolve-fusion.d.ts +6 -7
  17. package/dist/types/test-app.d.ts +12 -11
  18. package/dist/types/test.d.ts +20 -9
  19. package/dist/types/version.d.ts +1 -1
  20. package/package.json +17 -19
  21. package/CHANGELOG.md +0 -196
  22. package/docs/advanced.md +0 -209
  23. package/docs/configuration.md +0 -112
  24. package/docs/getting-started.md +0 -66
  25. package/docs/migrating-an-existing-app.md +0 -202
  26. package/docs/module-mocks.md +0 -120
  27. package/docs/overview.md +0 -49
  28. package/docs/troubleshooting.md +0 -67
  29. package/docs/why-browser-mode.md +0 -128
  30. package/src/__tests__/app-test-vite-plugin.test.ts +0 -109
  31. package/src/__tests__/merge-env-config.test.ts +0 -67
  32. package/src/__tests__/resolve-app-test-env.test.ts +0 -83
  33. package/src/app-test.ts +0 -19
  34. package/src/define-project.ts +0 -91
  35. package/src/index.ts +0 -124
  36. package/src/merge-env-config.ts +0 -58
  37. package/src/render-app-component.tsx +0 -116
  38. package/src/render-app-hook.tsx +0 -135
  39. package/src/render.tsx +0 -64
  40. package/src/resolve-app-test-env.ts +0 -132
  41. package/src/scope/create-app-scope-wrapper.tsx +0 -26
  42. package/src/scope/default-app-env.ts +0 -13
  43. package/src/scope/index.ts +0 -4
  44. package/src/scope/resolve-app-scope.ts +0 -46
  45. package/src/scope/resolve-fusion.ts +0 -74
  46. package/src/test-app.tsx +0 -148
  47. package/src/test.tsx +0 -42
  48. package/src/version.ts +0 -2
  49. package/src/virtual-modules.d.ts +0 -12
  50. package/tsconfig.json +0 -26
  51. package/vitest.config.ts +0 -10
package/docs/advanced.md DELETED
@@ -1,209 +0,0 @@
1
- # Advanced Fusion app testing
2
-
3
- The `/test` entry point extends Vitest's test context with `appEnv`, `configureApp`, `configureFusion`,
4
- `fusion`, `app`, `render`, and `renderHook`. Fixture declarations are reusable, while each test
5
- receives fresh framework and app module instances.
6
-
7
- This guide covers composing and overriding those Fusion fixture values; see Vitest's own
8
- [Test Context](https://vitest.dev/guide/test-context) documentation for fixture scope, cleanup,
9
- and the general `test.extend`/`test.override` mechanics Fusion builds on.
10
-
11
- ## Choose a rendering API
12
-
13
- | API | Use it when |
14
- | --- | --- |
15
- | `test` from `/test` | The app's manifest, config, and module configurator should resolve automatically |
16
- | `render` from `/test` | A standard `describe` or `it` block needs the automatically resolved app scope |
17
- | `renderAppHook` | A hook needs an app scope with explicitly supplied options |
18
- | `renderAppComponent` | A component needs explicit `env`, `configure`, or parent `fusion` options |
19
- | `testApp` | A reusable fixture should not depend on Vite's automatic app-file resolution |
20
-
21
- All Fusion rendering APIs initialize modules asynchronously and must be awaited. The lower
22
- level helpers return `fusion.framework` and `fusion.app`, allowing a test to drive the exact
23
- module instances consumed by the rendered subject.
24
-
25
- ## Extend app configuration
26
-
27
- Create one reusable extended test when several cases need the same deterministic modules.
28
- This must extend `test` from `/test`, not `testApp` from the main entry point:
29
- `testApp`'s `configureApp` defaults to `undefined` (no Vite dependency, so no way to load the
30
- real `src/config.ts` as live code), while `/test`'s `test` seeds it with the app's real
31
- `configure` export via `appTestVitePlugin`'s virtual modules.
32
-
33
- ```tsx
34
- import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
35
- import { test as baseTest } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
36
-
37
- const project = {
38
- id: 'project-a',
39
- title: 'Project A',
40
- type: { id: 'ProjectMaster' },
41
- value: {},
42
- };
43
-
44
- export const test = baseTest.extend('configureApp', ({ configureApp }) => (configurator, args) => {
45
- configureApp?.(configurator, args);
46
- enableContextMock(configurator, (mock) => mock.setCurrentContext(project));
47
- });
48
- ```
49
-
50
- Composing the original `configureApp` fixture preserves the application's production module
51
- configuration. See [Module mocks](module-mocks.md) for authentication, context, bookmark,
52
- feature-flag, HTTP, analytics, and telemetry boundaries.
53
-
54
- `.extend(...)` returns a new, separately exported `test` — reach for it when several test
55
- *files* need the same fixture default. Within one file, prefer `test.override(...)` (below):
56
- it replaces a fixture in place, so every test in that file keeps importing the same `test`.
57
-
58
- ## Fake an endpoint URL with `mergeEnvConfig`
59
-
60
- `appEnv`'s `config` is an `AppConfig` instance — its `environment`/`endpoints` live behind
61
- private fields exposed only through getters, so `{ ...appEnv.config, endpoints: {...} }` copies
62
- nothing and silently drops the rest of the config. Use `mergeEnvConfig` to fake one endpoint's
63
- URL (e.g. the app's real backend, resolved from `app.config.ts`) while keeping every other
64
- endpoint and environment variable intact:
65
-
66
- ```tsx
67
- import { mergeEnvConfig, test as baseTest } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
68
-
69
- export const test = baseTest.extend('appEnv', ({ appEnv }) =>
70
- mergeEnvConfig(appEnv, { endpoints: { 'cpr-api': { url: backendBaseUrl } } }),
71
- );
72
- ```
73
-
74
- ## Override a fixture for one test or a `describe` block
75
-
76
- `test.override('name', ...)` replaces a fixture's resolved value without creating a new `test`
77
- export. Called at the top of a `describe` block, the override applies to every test inside it
78
- and does not leak to sibling blocks or other files — each `describe` starts from the file's
79
- base `test` again. Called at the top of the file (outside any `describe`), it applies to every
80
- test in that file.
81
-
82
- **`configureApp`'s default value, from `/test`, *is* the app's real `src/config.ts` `configure`
83
- export.** `test.override('configureApp', ...)` replaces that default outright — an override
84
- that doesn't call the real `configure(configurator, args)` itself, as below, skips the app's
85
- production module setup entirely rather than composing with it.
86
-
87
- ```tsx
88
- import { test } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
89
- import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
90
- import { describe } from 'vitest';
91
-
92
- import { configure } from '../config'; // the app's own, real module configurator
93
-
94
- const project = { id: 'project-a', title: 'Project A', type: { id: 'ProjectMaster' }, value: {} };
95
-
96
- describe('with an initial project', () => {
97
- test.override('configureApp', { injected: true }, () => (configurator, args) => {
98
- configure(configurator, args); // compose the app's real configure, same as `.extend`
99
- enableContextMock(configurator, (mock) => mock.setCurrentContext(project));
100
- });
101
-
102
- test('displays the initial context the app resolves on startup', async ({ render }) => {
103
- const { getByText } = await render(<App />);
104
- await expect.element(getByText(/project-a/)).toBeInTheDocument();
105
- });
106
- });
107
- ```
108
-
109
- `fusion` itself can be overridden the same way, when a case needs a differently configured
110
- parent framework instance rather than a change to the app's own `configureApp`:
111
-
112
- ```tsx
113
- describe('with a parent framework context', () => {
114
- test.override('fusion', async ({ appEnv }) =>
115
- mockFramework<[AppModule, ContextModule]>((configurator) => {
116
- enableAppManifestMock(configurator, appEnv);
117
- enableContextMock(configurator, (mock) => mock.setCurrentContext(project));
118
- }),
119
- );
120
-
121
- test('mirrors the context the parent sets', async ({ render, fusion }) => {
122
- /* ... */
123
- });
124
- });
125
- ```
126
-
127
- The `{ injected: true }` option matches the fixture's original declaration (see
128
- `test-app.tsx`/`test.tsx`); it is not required for `.override(...)` itself, but keeping it
129
- consistent avoids re-deriving whether the base fixture accepts a config-injected value. See
130
- the [React context cookbook](../../../../cookbooks/app-react-context/src/App.test.tsx) and
131
- [feature-flag cookbook](../../../../cookbooks/app-react-feature-flag/src/components/FeatureFlags.test.tsx)
132
- for full working examples, and Vitest's own
133
- [Test Context](https://vitest.dev/guide/test-context) guide for fixture scope and cleanup
134
- mechanics beyond what Fusion adds.
135
-
136
- ## Supply a custom parent framework
137
-
138
- Override or explicitly pass `fusion` when the scenario depends on portal-level context,
139
- service discovery, authentication, or app manifests. Build the parent with `mockFramework`,
140
- then initialize the app beneath it. This exercises parent-to-app propagation without a
141
- running portal.
142
-
143
- Use [`enableAppManifestMock`](../../../app/docs/testing.md#enableappmanifestmockconfigurator-env)
144
- when the custom parent must serve the app's manifest and config. Use
145
- [`mockFramework`](../../../framework/docs/testing.md) to seed the parent modules.
146
-
147
- ## Extend the parent framework mock with `configureFusion`
148
-
149
- `fusion` (built by [`resolveFusion`](../src/scope/resolve-fusion.ts)) always carries a mocked
150
- `app` module (serving this app's manifest) and a `navigation` module with in-memory history, so
151
- tests don't leak URL/history state between runs. It also mocks the `featureFlag` module — with
152
- no flags enabled, so `useFeature` needs no `localStorage` or URL seeding — but only when
153
- `@equinor/fusion-framework-module-feature-flag` (an optional peer dependency) is actually
154
- installed; apps that don't use feature flags aren't forced to add it. `configureFusion` runs on
155
- the same configurator afterwards, so a test can register extra framework-level modules or
156
- override that setup, without reimplementing it:
157
-
158
- ```tsx
159
- import { enableFeatureFlagMock } from '@equinor/fusion-framework-module-feature-flag/mock';
160
-
161
- const test = baseTest.extend('configureFusion', { injected: true }, () => (configurator) => {
162
- // seed a specific flag rather than merely enabling the mock — that's already the default
163
- enableFeatureFlagMock(configurator, (mock) => mock.addFeature({ key: 'new-search', enabled: true }));
164
- configurator.serviceDiscovery.addServices([{ key: 'people', uri: baseUrl('people') }]);
165
- });
166
- ```
167
-
168
- Note this is a **framework-scope** module set, distinct from the app-scope one `configureApp`
169
- seeds. A typical app registers its own `navigation` module independently, inside its own
170
- `config.ts`'s `configure` callback (what `configureApp` composes with) — so overriding history
171
- through `configureFusion` only affects framework-scope consumers, such as
172
- `useFramework<[NavigationModule]>().modules.navigation` or `useBookmarkNavigate`, not a
173
- rendered app's own router:
174
-
175
- ```tsx
176
- import { enableNavigation, createHistory } from '@equinor/fusion-framework-module-navigation';
177
-
178
- test.override('configureFusion', { injected: true }, () => (configurator) =>
179
- enableNavigation(configurator, {
180
- configure: (config) => config.setHistory(createHistory('browser')),
181
- }),
182
- );
183
- ```
184
-
185
- **`.override('fusion', ...)` bypasses `configureFusion` entirely** — it replaces the resolver
186
- that calls it, so a `configureFusion` override on the same test/suite is silently never called.
187
- Reach for `configureFusion` to extend the base mock; reach for `fusion` only to replace it
188
- outright (e.g. with a fully custom or non-mocked instance) — see
189
- [Supply a custom parent framework](#supply-a-custom-parent-framework) above.
190
-
191
- ## Test routes and app lifecycle
192
-
193
- Render the complete `App` when assertions depend on route loaders, navigation, app
194
- configuration, or several modules working together. Set the initial browser URL before
195
- rendering, interact through visible controls or `app.navigation`, and assert the rendered
196
- result.
197
-
198
- The [React router cookbook](../../../../cookbooks/app-react-router/README.md) demonstrates
199
- route-unit tests, complete application navigation, and HTTP-backed loaders.
200
-
201
- ## Runner-level advanced behavior
202
-
203
- Fusion does not wrap these Vitest capabilities:
204
-
205
- - [Browser interactions and assertions](https://vitest.dev/guide/browser/)
206
- - [Asynchronous tests](https://vitest.dev/guide/learn/async)
207
- - [Test projects](https://vitest.dev/guide/projects)
208
- - [Coverage](https://vitest.dev/guide/coverage)
209
- - [Debugging](https://vitest.dev/guide/debugging)
@@ -1,112 +0,0 @@
1
- # Configure Fusion React app tests
2
-
3
- The package exposes three entry points with different responsibilities:
4
-
5
- | Import | Responsibility |
6
- | --- | --- |
7
- | `@equinor/fusion-framework-vitest-plugin-react-app/config` | Build a browser-ready Vitest project with `defineProject` |
8
- | `@equinor/fusion-framework-vitest-plugin-react-app/test` | Resolve the app's files and provide `test` and `render` fixtures |
9
- | `@equinor/fusion-framework-vitest-plugin-react-app` | Configure the Vite plugin manually or render with explicit `env`, `configure`, and `fusion` options |
10
-
11
- ## `defineProject` defaults
12
-
13
- ```ts
14
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
15
-
16
- export default defineProject();
17
- ```
18
-
19
- The default project registers `appTestVitePlugin()` and configures:
20
-
21
- - test files under `src/**/*.{test,spec}.{ts,tsx}`
22
- - Vitest Browser Mode enabled
23
- - the Playwright provider with one Chromium instance
24
- - headless browser execution
25
- - a `1024x768` default viewport, matching a typical low-resolution Citrix session rather than
26
- Vitest's own mobile-sized default
27
- - Vite warmup for `src/**/*.{ts,tsx}` to discover lazy imports before tests run
28
-
29
- ## Merge ordinary Vitest options
30
-
31
- Pass an object to deep-merge it with the defaults through Vite's `mergeConfig`:
32
-
33
- ```ts
34
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
35
- import { name, version } from './package.json' with { type: 'json' };
36
-
37
- export default defineProject({
38
- test: {
39
- name: `${name}@${version}`,
40
- // opt into a wider viewport for an app that only needs to support desktop
41
- browser: { viewport: { width: 1920, height: 1080 } },
42
- },
43
- });
44
- ```
45
-
46
- These are standard Vitest and Vite options. Use the official Vitest configuration reference
47
- for runner behavior rather than looking for Fusion-specific equivalents.
48
-
49
- ## Replace defaults deliberately
50
-
51
- Pass a function only when a deep merge cannot express the change. The function's return value
52
- replaces the defaults outright, so preserve every default the project still needs:
53
-
54
- ```ts
55
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
56
-
57
- export default defineProject((defaults) => ({
58
- ...defaults,
59
- test: {
60
- ...defaults.test,
61
- include: ['tests/browser/**/*.test.tsx'],
62
- },
63
- }));
64
- ```
65
-
66
- When test files move outside `src`, update both `test.include` and `server.warmup.clientFiles`
67
- if those tests load lazy application modules.
68
-
69
- ## Resolve non-standard app files
70
-
71
- Use `appTestVitePlugin` directly when the app manifest, app configuration, or module
72
- configurator does not use Fusion's normal file conventions:
73
-
74
- ```ts
75
- import { playwright } from '@vitest/browser-playwright';
76
- import { appTestVitePlugin } from '@equinor/fusion-framework-vitest-plugin-react-app';
77
- import { defineProject } from 'vitest/config';
78
-
79
- export default defineProject({
80
- plugins: [
81
- appTestVitePlugin({
82
- manifest: './config/app.manifest.ts',
83
- config: './config/app.config.ts',
84
- configure: './config/modules.ts',
85
- }),
86
- ],
87
- test: {
88
- browser: {
89
- enabled: true,
90
- provider: playwright(),
91
- headless: true,
92
- instances: [{ browser: 'chromium' }],
93
- },
94
- },
95
- server: { warmup: { clientFiles: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts'] } },
96
- });
97
- ```
98
-
99
- `entrypoint`, `manifest`, and `config` use the same resolution pipeline as `ffc app build`
100
- and `ffc app dev`. `configure` must identify a real source file because the plugin re-exports
101
- its live application code into the test bundle. Explicit files that do not exist throw
102
- `FileNotFoundError`; convention-based lookups fall back to defaults.
103
-
104
- ## Choose fixtures or explicit render options
105
-
106
- Use `/test` when the app's own files should resolve automatically. Use `renderAppHook`,
107
- `renderAppComponent`, or `testApp` from the root entry point when the test should pass `env`,
108
- `configure`, or a parent `fusion` instance explicitly.
109
-
110
- - [`test` and app rendering](getting-started.md)
111
- - [Detailed rendering and fixture APIs](../README.md#renderapphook)
112
- - [Troubleshooting configuration](troubleshooting.md)
@@ -1,66 +0,0 @@
1
- # Test a Fusion React app with Vitest
2
-
3
- Use `@equinor/fusion-framework-vitest-plugin-react-app` to render an existing Fusion React
4
- app in Vitest Browser Mode. The test runs in Chromium with a fresh framework and application
5
- module scope, a deterministic signed-in `Test User`, and no running portal or real credentials.
6
-
7
- ## Install
8
-
9
- ```sh
10
- pnpm add -D vitest playwright @vitest/browser-playwright vitest-browser-react \
11
- @equinor/fusion-framework-vitest-plugin-react-app
12
- pnpm exec playwright install chromium
13
- ```
14
-
15
- React, React DOM, RxJS, and Vite are peer dependencies that a Fusion React app normally
16
- already provides.
17
-
18
- ## Configure Vitest
19
-
20
- Create `vitest.config.ts` at the app project root:
21
-
22
- ```ts
23
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
24
-
25
- export default defineProject();
26
- ```
27
-
28
- `defineProject` registers `appTestVitePlugin`, enables headless Chromium through Playwright,
29
- includes `src/**/*.{test,spec}.{ts,tsx}`, and warms application source files so lazy route
30
- imports do not reload the browser during a test.
31
-
32
- ## Write the first app test
33
-
34
- ```tsx
35
- import { expect } from 'vitest';
36
- import { test } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
37
- import { App } from './App';
38
-
39
- test('renders the app', async ({ render }) => {
40
- const screen = await render(<App />);
41
- await expect.element(screen.getByRole('heading')).toBeVisible();
42
- });
43
- ```
44
-
45
- The `/test` entry point resolves the app's manifest, `app.config.ts`, and module configurator
46
- through the registered Vite plugin. Always await `render`, `renderHook`,
47
- `renderAppComponent`, and `renderAppHook`; Fusion module initialization completes before the
48
- first render.
49
-
50
- ## Run
51
-
52
- ```sh
53
- pnpm exec vitest
54
- pnpm exec vitest run
55
- ```
56
-
57
- The first command watches for changes. The second performs one run for CI. A successful run
58
- reports one passing test and requires no portal, credentials, or backend service.
59
-
60
- ## Next steps
61
-
62
- - [Configure the test project and app-file resolution](configuration.md)
63
- - [Resolve common setup, browser, and network failures](troubleshooting.md)
64
- - [Seed Fusion module dependencies](module-mocks.md)
65
- - [Choose between app, framework, module, and HTTP test layers](../../../framework/docs/testing-choosing-a-layer.md)
66
- - [Seed authentication, context, service discovery, and HTTP](../README.md#overview)
@@ -1,202 +0,0 @@
1
- # Migrate an existing app to Fusion Vitest
2
-
3
- Use this guide to move existing application tests to
4
- `@equinor/fusion-framework-vitest-plugin-react-app`. It covers suites built with
5
- `@testing-library/react`, `jsdom` or `happy-dom`, hand-written Fusion hook mocks, and mock HTTP
6
- servers such as MirageJS, MSW, or Nock.
7
-
8
- You can migrate one test at a time. A mixed suite is a valid final state.
9
-
10
- ## Choose which tests to migrate
11
-
12
- Start with tests that read from the application module scope through
13
- `useAppModule`, `useAccessToken`, `useCurrentContext`, `useCurrentBookmark`, `useFeature`, or a
14
- route that uses several Fusion modules. The plugin can replace their hand-written hook mocks
15
- with the real module pipeline and supported `enable*Mock` configuration.
16
-
17
- Keep pure functions, presentational components, and hooks with no Fusion dependency on their
18
- current runner when that is simpler. A test can also use the real Fusion module setup without
19
- Browser Mode by providing its own renderer. See
20
- [Why Browser Mode is the default](why-browser-mode.md#choose-a-different-runtime).
21
-
22
- ## What changes, concretely
23
-
24
- | Existing pattern | Replace with |
25
- | --- | --- |
26
- | `@testing-library/react`'s `render`/`renderHook`/`waitFor`/`act`, on `jsdom` or `happy-dom` | `vitest-browser-react`'s `render`/`renderHook` and `vitest`'s `waitFor`/`act`, on real Chromium (Vitest Browser Mode) |
27
- | `vi.mock('@equinor/fusion-framework-react/hooks', ...)`, `vi.mock('.../feature-flag', ...)`, and similar hand-rolled Fusion hook mocks | The owning module's `enable*Mock` from its own `/mock` entry point. See [Module mocks](module-mocks.md) |
28
- | A hand-written mock HTTP server (MirageJS, `msw/node`, `nock`) answering every backend call | `configurator.http.addMiddleware(...)` with a hand-written `HttpMiddleware`, or `createRouterMiddleware` for several routes under one base URI. See [HTTP testing](../../../modules/http/docs/testing.md) |
29
- | A hand-rolled test wrapper composing a router, context providers, and error boundaries around every render | `test.extend(...)` fixtures stacked on the base `render`/`configureApp` fixtures. See [Compose a router and a domain fixture](#compose-a-router-and-a-domain-fixture) below |
30
- | Synchronous assertions (`expect(screen.getByText(...))`) | Browser locators and `await expect.element(screen.getByText(...)).toBeVisible()` |
31
-
32
- ## Step 1: install Browser Mode dependencies
33
-
34
- ```sh
35
- pnpm add -D vitest playwright @vitest/browser-playwright vitest-browser-react \
36
- @equinor/fusion-framework-vitest-plugin-react-app
37
- pnpm exec playwright install chromium
38
- ```
39
-
40
- Remove `@testing-library/react`, `@testing-library/dom`, `@testing-library/user-event`, and
41
- whichever `jsdom`/`happy-dom` package the app installed once every test that used them has
42
- migrated. Keep them if part of the suite intentionally stays on the old renderer.
43
-
44
- Replace the app's `vitest.config.ts` with the plugin's project config:
45
-
46
- ```ts
47
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
48
-
49
- export default defineProject();
50
- ```
51
-
52
- `defineProject` registers `appTestVitePlugin`, enables headless Chromium, includes
53
- `src/**/*.{test,spec}.{ts,tsx}`, and warms application source so lazy route imports don't reload
54
- the browser mid-test. See [Configuration](configuration.md) for overriding any of that.
55
-
56
- ## Step 2: replace hand-rolled Fusion hook mocks
57
-
58
- The `/test` entry point initializes the real module pipeline. This removes most `vi.mock`
59
- implementations for hooks such as `useCurrentUser`, `useFeature`, and `useHttpClient`. The
60
- default setup includes a signed-in `Test User` and initialized feature-flag, context, and
61
- bookmark modules:
62
-
63
- ```diff
64
- -vi.mock('@equinor/fusion-framework-react/hooks', async (importOriginal) => ({
65
- - ...(await importOriginal()),
66
- - useCurrentUser: vi.fn(() => undefined),
67
- -}));
68
- -vi.mock('@equinor/fusion-framework-react-app/feature-flag', () => ({
69
- - useFeature: vi.fn(() => ({})),
70
- -}));
71
- +import { enableFeatureFlagMock } from '@equinor/fusion-framework-module-feature-flag/mock';
72
- +import { test as baseTest } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
73
- +
74
- +export const test = baseTest.extend('configureApp', ({ configureApp }) => (configurator, args) => {
75
- + configureApp?.(configurator, args);
76
- + configurator.msal.setAccount(null); // was: useCurrentUser returning undefined
77
- + enableFeatureFlagMock(configurator, (mock) => mock.addFeature({ key: 'new-search', enabled: true }));
78
- +});
79
- ```
80
-
81
- Each Fusion module owns its own mock entry point: `enableMsalMock`/`configurator.msal`,
82
- `enableFeatureFlagMock`, `enableContextMock`, `enableBookmarkMock`, and more. See
83
- [Module mocks](module-mocks.md) for the full list and each one's defaults. A hook or a small
84
- piece of app config with no Fusion dependency (for example, reading `environment` from the
85
- app's own manifest) stays a plain `vi.mock`.
86
-
87
- Some suites override `useModuleCurrentContext` directly and change its return value with
88
- `vi.spyOn`. Replace the initial value with `enableContextMock`. To change context after render,
89
- call the real module:
90
- `await fusion.app.context.setCurrentContextByIdAsync(otherProject.id)`. See the first
91
- [`renderAppComponent`](../README.md#renderappcomponent) example.
92
-
93
- ## Step 3: port the mock HTTP server
94
-
95
- Use `createRouterMiddleware` for a mock server that handles several routes under one base URL:
96
-
97
- ```diff
98
- -import { createServer } from 'miragejs';
99
- -
100
- -export function createTestServer() {
101
- - return createServer({
102
- - routes() {
103
- - this.get('/activities/:id', (schema, request) => ({ id: request.params.id, ... }));
104
- - this.post('/activities', (schema, request) => ({ ... }));
105
- - },
106
- - });
107
- -}
108
- +import { createRouterMiddleware } from '@equinor/fusion-framework-module-http/mock';
109
- +
110
- +export const activityRoutes = createRouterMiddleware('https://cpr-api.example.com', (router) => {
111
- + router.get('/activities/:id', ({ params }) => Response.json({ id: params.id /* ... */ }));
112
- + router.post('/activities', async ({ request }) => Response.json({ /* ... */ }));
113
- +});
114
- ```
115
-
116
- Register the result with `configurator.http.addMiddleware(activityRoutes)` inside a `configureApp`
117
- fixture (see step 4). Port one route file at a time. Each ported file is independently testable.
118
- An unported route can fall through to the real network, so keep the old interceptor
119
- for tests that have not migrated yet or add a final fail-closed middleware; do not rely on a
120
- missing route to fail by itself. If the backend has an OpenAPI document, prefer
121
- `createOpenApiMockMiddleware` (`@equinor/fusion-framework-module-http/mock`) over hand-written
122
- routes; see [HTTP testing](../../../modules/http/docs/testing.md).
123
-
124
- If `useHttpClient` currently returns a plain `fetch` wrapper, remove that hook mock as well. The
125
- real client reaches middleware registered with `configurator.http.addMiddleware`. Register the
126
- router for the base URI used by the real client.
127
-
128
- ## Compose a router and a domain fixture
129
-
130
- Use a `render` extension for JSX wrappers such as routers and app-owned React providers. Use a
131
- `configureApp` extension for Fusion module state. Stack both extensions when a test needs both:
132
-
133
- ```tsx
134
- import type { ReactElement } from 'react';
135
- import { test as baseTest } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
136
- import { Router } from '@equinor/fusion-framework-react-router';
137
- import type { RouteObject } from '@equinor/fusion-framework-react-router';
138
- import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
139
-
140
- // Every route matches, so the element under test stays mounted.
141
- const testWithRouter = baseTest.extend('render', () => (ui: ReactElement) => {
142
- const routes: RouteObject[] = [{ path: '*', Component: () => ui }];
143
- return baseTest.render(<Router routes={routes} />);
144
- });
145
-
146
- // The domain fixture uses the same shape as any other `configureApp` extension.
147
- export const test = testWithRouter.extend('configureApp', ({ configureApp }) => (configurator, args) => {
148
- configureApp?.(configurator, args);
149
- enableContextMock(configurator, (mock) => mock.setCurrentContext(projectA));
150
- });
151
- ```
152
-
153
- A test importing this `test` gets both the router wrapper and seeded Fusion context.
154
-
155
- Keep app-owned React providers, such as authorization, schema, or snackbar providers, in the
156
- `render` extension. Move only Fusion module state to `configureApp` and `enable*Mock`.
157
-
158
- ## Step 4: update renders and assertions
159
-
160
- Every render/hook helper is `async`, and most assertion helpers from `vitest-browser-react`
161
- resolve against the real browser rather than synchronously:
162
-
163
- ```diff
164
- -const { findByTestId } = render(<WrappedProcessPage {...props} />);
165
- -await findByTestId('customization-panel');
166
- +const { getByTestId } = await render(<WrappedProcessPage {...props} />);
167
- +await expect.element(getByTestId('customization-panel')).toBeVisible();
168
- ```
169
-
170
- `fireEvent`-style interactions become element method calls (`await screen.getByRole('button', {
171
- name: 'Activate' }).click()`), and `act`/`waitFor` come from `vitest` rather than
172
- `@testing-library/react`. See the [Vitest Browser Mode](https://vitest.dev/guide/browser/) and
173
- [mocking](https://vitest.dev/guide/mocking) guides for the general-purpose parts of this that
174
- aren't Fusion-specific.
175
-
176
- ## Step 5: reassess DOM-emulation-only workarounds
177
-
178
- Review polyfills and component replacements that may only exist for `jsdom` or `happy-dom`.
179
- Remove one workaround at a time and run the affected tests against the real component.
180
-
181
- A common one: a no-op `window.ResizeObserver` stub carried over from `jsdom`/`happy-dom` setup.
182
- Real Chromium doesn't need the stub, but leaving it in place silently breaks any component that
183
- measures itself through `ResizeObserver` before rendering — for example a virtualized popover
184
- list. Remove the stub rather than porting it.
185
-
186
- Do not remove unrelated suppressions automatically. AG Grid license messages, Lit dev-mode
187
- warnings, network behavior, and app-specific test doubles may still apply in Browser Mode.
188
-
189
- ## Migrate incrementally
190
-
191
- Nothing requires migrating a whole app in one pass. A common order: pick one page or route-level
192
- test that already hand-mocks several Fusion hooks, migrate it end to end (steps 1-4 above),
193
- confirm it's green, then migrate the next. Leave tests with no Fusion dependency on their
194
- current renderer for as long as that remains the cheaper option.
195
-
196
- ## Related documentation
197
-
198
- - [Choose a Fusion testing layer](../../../framework/docs/testing-choosing-a-layer.md)
199
- - [Module mocks](module-mocks.md)
200
- - [HTTP testing](../../../modules/http/docs/testing.md)
201
- - [Advanced usage](advanced.md): composing fixtures and explicit render options
202
- - [Troubleshooting](troubleshooting.md)