@equinor/fusion-framework-vitest-plugin-react-app 1.0.0 → 1.0.2
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/dist/esm/index.js +9 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/resolve-app-test-env.js +8 -2
- package/dist/esm/resolve-app-test-env.js.map +1 -1
- package/dist/esm/scope/resolve-fusion.js +22 -24
- package/dist/esm/scope/resolve-fusion.js.map +1 -1
- package/dist/esm/test-app.js +5 -8
- package/dist/esm/test-app.js.map +1 -1
- package/dist/esm/test.js +2 -1
- package/dist/esm/test.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/resolve-app-test-env.d.ts +3 -1
- package/dist/types/scope/resolve-fusion.d.ts +6 -7
- package/dist/types/test-app.d.ts +12 -11
- package/dist/types/test.d.ts +20 -9
- package/dist/types/version.d.ts +1 -1
- package/package.json +17 -19
- package/CHANGELOG.md +0 -81
- package/docs/advanced.md +0 -209
- package/docs/configuration.md +0 -112
- package/docs/getting-started.md +0 -66
- package/docs/migrating-an-existing-app.md +0 -202
- package/docs/module-mocks.md +0 -120
- package/docs/overview.md +0 -49
- package/docs/troubleshooting.md +0 -67
- package/docs/why-browser-mode.md +0 -128
- package/src/__tests__/app-test-vite-plugin.test.ts +0 -109
- package/src/__tests__/merge-env-config.test.ts +0 -67
- package/src/__tests__/resolve-app-test-env.test.ts +0 -83
- package/src/app-test.ts +0 -19
- package/src/define-project.ts +0 -91
- package/src/index.ts +0 -124
- package/src/merge-env-config.ts +0 -58
- package/src/render-app-component.tsx +0 -116
- package/src/render-app-hook.tsx +0 -135
- package/src/render.tsx +0 -64
- package/src/resolve-app-test-env.ts +0 -132
- package/src/scope/create-app-scope-wrapper.tsx +0 -26
- package/src/scope/default-app-env.ts +0 -13
- package/src/scope/index.ts +0 -4
- package/src/scope/resolve-app-scope.ts +0 -46
- package/src/scope/resolve-fusion.ts +0 -74
- package/src/test-app.tsx +0 -148
- package/src/test.tsx +0 -42
- package/src/version.ts +0 -2
- package/src/virtual-modules.d.ts +0 -12
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -10
package/src/merge-env-config.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { AppConfig } from '@equinor/fusion-framework-module-app';
|
|
2
|
-
import type { ConfigEnvironment } from '@equinor/fusion-framework-module-app';
|
|
3
|
-
import type { AppEnv } from '@equinor/fusion-framework-app';
|
|
4
|
-
|
|
5
|
-
type EndpointOverride = Partial<NonNullable<AppEnv['config']>['endpoints'][string]>;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Overrides accepted by {@link mergeEnvConfig}.
|
|
9
|
-
*/
|
|
10
|
-
export type MergeEnvConfigOverrides<TConfig extends ConfigEnvironment = ConfigEnvironment> = {
|
|
11
|
-
environment?: Partial<TConfig>;
|
|
12
|
-
endpoints?: Record<string, EndpointOverride>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Merges `environment`/`endpoints` overrides into an `AppEnv`'s `config`.
|
|
17
|
-
*
|
|
18
|
-
* @remarks
|
|
19
|
-
* `AppConfig` stores both behind private fields exposed only through getters, so
|
|
20
|
-
* `{ ...env.config, endpoints: {...} }` silently drops everything it doesn't explicitly
|
|
21
|
-
* restate — a plain object spread copies no own enumerable properties off an `AppConfig`
|
|
22
|
-
* instance. Reach for this instead of hand-rolling that merge in a test fixture.
|
|
23
|
-
*
|
|
24
|
-
* @template TEnv - The `AppEnv` shape being merged into.
|
|
25
|
-
* @param env - The `AppEnv` to merge overrides into; left untouched, `config` may be omitted.
|
|
26
|
-
* @param overrides - Partial `environment`/`endpoints` values, merged over any existing config.
|
|
27
|
-
* @returns A new `AppEnv` with a new `AppConfig` reflecting the merge.
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* const test = testApp.extend('appEnv', ({ appEnv }) =>
|
|
31
|
-
* mergeEnvConfig(appEnv, { endpoints: { 'cpr-api': { url: backendBaseUrl } } }),
|
|
32
|
-
* );
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export function mergeEnvConfig<TEnv extends AppEnv = AppEnv>(
|
|
36
|
-
env: TEnv,
|
|
37
|
-
overrides: MergeEnvConfigOverrides<
|
|
38
|
-
TEnv['config'] extends AppConfig<infer TConfig> ? TConfig : ConfigEnvironment
|
|
39
|
-
>,
|
|
40
|
-
): TEnv {
|
|
41
|
-
// each overridden endpoint keeps any field the caller didn't explicitly override
|
|
42
|
-
const endpoints = Object.entries(overrides.endpoints ?? {}).reduce(
|
|
43
|
-
// defaults, then any existing endpoint, then the override (override wins)
|
|
44
|
-
(acc, [key, override]) =>
|
|
45
|
-
Object.assign(acc, { [key]: Object.assign({ url: '', scopes: [] }, acc[key], override) }),
|
|
46
|
-
{ ...env.config?.endpoints },
|
|
47
|
-
);
|
|
48
|
-
// caller-supplied environment values win over the existing ones
|
|
49
|
-
return {
|
|
50
|
-
...env,
|
|
51
|
-
config: new AppConfig({
|
|
52
|
-
environment: { ...env.config?.environment, ...overrides.environment },
|
|
53
|
-
endpoints,
|
|
54
|
-
}),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export default mergeEnvConfig;
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import type { ReactElement } from 'react';
|
|
2
|
-
import { render } from 'vitest-browser-react';
|
|
3
|
-
import type { RenderOptions, RenderResult } from 'vitest-browser-react';
|
|
4
|
-
|
|
5
|
-
import type { AppMockConfigureFn } from '@equinor/fusion-framework-app/mock';
|
|
6
|
-
import type { AppEnv, AppModulesInstance } from '@equinor/fusion-framework-app';
|
|
7
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
8
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
9
|
-
|
|
10
|
-
import { resolveAppScope, createAppScopeWrapper } from './scope';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Options for {@link renderAppComponent}.
|
|
14
|
-
*
|
|
15
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
16
|
-
* @template TEnv - The application environment descriptor.
|
|
17
|
-
*/
|
|
18
|
-
export interface RenderAppComponentOptions<
|
|
19
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
20
|
-
TEnv extends AppEnv = AppEnv,
|
|
21
|
-
> extends Omit<RenderOptions, 'wrapper'> {
|
|
22
|
-
/** Configuration callback forwarded to {@link mockAppModules}. */
|
|
23
|
-
configure?: AppMockConfigureFn<TModules, TEnv>;
|
|
24
|
-
/** The application environment; defaults to a generic standalone test app. */
|
|
25
|
-
env?: TEnv;
|
|
26
|
-
/**
|
|
27
|
-
* The parent Fusion instance; defaults to a fresh {@link mockFramework} instance with
|
|
28
|
-
* this app's own manifest served. Pass one built beforehand to reuse a single instance
|
|
29
|
-
* across multiple render calls, or to pre-configure parent-level modules (e.g. `http`,
|
|
30
|
-
* `context`, `serviceDiscovery`, or `app` itself for a component that loads another app).
|
|
31
|
-
*/
|
|
32
|
-
fusion?: Fusion;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The result of {@link renderAppComponent}: the `vitest-browser-react` render result,
|
|
37
|
-
* plus the resolved application module scope and its parent Fusion instance.
|
|
38
|
-
*
|
|
39
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
40
|
-
*/
|
|
41
|
-
export interface RenderAppComponentResult<TModules extends Array<AnyModule> | unknown = unknown>
|
|
42
|
-
extends RenderResult {
|
|
43
|
-
/**
|
|
44
|
-
* The Fusion instances backing the rendered component, nested under this single key so
|
|
45
|
-
* `vitest-browser-react`'s own `RenderResult` fields stay free to evolve without ever
|
|
46
|
-
* colliding with it.
|
|
47
|
-
*/
|
|
48
|
-
fusion: {
|
|
49
|
-
/** The parent Fusion instance the component's `FrameworkProvider` was given. */
|
|
50
|
-
framework: Fusion;
|
|
51
|
-
/**
|
|
52
|
-
* The resolved application module instance backing the rendered component — the same
|
|
53
|
-
* instance a real app would read via `useAppModule`/`useAppModules`. Drive a module
|
|
54
|
-
* directly (e.g. `fusion.app.context.setCurrentContextByIdAsync(id)`) to exercise a
|
|
55
|
-
* state change after the initial render, then assert the component re-rendered accordingly.
|
|
56
|
-
*/
|
|
57
|
-
app: AppModulesInstance<TModules>;
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Renders a component inside a real, mock-backed application module scope.
|
|
63
|
-
*
|
|
64
|
-
* @remarks
|
|
65
|
-
* Wraps `vitest-browser-react`'s `render` with the same provider nesting
|
|
66
|
-
* `createComponent` uses in production — a `FrameworkProvider` (the parent Fusion
|
|
67
|
-
* instance, from `mockFramework`) around a `ModuleProvider` (this app's own modules,
|
|
68
|
-
* from `mockAppModules`, `@equinor/fusion-framework-app/mock`). See {@link renderAppHook}
|
|
69
|
-
* for the equivalent helper when testing a hook in isolation, rather than a component.
|
|
70
|
-
*
|
|
71
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
72
|
-
* @template TEnv - The application environment descriptor.
|
|
73
|
-
* @param ui - The component to render.
|
|
74
|
-
* @param options - A `configure` callback and `env` for `mockAppModules`, plus any other `render` option.
|
|
75
|
-
* @returns The `render` result plus `fusion.framework` and `fusion.app`, once the mocked application module scope resolves.
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```tsx
|
|
79
|
-
* const { getByText } = await renderAppComponent(<Apploader appKey="child-app" />, {
|
|
80
|
-
* fusion: await mockFramework<[AppModule]>((configurator) => {
|
|
81
|
-
* // register the child app's own manifest against the `app` module
|
|
82
|
-
* }),
|
|
83
|
-
* });
|
|
84
|
-
* await expect.element(getByText(/mounted/)).toBeInTheDocument();
|
|
85
|
-
* ```
|
|
86
|
-
*
|
|
87
|
-
* @example Drive a module directly and assert the re-render
|
|
88
|
-
* ```tsx
|
|
89
|
-
* const { getByText, fusion } = await renderAppComponent<[ContextModule]>(<App />, {
|
|
90
|
-
* configure: (configurator) => enableContextMock(configurator, (mock) => mock.setCurrentContext(projectA)),
|
|
91
|
-
* });
|
|
92
|
-
* await fusion.app.context.setCurrentContextByIdAsync(projectB.id);
|
|
93
|
-
* await expect.element(getByText(/project-b/)).toBeInTheDocument();
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
export async function renderAppComponent<
|
|
97
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
98
|
-
TEnv extends AppEnv = AppEnv,
|
|
99
|
-
>(
|
|
100
|
-
ui: ReactElement,
|
|
101
|
-
options?: RenderAppComponentOptions<TModules, TEnv>,
|
|
102
|
-
): Promise<RenderAppComponentResult<TModules>> {
|
|
103
|
-
const { configure, env, fusion: providedFusion, ...renderOptions } = options ?? {};
|
|
104
|
-
const { framework, app } = await resolveAppScope<TModules, TEnv>({
|
|
105
|
-
configure,
|
|
106
|
-
env,
|
|
107
|
-
fusion: providedFusion,
|
|
108
|
-
});
|
|
109
|
-
const result = await render(ui, {
|
|
110
|
-
...renderOptions,
|
|
111
|
-
wrapper: createAppScopeWrapper<TModules>({ framework, app }),
|
|
112
|
-
});
|
|
113
|
-
return { ...result, fusion: { framework, app } };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export default renderAppComponent;
|
package/src/render-app-hook.tsx
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { renderHook } from 'vitest-browser-react';
|
|
2
|
-
import type { RenderHookOptions, RenderHookResult } from 'vitest-browser-react';
|
|
3
|
-
|
|
4
|
-
import type { AppMockConfigureFn } from '@equinor/fusion-framework-app/mock';
|
|
5
|
-
import type { AppEnv, AppModulesInstance } from '@equinor/fusion-framework-app';
|
|
6
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
7
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
8
|
-
|
|
9
|
-
import { resolveAppScope, createAppScopeWrapper } from './scope';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Options for {@link renderAppHook}.
|
|
13
|
-
*
|
|
14
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
15
|
-
* @template TEnv - The application environment descriptor.
|
|
16
|
-
* @template Props - The props type accepted by the rendered hook.
|
|
17
|
-
*/
|
|
18
|
-
export interface RenderAppHookOptions<
|
|
19
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
20
|
-
TEnv extends AppEnv = AppEnv,
|
|
21
|
-
Props = undefined,
|
|
22
|
-
> extends Omit<RenderHookOptions<Props>, 'wrapper'> {
|
|
23
|
-
/** Configuration callback forwarded to {@link mockAppModules}. */
|
|
24
|
-
configure?: AppMockConfigureFn<TModules, TEnv>;
|
|
25
|
-
/** The application environment; defaults to a generic standalone test app. */
|
|
26
|
-
env?: TEnv;
|
|
27
|
-
/**
|
|
28
|
-
* The parent Fusion instance; defaults to a fresh {@link mockFramework} instance with
|
|
29
|
-
* this app's own manifest served. Pass one built beforehand to reuse a single instance
|
|
30
|
-
* across multiple `renderAppHook` calls, or to pre-configure parent-level modules (e.g.
|
|
31
|
-
* `http`, `context`, `serviceDiscovery`) the app reads through `useFramework`.
|
|
32
|
-
*/
|
|
33
|
-
fusion?: Fusion;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The result of {@link renderAppHook}: the `vitest-browser-react` `renderHook` result,
|
|
38
|
-
* plus the resolved application module scope and its parent Fusion instance.
|
|
39
|
-
*
|
|
40
|
-
* @template Result - The value returned by the rendered hook.
|
|
41
|
-
* @template Props - The props accepted by the rendered hook.
|
|
42
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
43
|
-
*/
|
|
44
|
-
export interface RenderAppHookResult<
|
|
45
|
-
Result,
|
|
46
|
-
Props,
|
|
47
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
48
|
-
> extends RenderHookResult<Result, Props> {
|
|
49
|
-
/**
|
|
50
|
-
* The Fusion instances backing the rendered hook, nested under this single key so
|
|
51
|
-
* `vitest-browser-react`'s own `RenderHookResult` fields stay free to evolve without
|
|
52
|
-
* ever colliding with it.
|
|
53
|
-
*/
|
|
54
|
-
fusion: {
|
|
55
|
-
/** The parent Fusion instance the hook's `FrameworkProvider` was given. */
|
|
56
|
-
framework: Fusion;
|
|
57
|
-
/**
|
|
58
|
-
* The resolved application module instance backing the rendered hook — the same
|
|
59
|
-
* instance a real app would read via `useAppModule`/`useAppModules`. Drive a module
|
|
60
|
-
* not returned by the hook itself (e.g. `fusion.app.context.setCurrentContextByIdAsync(id)`)
|
|
61
|
-
* to exercise a state change after the initial render.
|
|
62
|
-
*/
|
|
63
|
-
app: AppModulesInstance<TModules>;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Renders a hook inside a real, mock-backed application module scope.
|
|
69
|
-
*
|
|
70
|
-
* @remarks
|
|
71
|
-
* Wraps `vitest-browser-react`'s `renderHook` with the same provider nesting
|
|
72
|
-
* `createComponent` uses in production — a `FrameworkProvider` (the parent Fusion
|
|
73
|
-
* instance, from `mockFramework`) around a `ModuleProvider` (this app's own modules,
|
|
74
|
-
* from `mockAppModules`, `@equinor/fusion-framework-app/mock`) — the real
|
|
75
|
-
* `event`/`http`/`msal` module pipeline. Only requests a seeded middleware answers are
|
|
76
|
-
* faked; a request with no matching middleware still reaches the real network. Use this
|
|
77
|
-
* for any hook that reads from the application module scope or the parent framework
|
|
78
|
-
* (e.g. `useAppModule`, `useAccessToken`, `useFramework`), instead of hand-wiring
|
|
79
|
-
* `mockFramework`, `mockAppModules`, `FrameworkProvider` and `ModuleProvider` in every test.
|
|
80
|
-
*
|
|
81
|
-
* @template Result - The value returned by the rendered hook.
|
|
82
|
-
* @template Props - The props accepted by the rendered hook.
|
|
83
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
84
|
-
* @template TEnv - The application environment descriptor.
|
|
85
|
-
* @param render - The hook to render, receiving `initialProps`.
|
|
86
|
-
* @param options - A `configure` callback and `env` for `mockAppModules`, plus any other
|
|
87
|
-
* `renderHook` option.
|
|
88
|
-
* @returns The `renderHook` result plus `fusion.framework` and `fusion.app`, once the mocked application module scope resolves.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```tsx
|
|
92
|
-
* const { result } = await renderAppHook(() => useAccessToken({ scopes: ['User.Read'] }));
|
|
93
|
-
* await vi.waitFor(() => expect(result.current.pending).toBe(false));
|
|
94
|
-
* ```
|
|
95
|
-
*
|
|
96
|
-
* @example Sign in a named user
|
|
97
|
-
* ```tsx
|
|
98
|
-
* const { result } = await renderAppHook(() => useCurrentAccount(), {
|
|
99
|
-
* configure: (configurator) => configurator.msal.setAccount({ name: 'Ada Lovelace' }),
|
|
100
|
-
* });
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* @example Reuse a pre-built parent Fusion instance
|
|
104
|
-
* ```tsx
|
|
105
|
-
* const fusion = await mockFramework<[AppModule]>((configurator) =>
|
|
106
|
-
* enableAppManifestMock(configurator, env),
|
|
107
|
-
* );
|
|
108
|
-
*
|
|
109
|
-
* const { result: a } = await renderAppHook(() => useAccessToken({ scopes: ['User.Read'] }), { fusion });
|
|
110
|
-
* const { result: b } = await renderAppHook(() => useCurrentAccount(), { fusion });
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export async function renderAppHook<
|
|
114
|
-
Result,
|
|
115
|
-
Props = undefined,
|
|
116
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
117
|
-
TEnv extends AppEnv = AppEnv,
|
|
118
|
-
>(
|
|
119
|
-
render: (initialProps?: Props) => Result,
|
|
120
|
-
options?: RenderAppHookOptions<TModules, TEnv, Props>,
|
|
121
|
-
): Promise<RenderAppHookResult<Result, Props, TModules>> {
|
|
122
|
-
const { configure, env, fusion: providedFusion, ...renderHookOptions } = options ?? {};
|
|
123
|
-
const { framework, app } = await resolveAppScope<TModules, TEnv>({
|
|
124
|
-
configure,
|
|
125
|
-
env,
|
|
126
|
-
fusion: providedFusion,
|
|
127
|
-
});
|
|
128
|
-
const result = await renderHook(render, {
|
|
129
|
-
...renderHookOptions,
|
|
130
|
-
wrapper: createAppScopeWrapper<TModules>({ framework, app }),
|
|
131
|
-
});
|
|
132
|
-
return { ...result, fusion: { framework, app } };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export default renderAppHook;
|
package/src/render.tsx
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { ReactElement } from 'react';
|
|
2
|
-
|
|
3
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
4
|
-
import type { AppEnv } from '@equinor/fusion-framework-app';
|
|
5
|
-
import type { AppMockConfigureFn } from '@equinor/fusion-framework-app/mock';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
renderAppComponent,
|
|
9
|
-
type RenderAppComponentOptions,
|
|
10
|
-
type RenderAppComponentResult,
|
|
11
|
-
} from './render-app-component';
|
|
12
|
-
|
|
13
|
-
// resolved at test-time by `appTestVitePlugin` (@equinor/fusion-framework-vitest-plugin-react-app);
|
|
14
|
-
// see virtual-modules.d.ts for the ambient module declarations
|
|
15
|
-
import { manifest, config } from 'virtual:fusion-app-test-env';
|
|
16
|
-
import { configure } from 'virtual:fusion-app-test-configure';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Renders a component inside the application's own module scope for use in plain `describe`/`it`
|
|
20
|
-
* tests, using the manifest, config, and module-configurator resolved for this application — no
|
|
21
|
-
* per-test wiring, and no custom `test` fixture required.
|
|
22
|
-
*
|
|
23
|
-
* @remarks
|
|
24
|
-
* Requires `appTestVitePlugin` (`@equinor/fusion-framework-vitest-plugin-react-app`) registered in
|
|
25
|
-
* your `vitest.config.ts` `plugins`, which serves the virtual modules backing the resolved
|
|
26
|
-
* `env`/`configure`. Running the same test file without the plugin registered fails to resolve
|
|
27
|
-
* those imports.
|
|
28
|
-
*
|
|
29
|
-
* Pass `env` or `configure` in `options` to override the resolved values for a single render.
|
|
30
|
-
*
|
|
31
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
32
|
-
* @param ui - The component to render.
|
|
33
|
-
* @param options - Overrides for `env`/`configure`, plus any other `renderAppComponent` option.
|
|
34
|
-
* @returns The `render` result plus `fusion.framework` and `fusion.app`, once the mocked application module scope resolves.
|
|
35
|
-
* @example
|
|
36
|
-
* ```tsx
|
|
37
|
-
* import { describe, expect, it } from 'vitest';
|
|
38
|
-
* import { render } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
|
|
39
|
-
* import { App } from '../App';
|
|
40
|
-
*
|
|
41
|
-
* describe('App', () => {
|
|
42
|
-
* it('renders the app', async () => {
|
|
43
|
-
* const { getByRole } = await render(<App />);
|
|
44
|
-
* await expect.element(getByRole('heading')).toBeVisible();
|
|
45
|
-
* });
|
|
46
|
-
* });
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export async function render<TModules extends Array<AnyModule> | unknown = unknown>(
|
|
50
|
-
ui: ReactElement,
|
|
51
|
-
options?: RenderAppComponentOptions<TModules, AppEnv>,
|
|
52
|
-
): Promise<RenderAppComponentResult<TModules>> {
|
|
53
|
-
const { configure: configureOverride, env: envOverride, ...renderOptions } = options ?? {};
|
|
54
|
-
return renderAppComponent<TModules, AppEnv>(ui, {
|
|
55
|
-
...renderOptions,
|
|
56
|
-
env: envOverride ?? { manifest, config },
|
|
57
|
-
// the resolved `configure` is generic over the app's own module set (`unknown`), while
|
|
58
|
-
// `TModules` here is caller-supplied *extra* modules — safe to widen for the common case
|
|
59
|
-
// where callers don't override it with a `TModules`-specific configurator
|
|
60
|
-
configure: (configureOverride ?? configure) as AppMockConfigureFn<TModules, AppEnv> | undefined,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export default render;
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { FileNotFoundError } from '@equinor/fusion-imports';
|
|
2
|
-
import type { AppManifest } from '@equinor/fusion-framework-module-app';
|
|
3
|
-
import type { RuntimeEnv } from '@equinor/fusion-framework-cli';
|
|
4
|
-
import {
|
|
5
|
-
createAppManifestFromPackage,
|
|
6
|
-
loadAppManifest,
|
|
7
|
-
loadAppConfig,
|
|
8
|
-
mergeAppManifests,
|
|
9
|
-
ApiAppConfigSchema,
|
|
10
|
-
type AppManifestFn,
|
|
11
|
-
type AppConfigFn,
|
|
12
|
-
type ApiAppConfig,
|
|
13
|
-
} from '@equinor/fusion-framework-cli/app';
|
|
14
|
-
import { resolvePackage } from '@equinor/fusion-framework-cli/utils';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The application manifest and config resolved for a test run.
|
|
18
|
-
*/
|
|
19
|
-
export type AppTestEnv = {
|
|
20
|
-
manifest: AppManifest;
|
|
21
|
-
config: ApiAppConfig;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Options for {@link resolveAppTestEnv}.
|
|
26
|
-
*/
|
|
27
|
-
export type ResolveAppTestEnvOptions = {
|
|
28
|
-
/** Directory to resolve the package, manifest, and config from. Defaults to `process.cwd()`. */
|
|
29
|
-
entrypoint?: string;
|
|
30
|
-
/**
|
|
31
|
-
* An explicit manifest file to load instead of the default `app.manifest(.*)?` lookup, or a
|
|
32
|
-
* manifest function (same shape as `defineAppManifest`'s argument) applied directly.
|
|
33
|
-
*/
|
|
34
|
-
manifest?: string | AppManifestFn;
|
|
35
|
-
/**
|
|
36
|
-
* An explicit config file to load instead of the default `app.config(.*)?` lookup, or a
|
|
37
|
-
* config function (same shape as `defineAppConfig`'s argument) applied directly.
|
|
38
|
-
*/
|
|
39
|
-
config?: string | AppConfigFn;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Resolves an application's manifest and config using the same pipeline `ffc app build`/
|
|
44
|
-
* `ffc app dev` use: a base manifest generated from `package.json`, merged with a local
|
|
45
|
-
* `app.manifest.ts` if one exists; and `app.config.ts` for endpoints/environment, falling
|
|
46
|
-
* back to an empty config if none exists.
|
|
47
|
-
*
|
|
48
|
-
* @remarks
|
|
49
|
-
* Intended to seed `@equinor/fusion-framework-vitest-plugin-react-app/test`'s `testApp` `appEnv` fixture, so
|
|
50
|
-
* a test suite exercises the application's real manifest/config instead of a hand-maintained
|
|
51
|
-
* duplicate. Anything a specific test still needs faked (a missing endpoint, a different
|
|
52
|
-
* `appKey`) can be layered on top with `testApp.extend('appEnv', ...)` or a per-test
|
|
53
|
-
* `test.override('appEnv', ...)`.
|
|
54
|
-
*
|
|
55
|
-
* @param options - Resolution options; `entrypoint` defaults to the current working directory.
|
|
56
|
-
* @returns The resolved application manifest and config.
|
|
57
|
-
* @throws If no `package.json` can be found from `entrypoint` upward, or an explicitly requested
|
|
58
|
-
* `manifest`/`config` file does not exist.
|
|
59
|
-
* @example
|
|
60
|
-
* ```ts
|
|
61
|
-
* import { resolveAppTestEnv } from '@equinor/fusion-framework-vitest-plugin-react-app';
|
|
62
|
-
* import { testApp } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
|
|
63
|
-
* import { configure } from '../config';
|
|
64
|
-
*
|
|
65
|
-
* const test = testApp
|
|
66
|
-
* .extend('appEnv', { injected: true }, () => resolveAppTestEnv())
|
|
67
|
-
* .extend('configureApp', { injected: true }, () => configure);
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export const resolveAppTestEnv = async (
|
|
71
|
-
options?: ResolveAppTestEnvOptions,
|
|
72
|
-
): Promise<AppTestEnv> => {
|
|
73
|
-
const pkg = await resolvePackage({ cwd: options?.entrypoint });
|
|
74
|
-
const env: RuntimeEnv = { command: 'build', mode: 'test', root: pkg.root, environment: 'test' };
|
|
75
|
-
|
|
76
|
-
const baseManifest = createAppManifestFromPackage(env, pkg.packageJson);
|
|
77
|
-
const [manifest, config] = await Promise.all([
|
|
78
|
-
resolveManifest(env, baseManifest, options?.manifest),
|
|
79
|
-
resolveConfig(env, options?.config),
|
|
80
|
-
]);
|
|
81
|
-
|
|
82
|
-
return { manifest, config };
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Loads `app.manifest(.*)?` (or applies an inline {@link AppManifestFn}), falling back to the
|
|
87
|
-
* package-derived manifest when none exists — mirrors `ffc app build`'s own fallback so a test
|
|
88
|
-
* env matches a real build with zero manifest file.
|
|
89
|
-
*/
|
|
90
|
-
const resolveManifest = async (
|
|
91
|
-
env: RuntimeEnv,
|
|
92
|
-
base: AppManifest,
|
|
93
|
-
manifest?: string | AppManifestFn,
|
|
94
|
-
): Promise<AppManifest> => {
|
|
95
|
-
// an inline function is applied directly, the same way a loaded `app.manifest.ts`'s default export would be
|
|
96
|
-
if (typeof manifest === 'function') {
|
|
97
|
-
const result = await manifest(env, { base });
|
|
98
|
-
return mergeAppManifests(base, result ?? {});
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
return (await loadAppManifest(env, { base, file: manifest })).manifest;
|
|
102
|
-
} catch (err) {
|
|
103
|
-
// an explicitly requested manifest that's missing is a real error; only the default lookup falls back
|
|
104
|
-
if (err instanceof FileNotFoundError && !manifest) return base;
|
|
105
|
-
throw err;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Loads `app.config(.*)?` (or applies an inline {@link AppConfigFn}), falling back to an empty
|
|
111
|
-
* config when none exists — mirrors `ffc app build`'s own fallback so a test env matches a real
|
|
112
|
-
* build with zero config file.
|
|
113
|
-
*/
|
|
114
|
-
const resolveConfig = async (
|
|
115
|
-
env: RuntimeEnv,
|
|
116
|
-
config?: string | AppConfigFn,
|
|
117
|
-
): Promise<ApiAppConfig> => {
|
|
118
|
-
// an inline function is applied directly, the same way a loaded `app.config.ts`'s default export would be
|
|
119
|
-
if (typeof config === 'function') {
|
|
120
|
-
const result = await config(env, { base: { environment: {} } });
|
|
121
|
-
return ApiAppConfigSchema.parse(result ?? { environment: {} });
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
return (await loadAppConfig(env, { file: config })).config;
|
|
125
|
-
} catch (err) {
|
|
126
|
-
// an explicitly requested config that's missing is a real error; only the default lookup falls back
|
|
127
|
-
if (err instanceof FileNotFoundError && !config) return { environment: {} };
|
|
128
|
-
throw err;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export default resolveAppTestEnv;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
import { FrameworkProvider } from '@equinor/fusion-framework-react';
|
|
4
|
-
import { ModuleProvider } from '@equinor/fusion-framework-react-module';
|
|
5
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
6
|
-
|
|
7
|
-
import type { AppScope } from './resolve-app-scope';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The provider nesting `createComponent` uses in production, for wrapping a component or
|
|
11
|
-
* hook under test in its resolved {@link AppScope}.
|
|
12
|
-
*
|
|
13
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
14
|
-
* @param scope - The resolved parent Fusion instance and application module scope.
|
|
15
|
-
* @returns A wrapper component nesting a `FrameworkProvider` around a `ModuleProvider`.
|
|
16
|
-
*/
|
|
17
|
-
export function createAppScopeWrapper<TModules extends Array<AnyModule> | unknown = unknown>({
|
|
18
|
-
framework,
|
|
19
|
-
app,
|
|
20
|
-
}: AppScope<TModules>): (props: { children: ReactNode }) => ReactElement {
|
|
21
|
-
return ({ children }: { children: ReactNode }) => (
|
|
22
|
-
<FrameworkProvider value={framework}>
|
|
23
|
-
<ModuleProvider value={app}>{children}</ModuleProvider>
|
|
24
|
-
</FrameworkProvider>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { AppEnv } from '@equinor/fusion-framework-app';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The application environment used when a test does not care about its own app identity.
|
|
5
|
-
*/
|
|
6
|
-
export const defaultAppEnv: AppEnv = {
|
|
7
|
-
manifest: {
|
|
8
|
-
appKey: 'test-app',
|
|
9
|
-
displayName: 'Test App',
|
|
10
|
-
description: 'A test application',
|
|
11
|
-
type: 'standalone',
|
|
12
|
-
},
|
|
13
|
-
};
|
package/src/scope/index.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { mockAppModules } from '@equinor/fusion-framework-app/mock';
|
|
2
|
-
import type { AppMockConfigureFn } from '@equinor/fusion-framework-app/mock';
|
|
3
|
-
import type { AppEnv, AppModulesInstance } from '@equinor/fusion-framework-app';
|
|
4
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
5
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
6
|
-
|
|
7
|
-
import { resolveFusion } from './resolve-fusion';
|
|
8
|
-
import { defaultAppEnv } from './default-app-env';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The parent Fusion instance and resolved application module scope shared by every
|
|
12
|
-
* `packages/react/app` testing helper.
|
|
13
|
-
*
|
|
14
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
15
|
-
*/
|
|
16
|
-
export interface AppScope<TModules extends Array<AnyModule> | unknown = unknown> {
|
|
17
|
-
/** The parent Fusion instance the application scope was resolved against. */
|
|
18
|
-
framework: Fusion;
|
|
19
|
-
/** The resolved application module instance. */
|
|
20
|
-
app: AppModulesInstance<TModules>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Resolves the parent Fusion instance and application module scope shared by every
|
|
25
|
-
* `packages/react/app` testing helper.
|
|
26
|
-
*
|
|
27
|
-
* @template TModules - Module descriptors beyond the default set.
|
|
28
|
-
* @template TEnv - The application environment descriptor.
|
|
29
|
-
* @param options - A `configure` callback and `env` for {@link mockAppModules}, plus an
|
|
30
|
-
* already-built `fusion` instance to reuse instead of a fresh one.
|
|
31
|
-
* @returns The resolved {@link AppScope}.
|
|
32
|
-
*/
|
|
33
|
-
export async function resolveAppScope<
|
|
34
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
35
|
-
TEnv extends AppEnv = AppEnv,
|
|
36
|
-
>(options?: {
|
|
37
|
-
configure?: AppMockConfigureFn<TModules, TEnv>;
|
|
38
|
-
env?: TEnv;
|
|
39
|
-
fusion?: Fusion;
|
|
40
|
-
}): Promise<AppScope<TModules>> {
|
|
41
|
-
const { configure, env, fusion: providedFusion } = options ?? {};
|
|
42
|
-
const resolvedEnv = env ?? (defaultAppEnv as TEnv);
|
|
43
|
-
const framework = await resolveFusion({ env: resolvedEnv, fusion: providedFusion });
|
|
44
|
-
const app = await mockAppModules(configure, resolvedEnv, framework);
|
|
45
|
-
return { framework, app };
|
|
46
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { AppEnv } from '@equinor/fusion-framework-app';
|
|
2
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
3
|
-
import {
|
|
4
|
-
mockFramework,
|
|
5
|
-
type FrameworkMockConfigureFn,
|
|
6
|
-
type FrameworkMockConfigurator,
|
|
7
|
-
} from '@equinor/fusion-framework/mock';
|
|
8
|
-
import { enableAppManifestMock } from '@equinor/fusion-framework-app/mock';
|
|
9
|
-
import type { AppModule } from '@equinor/fusion-framework-module-app';
|
|
10
|
-
import { enableNavigation, createHistory } from '@equinor/fusion-framework-module-navigation';
|
|
11
|
-
import type { NavigationModule } from '@equinor/fusion-framework-module-navigation';
|
|
12
|
-
|
|
13
|
-
import { defaultAppEnv } from './default-app-env';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Enables the feature-flag mock only when `@equinor/fusion-framework-module-feature-flag` — an
|
|
17
|
-
* optional peer dependency — is actually installed, so apps that don't use feature flags aren't
|
|
18
|
-
* forced to install a module they never reference.
|
|
19
|
-
*/
|
|
20
|
-
async function enableFeatureFlagMockIfAvailable(
|
|
21
|
-
configurator: FrameworkMockConfigurator<[AppModule, NavigationModule]>,
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
try {
|
|
24
|
-
const { enableFeatureFlagMock } = await import(
|
|
25
|
-
'@equinor/fusion-framework-module-feature-flag/mock'
|
|
26
|
-
);
|
|
27
|
-
enableFeatureFlagMock(configurator);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
// re-throw anything other than the optional peer being missing, so a real
|
|
30
|
-
// registration failure isn't silently swallowed
|
|
31
|
-
if (
|
|
32
|
-
error instanceof Error &&
|
|
33
|
-
(error.message.includes('Cannot find module') || error.message.includes('MODULE_NOT_FOUND'))
|
|
34
|
-
) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Resolves the parent Fusion instance backing an application module scope, building a
|
|
43
|
-
* fresh {@link mockFramework} instance with this app's own manifest served when none is
|
|
44
|
-
* given.
|
|
45
|
-
*
|
|
46
|
-
* @template TEnv - The application environment descriptor.
|
|
47
|
-
* @param options - `env` seeds the built-in app manifest mock; navigation defaults to in-memory
|
|
48
|
-
* history, so tests don't leak URL/history state between runs. Feature flags default to the
|
|
49
|
-
* in-memory feature-flag mock, with none enabled, when the optional
|
|
50
|
-
* `@equinor/fusion-framework-module-feature-flag` peer dependency is installed. `configure`
|
|
51
|
-
* runs afterwards on the same configurator — e.g. call `enableNavigation` or
|
|
52
|
-
* `enableFeatureFlagMock` again to override either, or register extra modules and service
|
|
53
|
-
* discovery entries. `fusion`, an already-built parent instance, is reused as-is and skips
|
|
54
|
-
* `configure` entirely.
|
|
55
|
-
* @returns The given `fusion`, or a fresh mocked parent Fusion instance.
|
|
56
|
-
*/
|
|
57
|
-
export async function resolveFusion<TEnv extends AppEnv = AppEnv>(options?: {
|
|
58
|
-
env?: TEnv;
|
|
59
|
-
fusion?: Fusion;
|
|
60
|
-
configure?: FrameworkMockConfigureFn<[AppModule, NavigationModule]>;
|
|
61
|
-
}): Promise<Fusion> {
|
|
62
|
-
const { env, fusion, configure } = options ?? {};
|
|
63
|
-
return (
|
|
64
|
-
fusion ??
|
|
65
|
-
mockFramework<[AppModule, NavigationModule]>(async (configurator) => {
|
|
66
|
-
enableAppManifestMock(configurator, env ?? (defaultAppEnv as TEnv));
|
|
67
|
-
enableNavigation(configurator, {
|
|
68
|
-
configure: (config) => config.setHistory(createHistory('memory')),
|
|
69
|
-
});
|
|
70
|
-
await enableFeatureFlagMockIfAvailable(configurator);
|
|
71
|
-
await configure?.(configurator);
|
|
72
|
-
})
|
|
73
|
-
);
|
|
74
|
-
}
|