@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
@@ -1,120 +0,0 @@
1
- # Mock Fusion dependencies in app tests
2
-
3
- Use module-owned `/mock` entry points to seed deterministic Fusion state while retaining each
4
- module's real configurator, provider, validation, and lifecycle. Import only the boundary the
5
- app test needs; the React app test fixture already composes the normal framework and app
6
- module scopes.
7
-
8
- | App dependency | Import path | Primary API |
9
- | --- | --- | --- |
10
- | Analytics | `@equinor/fusion-framework-module-analytics/mock` | `MockAnalyticsAdapter` |
11
- | App manifests and config | `@equinor/fusion-framework-module-app/mock` | `MockAppClient` |
12
- | Browser authentication | `@equinor/fusion-framework-module-msal/mock` | `enableMsalMock`, `MsalMockClient`, `createMockToken` |
13
- | Node authentication with Azure Identity | `@equinor/fusion-framework-module-azure-identity/mock` | `enableAuthMock`, `MockAuthProvider` |
14
- | Node authentication with MSAL | `@equinor/fusion-framework-module-msal-node/mock` | `enableAuthMock`, `MockAuthProvider` |
15
- | Bookmarks | `@equinor/fusion-framework-module-bookmark/mock` | `enableBookmarkMock`, `BookmarkMockConfigurator` |
16
- | Context | `@equinor/fusion-framework-module-context/mock` | `enableContextMock`, `ContextMockConfigurator` |
17
- | Context fixture generation | `@equinor/fusion-framework-module-context/mock/fixtures` | `createContextItemFactory`, `createContextItems` |
18
- | Feature flags | `@equinor/fusion-framework-module-feature-flag/mock` | `enableFeatureFlagMock`, `FeatureFlagMockConfigurator` |
19
- | HTTP routes and OpenAPI responses | `@equinor/fusion-framework-module-http/mock` | `createRouterMiddleware`, `createOpenApiMockMiddleware` |
20
- | Service discovery | `@equinor/fusion-framework-module-service-discovery/mock` | `enableServiceDiscoveryMock`, `ServiceDiscoveryMockConfigurator` |
21
- | Telemetry | `@equinor/fusion-framework-module-telemetry/mock` | `enableTelemetryMock`, `MockTelemetryAdapter` |
22
-
23
- The event module has no mock entry point. `waitForEvent` and `watchEvents` from
24
- `@equinor/fusion-framework-module-event/utils` observe the real event provider initialized by
25
- the app test fixture.
26
-
27
- ## Authentication
28
-
29
- App tests start with a deterministic signed-in `Test User`. Set a named account or `null`
30
- through `configurator.msal.setAccount(...)` before rendering. The real MSAL provider still
31
- runs its initialization, account, token, and logout behavior against the in-process client.
32
-
33
- See [MSAL testing](../../../modules/msal/docs/testing.md) for signed-out startup, login flows,
34
- deterministic JWTs, and individual client spies.
35
-
36
- ## App manifest and configuration
37
-
38
- The `/test` entry point automatically resolves the current app's manifest, `app.config.ts`,
39
- and module configurator. Use `enableAppManifestMock` when a custom parent framework must serve
40
- those values, or `mockAppModules` for app module testing without React.
41
-
42
- See [Application module testing](../../../app/docs/testing.md).
43
-
44
- ## Context
45
-
46
- Use `enableContextMock` for known domain state without a context API or HTTP setup. The pool
47
- starts empty; `setCurrentContext` seeds and selects one item, while `setContexts` only makes
48
- items resolvable. Use HTTP middleware instead when the context service and transport pipeline
49
- are part of the test.
50
-
51
- See [Context testing](../../../modules/context/README.md#testing) for defaults, related-context
52
- behavior, and deterministic fixture generators.
53
-
54
- ## Service discovery and HTTP
55
-
56
- Use `enableServiceDiscoveryMock` to resolve services from an in-memory registry. Use
57
- `configurator.http.addMiddleware` to answer a few explicit requests through the app's real
58
- named clients. Use `createOpenApiMockMiddleware` when an OpenAPI document describes many
59
- operations.
60
-
61
- - [Service discovery testing](../../../modules/service-discovery/docs/testing.md)
62
- - [HTTP testing](../../../modules/http/docs/testing.md)
63
- - [OpenAPI mock](../../../utils/openapi-mock/README.md)
64
-
65
- An HTTP middleware handles only requests for which it returns a `Response`. Calling
66
- `next(uri, init)` delegates to the next middleware and eventually the real network.
67
-
68
- ## Events, analytics, and telemetry
69
-
70
- - Observe the real event provider with `waitForEvent` or `watchEvents` from
71
- `@equinor/fusion-framework-module-event/utils`.
72
- - Register `MockAnalyticsAdapter` to record analytics without exporting them.
73
- - Use `enableTelemetryMock` and assert through `MockTelemetryAdapter.getItems` or
74
- `waitForItem`.
75
-
76
- - [Event testing utilities](../../../modules/event/docs/testing.md)
77
- - [Analytics testing](../../../modules/analytics/docs/testing.md)
78
- - [Telemetry testing](../../../modules/telemetry/README.md#testing)
79
-
80
- ## Seed several app dependencies
81
-
82
- Compose the app's real configuration with the module mocks required by the scenario:
83
-
84
- ```tsx
85
- import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
86
- import { enableFeatureFlagMock } from '@equinor/fusion-framework-module-feature-flag/mock';
87
- import { test as baseTest } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
88
-
89
- const project = {
90
- id: 'project-a',
91
- title: 'Project A',
92
- type: { id: 'ProjectMaster' },
93
- value: {},
94
- };
95
-
96
- export const test = baseTest.extend('configureApp', ({ configureApp }) => (configurator, args) => {
97
- configureApp?.(configurator, args);
98
- configurator.msal.setAccount({ name: 'Ada Lovelace' });
99
- enableContextMock(configurator, (mock) => mock.setCurrentContext(project));
100
- enableFeatureFlagMock(configurator, (mock) => {
101
- mock.addFeature({ key: 'new-search', enabled: true });
102
- });
103
- });
104
- ```
105
-
106
- The fixture declarations are reusable, while every test still receives fresh framework and
107
- app module instances.
108
-
109
- ## Choose direct module mocks or HTTP
110
-
111
- - Use a module mock for known domain state such as one current context, one account, or a set
112
- of feature flags.
113
- - Use `configurator.http.addMiddleware` when the test must exercise the module's production
114
- service-discovery, HTTP-client, and response-handling pipeline.
115
- - Use `createOpenApiMockMiddleware` when an OpenAPI document describes many operations.
116
- - Use Vitest `vi.fn`, `vi.spyOn`, or `vi.mock` for individual JavaScript calls rather than a
117
- Fusion-specific wrapper.
118
-
119
- See each owning package README or `docs/testing.md` for its defaults, builder methods, and
120
- failure behavior.
package/docs/overview.md DELETED
@@ -1,49 +0,0 @@
1
- # Test Fusion React apps
2
-
3
- Fusion Framework uses Vitest for application tests. Vitest shares Vite's module and
4
- configuration model, runs components in a real browser, and owns the test runner, assertions,
5
- fixtures, spies, coverage, and editor integration. Fusion provides only the application scope
6
- and deterministic platform boundaries needed to test production behavior.
7
-
8
- Use `@equinor/fusion-framework-vitest-plugin-react-app` when a React hook, component, route,
9
- or complete app consumes Fusion modules. The package resolves app files, initializes the real
10
- framework and app provider nesting, and substitutes supported external clients without
11
- introducing a second testing language.
12
-
13
- ## Choose the subject
14
-
15
- | Subject | Start with | What the test proves |
16
- | --- | --- | --- |
17
- | Pure function or framework-independent hook | Standard Vitest | Local behavior without a Fusion runtime |
18
- | Hook that consumes Fusion app modules | `renderAppHook` | Hook behavior inside an initialized app scope |
19
- | Component that consumes Fusion app modules | `/test` `render` fixture or `renderAppComponent` | Rendered behavior with real providers |
20
- | Route or complete application | `/test` `test` and `render` | App configuration, navigation, loaders, and module lifecycle together |
21
- | Framework or module integration without React | Package `/mock` entry points | Production provider behavior with external boundaries substituted |
22
-
23
- Prefer the smallest layer containing the behavior under assertion. A component test should
24
- not recreate a portal, while an app lifecycle test should not replace every Fusion hook with
25
- a JavaScript module mock.
26
-
27
- ## Start here
28
-
29
- Follow [Getting started](getting-started.md) to install the browser dependencies, configure
30
- Vitest, and run one deterministic app test without credentials, a portal, or backend services.
31
-
32
- ## Continue by task
33
-
34
- - [Configure Vitest defaults and non-standard app files](configuration.md)
35
- - [Compose fixtures and explicit render options](advanced.md)
36
- - [Seed Fusion module dependencies](module-mocks.md)
37
- - [Troubleshoot browser, app-resolution, and network failures](troubleshooting.md)
38
- - [Understand the Browser Mode default or choose another renderer](why-browser-mode.md)
39
- - [Migrate an existing app from Testing Library and DOM emulation](migrating-an-existing-app.md)
40
- - [Choose between app, framework, module, and HTTP testing](../../../framework/docs/testing-choosing-a-layer.md)
41
-
42
- Use the official Vitest documentation for generic runner concepts:
43
-
44
- - [Why Vitest](https://vitest.dev/guide/why)
45
- - [Writing tests](https://vitest.dev/guide/learn/writing-tests)
46
- - [Browser Mode](https://vitest.dev/guide/browser/)
47
- - [Test context and fixtures](https://vitest.dev/guide/test-context)
48
- - [Configuration](https://vitest.dev/config/)
49
- - [Mocking](https://vitest.dev/guide/mocking)
@@ -1,67 +0,0 @@
1
- # Troubleshoot Fusion React app tests
2
-
3
- Use this guide when a Fusion app test fails before the assertion, loads real services, cannot
4
- resolve the app, or behaves differently in Vitest Browser Mode.
5
-
6
- ## Common symptoms
7
-
8
- | Symptom | Likely cause | Resolution |
9
- | --- | --- | --- |
10
- | Chromium executable is missing | Playwright installed without its browser binary | Run `pnpm exec playwright install chromium` |
11
- | The `/test` import cannot resolve Fusion virtual modules | `appTestVitePlugin` is not registered in this Vitest project | Export `defineProject()` from the package's `/config` entry point or register `appTestVitePlugin()` manually |
12
- | `FileNotFoundError` names a manifest, config, or configurator | An explicit plugin path does not exist | Correct the path relative to the Vitest project root; explicit paths fail instead of falling back |
13
- | The app's module configuration is not applied | No conventional `src/config.ts(x|js)` file was found | Pass `configure` to `appTestVitePlugin` or override the `configure` fixture |
14
- | A render result is a Promise or assertions run before initialization | A Fusion render helper was not awaited | Await `render`, `renderHook`, `renderAppComponent`, or `renderAppHook` |
15
- | A request reaches a live backend or fails with a network error | No HTTP middleware answered it | Add a matching `configurator.http.addMiddleware`; unmatched requests eventually reach the real network |
16
- | A lazy route import reloads the browser during a test | Source warmup no longer covers the lazy module | Keep `server.warmup.clientFiles` aligned with application source and route locations |
17
- | No tests are found | Tests are outside the default `src/**/*.{test,spec}.{ts,tsx}` pattern | Override `test.include` in `defineProject` |
18
- | The app starts signed in unexpectedly | The MSAL mock defaults to `Test User` | Set `configurator.msal.setAccount(null)` before rendering |
19
- | State configured in one test is missing in another | Framework and app fixtures are test-scoped | Seed the required state per test or publish reusable fixture declarations with `test.extend` |
20
- | `vi.spyOn` fails on an imported module in Browser Mode | Native ESM module namespace objects are sealed | Use `vi.mock('./module.js', { spy: true })` as documented by Vitest Browser Mode |
21
- | A `<Router>` route with a catch-all or parameterized `path` never renders; every query on it times out | The navigation module's current location does not match that path yet | Push the target location through the navigation module (`app.modules.navigation.push(path)`) before rendering, not after |
22
- | A virtualized list or popover (for example an EDS `Autocomplete` built on `@tanstack/react-virtual`) never renders its rows | `window.ResizeObserver` was stubbed as a no-op in test setup | Remove the stub; real Chromium's `ResizeObserver` is required for virtualized layout to measure correctly |
23
- | `test.each` runs but a fixture in its per-case callback is `undefined` | `test.each` does not forward `test.extend` fixture context to each case | Use Vitest's `test.for` instead, which does receive fixture context |
24
-
25
- ## Verify the project wiring
26
-
27
- Start with the smallest configuration:
28
-
29
- ```ts
30
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
31
-
32
- export default defineProject();
33
- ```
34
-
35
- Then verify the fixture itself before testing application behavior:
36
-
37
- ```tsx
38
- import { expect } from 'vitest';
39
- import { test } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
40
-
41
- test('initializes a Fusion app scope', async ({ app, render }) => {
42
- expect(app.auth.account?.name).toBe('Test User');
43
- const screen = await render(<div>ready</div>);
44
- await expect.element(screen.getByText('ready')).toBeVisible();
45
- });
46
- ```
47
-
48
- If this test fails, fix project or app-file resolution before adding module-specific data.
49
-
50
- ## Keep tests offline
51
-
52
- Fusion mocks replace known external boundaries, but an HTTP middleware is an ordered chain.
53
- Returning `next(uri, init)` delegates to the next middleware and eventually `fetch`. A test
54
- that must never use the network should register middleware for every expected request and
55
- fail deliberately on unexpected requests.
56
-
57
- Use `createOpenApiMockMiddleware` from
58
- `@equinor/fusion-framework-module-http/mock` when an OpenAPI document describes many routes.
59
- Use `vi.fn` around a middleware when the test also needs call assertions.
60
-
61
- ## Related references
62
-
63
- - [Getting started](getting-started.md)
64
- - [Configuration and app-file resolution](configuration.md)
65
- - [Choose a Fusion testing layer](../../../framework/docs/testing-choosing-a-layer.md)
66
- - [HTTP middleware testing](../../../modules/http/docs/testing.md)
67
- - [Vitest Browser Mode limitations](https://vitest.dev/guide/browser/#limitations)
@@ -1,128 +0,0 @@
1
- # Why Browser Mode is the default
2
-
3
- `@equinor/fusion-framework-vitest-plugin-react-app` runs component and hook tests in headless
4
- Chromium by default. A real browser gives these tests more accurate DOM behavior, but it is
5
- slower than `happy-dom` or `jsdom`.
6
-
7
- This guide explains that tradeoff and shows how to use a different browser or renderer when a
8
- test does not need Chromium.
9
-
10
- ## Performance
11
-
12
- A real browser is slower than DOM emulation. `happy-dom` and `jsdom` run in the same Node process
13
- as the test. Browser Mode starts Chromium through Playwright and communicates with that browser
14
- process.
15
-
16
- Choose Browser Mode for fidelity, not speed. Keep fast unit tests on Node or DOM emulation when
17
- they do not need browser behavior.
18
-
19
- ## Why use a real browser
20
-
21
- - **React compatibility:** the previous React test setup failed after its React 19 peer
22
- dependency update. The repository then moved those tests to Vitest Browser Mode. See the
23
- `packages/utils/observable` 8.4.4 changelog and migration commit `059aefae5d`.
24
- - **Real framework execution:** tests control external boundaries and seed module state through
25
- [Module mocks](module-mocks.md), while the actual module configuration, lifecycle, providers,
26
- and rendering behavior still run.
27
- - **Browser fidelity:** Chromium provides real layout, `ResizeObserver`, custom elements, and
28
- Shadow DOM — behavior `happy-dom` and `jsdom` only approximate.
29
-
30
- > [!WARNING]
31
- > A DOM-emulation gap is usually patched one of two ways: a polyfill standing in for the missing
32
- > browser API, or a component replacement that avoids exercising it at all. Both are test-only
33
- > code with no equivalent in production, and both defeat the point of the test: a polyfill can
34
- > drift from real browser behavior unnoticed, and a mocked component only proves the *mock*
35
- > renders correctly, never the real one. A passing test built on either can still fail — or
36
- > silently lie — against the real component. Treat every DOM-emulation workaround as debt to
37
- > remove, not a pattern to reach for.
38
-
39
- A real browser does not eliminate every workaround: AG Grid license messages and Lit dev-mode
40
- warnings are console noise from real Chromium runs, not DOM-emulation gaps, and suppressing them
41
- carries none of the risk above (see [Module mocks](module-mocks.md) for the supported way to
42
- seed module state instead of replacing a component). Remove a DOM-emulation-only workaround only
43
- after confirming the affected test still passes against the real component in Browser Mode.
44
-
45
- ## Choose a different runtime
46
-
47
- You can change either the browser provider or the renderer:
48
-
49
- - Stay in Browser Mode and replace Playwright with another Vitest browser provider.
50
- - Keep the Fusion module setup but render with `happy-dom` or `jsdom`.
51
-
52
- ### Use another browser provider
53
-
54
- Pass `test.browser.provider` to `defineProject` to replace Playwright while staying in Browser
55
- Mode. This example uses WebdriverIO:
56
-
57
- ```ts
58
- import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
59
- import { webdriverio } from '@vitest/browser-webdriverio';
60
-
61
- export default defineProject({
62
- test: { browser: { provider: webdriverio() } },
63
- });
64
- ```
65
-
66
- See [Configuration](configuration.md) for the complete `defineProject` behavior.
67
-
68
- ### Use happy-dom or jsdom
69
-
70
- > [!CAUTION]
71
- > Rendering with `happy-dom` or `jsdom` reintroduces the DOM-emulation tradeoff above: any gap
72
- > between the emulation and a real browser has to be closed with a polyfill or a component
73
- > replacement, not fixed. Only take this path for a component or hook that provably does not
74
- > need browser fidelity — otherwise stay in Browser Mode.
75
-
76
- The Fusion module setup does not depend on Browser Mode. Build the same provider tree and pass it
77
- to another renderer. The example below uses `@testing-library/react` on `happy-dom`:
78
-
79
- ```tsx
80
- import { render } from '@testing-library/react';
81
- import { mockFramework } from '@equinor/fusion-framework/mock';
82
- import { enableAppManifestMock, mockAppModules } from '@equinor/fusion-framework-app/mock';
83
- import { FrameworkProvider } from '@equinor/fusion-framework-react';
84
- import { ModuleProvider } from '@equinor/fusion-framework-react-module';
85
- import type { AppModule } from '@equinor/fusion-framework-module-app';
86
- import type { ReactElement } from 'react';
87
-
88
- const env = {
89
- manifest: {
90
- appKey: 'test-app',
91
- displayName: 'Test App',
92
- description: 'Test app',
93
- type: 'standalone',
94
- },
95
- } as const;
96
-
97
- async function renderWithHappyDom(ui: ReactElement) {
98
- const framework = await mockFramework<[AppModule]>((configurator) =>
99
- enableAppManifestMock(configurator, env),
100
- );
101
- const app = await mockAppModules(undefined, env, framework);
102
- return render(ui, {
103
- wrapper: ({ children }) => (
104
- <FrameworkProvider value={framework}>
105
- <ModuleProvider value={app}>{children}</ModuleProvider>
106
- </FrameworkProvider>
107
- ),
108
- });
109
- }
110
- ```
111
-
112
- Run the test in a Vitest project with `environment: 'happy-dom'` instead of
113
- `browser.enabled`. Use a separate project if the same app also has Browser Mode tests.
114
-
115
- Call the same `enable*Mock` functions from the `mockAppModules` configuration callback. These
116
- mocks configure Fusion modules and do not depend on the renderer. `renderHook` from
117
- `@testing-library/react` can use the same wrapper for hook tests.
118
-
119
- Use Browser Mode when browser behavior matters. Use `happy-dom`, `jsdom`, or no DOM for focused
120
- tests that only need application logic or simple rendering.
121
-
122
- ## Related documentation
123
-
124
- - [Overview](overview.md)
125
- - [Configuration](configuration.md)
126
- - [Module mocks](module-mocks.md)
127
- - [Migrate an existing app to Fusion Vitest](migrating-an-existing-app.md)
128
- - [Choose a Fusion testing layer](../../../framework/docs/testing-choosing-a-layer.md)
@@ -1,109 +0,0 @@
1
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { appTestVitePlugin } from '../index.js';
8
-
9
- type ResolveIdHook = (id: string) => string | null | undefined;
10
- type LoadHook = (id: string) => string | null | undefined | Promise<string | null | undefined>;
11
- type ConfigResolvedHook = (config: { root: string }) => void;
12
-
13
- describe('appTestVitePlugin', () => {
14
- let dir: string;
15
-
16
- beforeEach(async () => {
17
- dir = await mkdtemp(join(tmpdir(), 'fusion-vitest-plugin-react-app-'));
18
- await writeFile(
19
- join(dir, 'package.json'),
20
- JSON.stringify({ name: '@equinor/my-app', version: '1.2.3' }),
21
- );
22
- });
23
-
24
- afterEach(async () => {
25
- await rm(dir, { recursive: true, force: true });
26
- });
27
-
28
- it('claims only its two virtual specifiers, leaving everything else unresolved', () => {
29
- const plugin = appTestVitePlugin({ entrypoint: dir });
30
- const resolveId = plugin.resolveId as ResolveIdHook;
31
-
32
- expect(resolveId('virtual:fusion-app-test-env')).toBe('\0virtual:fusion-app-test-env');
33
- expect(resolveId('virtual:fusion-app-test-configure')).toBe(
34
- '\0virtual:fusion-app-test-configure',
35
- );
36
- expect(resolveId('some-other-module')).toBeNull();
37
- });
38
-
39
- it('serves the resolved manifest/config as the env virtual module', async () => {
40
- const plugin = appTestVitePlugin({ entrypoint: dir });
41
- const load = plugin.load as LoadHook;
42
-
43
- const source = await load('\0virtual:fusion-app-test-env');
44
-
45
- expect(source).toContain('"appKey":"my-app"');
46
- expect(source).toContain('export const config = {"environment":{}};');
47
- });
48
-
49
- it('serves an undefined configure export when no config file exists', async () => {
50
- const plugin = appTestVitePlugin({ entrypoint: dir });
51
- const load = plugin.load as LoadHook;
52
-
53
- const source = await load('\0virtual:fusion-app-test-configure');
54
-
55
- expect(source).toBe('export const configure = undefined;');
56
- });
57
-
58
- it('resolves the app from the Vitest project root when no entrypoint is provided', async () => {
59
- await mkdir(join(dir, 'src'), { recursive: true });
60
- await writeFile(join(dir, 'src', 'index.ts'), 'export {};');
61
- await writeFile(join(dir, 'src', 'config.ts'), 'export default () => undefined;');
62
- const plugin = appTestVitePlugin();
63
- const configResolved = plugin.configResolved as ConfigResolvedHook;
64
- const load = plugin.load as LoadHook;
65
-
66
- configResolved({ root: dir });
67
-
68
- expect(await load('\0virtual:fusion-app-test-env')).toContain('"appKey":"my-app"');
69
- expect(await load('\0virtual:fusion-app-test-configure')).toBe(
70
- `export { default as configure } from ${JSON.stringify(join(dir, 'src', 'config.ts'))};`,
71
- );
72
- });
73
-
74
- it('re-exports the default candidate config file as the configure virtual module', async () => {
75
- await mkdir(join(dir, 'src'), { recursive: true });
76
- await writeFile(join(dir, 'src', 'config.ts'), 'export default () => undefined;');
77
- const plugin = appTestVitePlugin({ entrypoint: dir });
78
- const load = plugin.load as LoadHook;
79
-
80
- const source = await load('\0virtual:fusion-app-test-configure');
81
-
82
- expect(source).toBe(
83
- `export { default as configure } from ${JSON.stringify(join(dir, 'src', 'config.ts'))};`,
84
- );
85
- });
86
-
87
- it('re-exports an explicitly requested configure file', async () => {
88
- await writeFile(join(dir, 'my-config.ts'), 'export default () => undefined;');
89
- const plugin = appTestVitePlugin({ entrypoint: dir, configure: 'my-config.ts' });
90
- const load = plugin.load as LoadHook;
91
-
92
- const source = await load('\0virtual:fusion-app-test-configure');
93
-
94
- expect(source).toBe(
95
- `export { default as configure } from ${JSON.stringify(join(dir, 'my-config.ts'))};`,
96
- );
97
- });
98
-
99
- it('throws when an explicitly requested configure file does not exist', () => {
100
- expect(() => appTestVitePlugin({ entrypoint: dir, configure: 'missing.ts' })).toThrow();
101
- });
102
-
103
- it('returns null for module ids other than its own virtual specifiers', async () => {
104
- const plugin = appTestVitePlugin({ entrypoint: dir });
105
- const load = plugin.load as LoadHook;
106
-
107
- expect(await load('some-other-module')).toBeNull();
108
- });
109
- });
@@ -1,67 +0,0 @@
1
- import { AppConfig } from '@equinor/fusion-framework-module-app';
2
- import type { AppEnv } from '@equinor/fusion-framework-app';
3
- import { describe, expect, it } from 'vitest';
4
-
5
- import { mergeEnvConfig } from '../merge-env-config.js';
6
-
7
- describe('mergeEnvConfig', () => {
8
- const baseEnv: AppEnv = {
9
- manifest: {
10
- appKey: 'test-app',
11
- displayName: 'Test App',
12
- description: 'A test application',
13
- type: 'standalone',
14
- },
15
- config: new AppConfig({
16
- environment: { foo: 'bar' },
17
- endpoints: { api: { url: 'https://api.example.com', scopes: ['api.read'] } },
18
- }),
19
- };
20
-
21
- it('adds a new endpoint without dropping existing ones', () => {
22
- const merged = mergeEnvConfig(baseEnv, {
23
- endpoints: { 'cpr-api': { url: 'https://cpr.example.com' } },
24
- });
25
-
26
- expect(merged.config?.endpoints).toEqual({
27
- api: { url: 'https://api.example.com', scopes: ['api.read'] },
28
- 'cpr-api': { url: 'https://cpr.example.com', scopes: [] },
29
- });
30
- });
31
-
32
- it('overrides one field of an existing endpoint, keeping the rest', () => {
33
- const merged = mergeEnvConfig(baseEnv, {
34
- endpoints: { api: { url: 'https://fake.example.com' } },
35
- });
36
-
37
- expect(merged.config?.endpoints.api).toEqual({
38
- url: 'https://fake.example.com',
39
- scopes: ['api.read'],
40
- });
41
- });
42
-
43
- it('merges environment overrides over the existing environment', () => {
44
- const merged = mergeEnvConfig(baseEnv, { environment: { baz: 'qux' } });
45
-
46
- expect(merged.config?.environment).toEqual({ foo: 'bar', baz: 'qux' });
47
- });
48
-
49
- it('leaves the original env and its config untouched', () => {
50
- mergeEnvConfig(baseEnv, { endpoints: { api: { url: 'https://fake.example.com' } } });
51
-
52
- expect(baseEnv.config?.endpoints.api).toEqual({
53
- url: 'https://api.example.com',
54
- scopes: ['api.read'],
55
- });
56
- });
57
-
58
- it('handles an env with no existing config', () => {
59
- const env: AppEnv = { manifest: baseEnv.manifest };
60
- const merged = mergeEnvConfig(env, { endpoints: { api: { url: 'https://api.example.com' } } });
61
-
62
- expect(merged.config?.endpoints).toEqual({
63
- api: { url: 'https://api.example.com', scopes: [] },
64
- });
65
- expect(merged.config?.environment).toEqual({});
66
- });
67
- });
@@ -1,83 +0,0 @@
1
- import { mkdtemp, rm, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { resolveAppTestEnv } from '../resolve-app-test-env.js';
8
-
9
- describe('resolveAppTestEnv', () => {
10
- let dir: string;
11
-
12
- beforeEach(async () => {
13
- dir = await mkdtemp(join(tmpdir(), 'fusion-vitest-plugin-react-app-'));
14
- await writeFile(
15
- join(dir, 'package.json'),
16
- JSON.stringify({ name: '@equinor/my-app', version: '1.2.3', description: 'a test app' }),
17
- );
18
- });
19
-
20
- afterEach(async () => {
21
- await rm(dir, { recursive: true, force: true });
22
- });
23
-
24
- it('falls back to a package-derived manifest and an empty config with no local files', async () => {
25
- const { manifest, config } = await resolveAppTestEnv({ entrypoint: dir });
26
-
27
- expect(manifest).toMatchObject({
28
- appKey: 'my-app',
29
- displayName: '@equinor/my-app',
30
- description: 'a test app',
31
- type: 'standalone',
32
- });
33
- expect(config).toEqual({ environment: {} });
34
- });
35
-
36
- it('merges a local app.manifest.ts and app.config.ts, same as ffc app build', async () => {
37
- await writeFile(
38
- join(dir, 'app.manifest.ts'),
39
- "export default { displayName: 'My Custom App' };",
40
- );
41
- await writeFile(
42
- join(dir, 'app.config.ts'),
43
- "export default { environment: {}, endpoints: { api: { url: 'https://example.com' } } };",
44
- );
45
-
46
- const { manifest, config } = await resolveAppTestEnv({ entrypoint: dir });
47
-
48
- expect(manifest).toMatchObject({ appKey: 'my-app', displayName: 'My Custom App' });
49
- expect(config).toEqual({
50
- environment: {},
51
- endpoints: { api: { url: 'https://example.com', scopes: [] } },
52
- });
53
- });
54
-
55
- it('applies an inline manifest/config function directly, same as a loaded file default export', async () => {
56
- const { manifest, config } = await resolveAppTestEnv({
57
- entrypoint: dir,
58
- manifest: () => ({ displayName: 'Inline App' }),
59
- config: () => ({
60
- environment: {},
61
- endpoints: { api: { url: 'https://inline.example.com' } },
62
- }),
63
- });
64
-
65
- expect(manifest).toMatchObject({ appKey: 'my-app', displayName: 'Inline App' });
66
- expect(config).toEqual({
67
- environment: {},
68
- endpoints: { api: { url: 'https://inline.example.com', scopes: [] } },
69
- });
70
- });
71
-
72
- it('propagates an error for an explicitly requested manifest file that does not exist', async () => {
73
- await expect(
74
- resolveAppTestEnv({ entrypoint: dir, manifest: 'does-not-exist.manifest.ts' }),
75
- ).rejects.toThrow();
76
- });
77
-
78
- it('propagates an error for an explicitly requested config file that does not exist', async () => {
79
- await expect(
80
- resolveAppTestEnv({ entrypoint: dir, config: 'does-not-exist.config.ts' }),
81
- ).rejects.toThrow();
82
- });
83
- });
package/src/app-test.ts DELETED
@@ -1,19 +0,0 @@
1
- export {
2
- renderAppHook,
3
- type RenderAppHookOptions,
4
- type RenderAppHookResult,
5
- } from './render-app-hook';
6
- export {
7
- renderAppComponent,
8
- type RenderAppComponentOptions,
9
- type RenderAppComponentResult,
10
- } from './render-app-component';
11
- export { testApp } from './test-app';
12
- export { mergeEnvConfig, type MergeEnvConfigOverrides } from './merge-env-config.js';
13
- export type { AppMockConfigureFn } from '@equinor/fusion-framework-app/mock';
14
-
15
- // `test`/`render` import virtual modules only served once `appTestVitePlugin`
16
- // (@equinor/fusion-framework-vitest-plugin-react-app) is registered — using any export from
17
- // this module requires the plugin registered in your `vitest.config.ts` `plugins`.
18
- export { test } from './test';
19
- export { render } from './render';