@equinor/fusion-framework 8.0.14 → 8.1.0-next.0

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 (41) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +30 -0
  3. package/dist/esm/__tests__/mock/application-module.test.js +53 -0
  4. package/dist/esm/__tests__/mock/application-module.test.js.map +1 -0
  5. package/dist/esm/__tests__/mock/documented-usage.test.js +73 -0
  6. package/dist/esm/__tests__/mock/documented-usage.test.js.map +1 -0
  7. package/dist/esm/__tests__/mock/mock-framework.test.js +212 -0
  8. package/dist/esm/__tests__/mock/mock-framework.test.js.map +1 -0
  9. package/dist/esm/init.js +7 -2
  10. package/dist/esm/init.js.map +1 -1
  11. package/dist/esm/mock/FrameworkMockConfigurator.js +233 -0
  12. package/dist/esm/mock/FrameworkMockConfigurator.js.map +1 -0
  13. package/dist/esm/mock/index.js +32 -0
  14. package/dist/esm/mock/index.js.map +1 -0
  15. package/dist/esm/mock/mock-framework.js +55 -0
  16. package/dist/esm/mock/mock-framework.js.map +1 -0
  17. package/dist/esm/version.js +1 -1
  18. package/dist/esm/version.js.map +1 -1
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/types/__tests__/mock/application-module.test.d.ts +1 -0
  21. package/dist/types/__tests__/mock/documented-usage.test.d.ts +1 -0
  22. package/dist/types/__tests__/mock/mock-framework.test.d.ts +1 -0
  23. package/dist/types/mock/FrameworkMockConfigurator.d.ts +185 -0
  24. package/dist/types/mock/index.d.ts +29 -0
  25. package/dist/types/mock/mock-framework.d.ts +58 -0
  26. package/dist/types/version.d.ts +1 -1
  27. package/docs/testing-api.md +25 -0
  28. package/docs/testing-choosing-a-layer.md +90 -0
  29. package/docs/testing-design.md +63 -0
  30. package/docs/testing-extending.md +102 -0
  31. package/docs/testing.md +178 -0
  32. package/package.json +17 -11
  33. package/src/__tests__/mock/application-module.test.ts +87 -0
  34. package/src/__tests__/mock/documented-usage.test.ts +98 -0
  35. package/src/__tests__/mock/mock-framework.test.ts +278 -0
  36. package/src/init.ts +7 -2
  37. package/src/mock/FrameworkMockConfigurator.ts +270 -0
  38. package/src/mock/index.ts +62 -0
  39. package/src/mock/mock-framework.ts +72 -0
  40. package/src/version.ts +1 -1
  41. package/vitest.config.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,203 @@
1
1
  # Change Log
2
2
 
3
+ ## 8.1.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2836e0b: Restructure documentation so each README is an entry point rather than a manual.
8
+
9
+ Long-form content moved into per-package `docs/` folders, matching the convention already used by `@equinor/fusion-framework-module` and `@equinor/fusion-framework-module-http`. Each README now keeps the elevator pitch, the shortest working example and a documentation table linking to the rest.
10
+
11
+ - **msal** — `docs/api-reference.md`, `docs/auth-code-flow.md`, `docs/testing.md`, `docs/version-management.md`, `docs/migration-v2-to-v4.md`, `docs/troubleshooting.md`. The README also gained the top-level heading it was missing.
12
+ - **service-discovery** — `docs/configuration.md`, `docs/testing.md`, `docs/session-overrides.md`, `docs/api-reference.md`.
13
+ - **framework** — `docs/testing-choosing-a-layer.md`, `docs/testing.md`, `docs/testing-design.md`, `docs/testing-extending.md`, `docs/testing-api.md`.
14
+
15
+ Both module READMEs now document their `/mock` entry point, which was previously undocumented, and state that spying on an individual call is the test runner's job rather than something these packages provide.
16
+
17
+ - 2836e0b: `FrameworkMockConfigurator.context` is now backed by `ContextMockConfigurator` from `@equinor/fusion-framework-module-context/mock` instead of the real `ContextModuleConfigurator` — context resolution in tests no longer performs real I/O:
18
+
19
+ ```typescript
20
+ const configurator = new FrameworkMockConfigurator();
21
+ configurator.context.setCurrentContext({
22
+ id: "my-ctx",
23
+ type: { id: "ProjectMaster" },
24
+ value: {},
25
+ });
26
+
27
+ const fusion = await init(configurator);
28
+ ```
29
+
30
+ This changes the type returned by `.context` from `ContextModuleConfigurator` to `ContextMockConfigurator` (which extends it) — code relying on `.context` being exactly `ContextModuleConfigurator` should switch to seeding data through the new mock methods (`setCurrentContext`, `setContexts`, `addContext`, `setRelatedContexts`, `setResolver`) instead of configuring a real client.
31
+
32
+ `docs/testing.md`, `docs/testing-extending.md` and `docs/testing-api.md` are updated to describe both mocking strategies now available for context: the in-memory `ContextMockConfigurator` above, and mocking the context API's HTTP responses directly for tests that need to exercise the real configurator/services/HTTP pipeline.
33
+
34
+ - 2836e0b: Add `_pin` and `_getConfig` to `FrameworkMockConfigurator`, so a module supplied through `TModules` can get the same kind of named accessor `.msal` and `.serviceDiscovery` already have.
35
+
36
+ Previously that pinning was hand-written twice, once per built-in mock, with no way for anything else to do the same. A subclass now reuses it directly:
37
+
38
+ ```typescript
39
+ class AppMockConfigurator extends FrameworkMockConfigurator<[InvoiceModule]> {
40
+ constructor() {
41
+ super();
42
+ this._pin(invoiceMockModule);
43
+ }
44
+
45
+ get invoices(): InvoiceMockConfigurator {
46
+ return this._getConfig("invoices");
47
+ }
48
+ }
49
+ ```
50
+
51
+ `_pin(module)` replaces the module's own `configure` factory with one that always returns the same instance, and registers it — pinning it before initialization runs is what lets a test reach the accessor synchronously and have it be the configurator the module is actually built from. `_getConfig(name)` looks that instance up by the module's name, throwing if nothing was pinned for it.
52
+
53
+ `.msal` and `.serviceDiscovery` are unchanged for consumers; they are now built from `_pin`/`_getConfig` themselves rather than from two private fields.
54
+
55
+ - 2836e0b: Add `.services`, `.context` and `.telemetry` accessors to `FrameworkMockConfigurator`, alongside the existing `.msal`, `.serviceDiscovery` and `.http`.
56
+
57
+ These three modules have no test double yet, so anything read through `.services`, `.context` or `.telemetry` still performs real I/O — but their configurators are now reachable synchronously the same way `.msal` and `.serviceDiscovery` already are, since none of their `configure` factories depend on `ref`:
58
+
59
+ ```typescript
60
+ const configurator = new FrameworkMockConfigurator();
61
+ configurator.services.configureClient("my-api", {
62
+ baseUri: "http://localhost:6669",
63
+ });
64
+
65
+ const fusion = await init(configurator);
66
+ ```
67
+
68
+ `event` is intentionally left out: its `configure` factory reads `ref` to wire event bubbling to a parent event provider when `FrameworkMockConfigurator` is hoisted inside a host framework, and pinning it would call `configure()` with `ref` always `undefined` — silently disabling that bubbling.
69
+
70
+ - 2836e0b: Add a `./mock` entry point for initializing the framework in a test.
71
+
72
+ ```typescript
73
+ import { mockFramework } from "@equinor/fusion-framework/mock";
74
+
75
+ const fusion = await mockFramework();
76
+ ```
77
+
78
+ `mockFramework` runs the real configure → initialize pipeline with the real built-in modules and substitutes only the boundaries that leave the process — the MSAL client and the service discovery client. Module wiring, configuration validation and lifecycle hooks behave as they do in production, so a test still catches wiring mistakes that a hand-built replacement for the module graph would hide.
79
+
80
+ It takes a single callback receiving a `FrameworkMockConfigurator`, which **is** a `FrameworkConfigurator`. Modules whose boundary is mocked expose their own configurator as a property, so a test configures them without registering a callback:
81
+
82
+ ```typescript
83
+ const fusion = await mockFramework((configurator) => {
84
+ configurator.msal.setAccount({ name: "Ada Lovelace" });
85
+ configurator.serviceDiscovery.setBaseUri("http://localhost:6669");
86
+ configurator.serviceDiscovery.addService({ key: "my-api" });
87
+ });
88
+ ```
89
+
90
+ Because it is a real configurator, every `enableX` helper an application already uses accepts it unchanged — including the ones a team writes for their own modules. Those modules can be passed as a type argument so they are typed on the configurator _and_ on the resulting `fusion.modules` without a cast:
91
+
92
+ ```typescript
93
+ const fusion = await mockFramework<[InvoiceModule]>((configurator) => {
94
+ enableInvoicesMock(configurator, { total: 42 });
95
+ });
96
+
97
+ await fusion.modules.invoices.getInvoice("inv-1"); // typed
98
+ ```
99
+
100
+ `FrameworkMockConfigurator` is also exported for tests that need to hold on to the configurator and call `init` themselves.
101
+
102
+ The entry point owns no mock logic. Each module exports its own test double from its own `./mock` entry point, and this one composes the built-in set; an application module follows the same pattern and plugs in without any support from this package. It has no test-runner dependency and provides no mocking API, because replacing an individual call belongs to your test runner.
103
+
104
+ - 2836e0b: `FrameworkMockConfigurator.telemetry` is now backed by `TelemetryMockConfigurator` from `@equinor/fusion-framework-module-telemetry/mock` instead of the real `TelemetryConfigurator` — telemetry tracked through a mocked framework instance no longer reaches Application Insights or any real endpoint:
105
+
106
+ ```typescript
107
+ import { filter } from "rxjs";
108
+
109
+ const fusion = await mockFramework();
110
+
111
+ fusion.modules.telemetry.items
112
+ .pipe(filter((item) => item.name === "button-click"))
113
+ .subscribe((item) => {
114
+ // ...
115
+ });
116
+
117
+ fusion.modules.telemetry.trackEvent({ name: "button-click" });
118
+ ```
119
+
120
+ A test can register its own adapter alongside the mock's default one, the same way `enableTelemetry` already does:
121
+
122
+ ```typescript
123
+ const myAdapter: ITelemetryAdapter = {
124
+ processItem: (item) => forwardSomewhere(item),
125
+ };
126
+ const fusion = await mockFramework((configurator) => {
127
+ configurator.telemetry.setAdapter("my-adapter", myAdapter);
128
+ });
129
+ ```
130
+
131
+ This changes the type returned by `.telemetry` from `ITelemetryConfigurator` to `TelemetryMockConfigurator` (which extends the real `TelemetryConfigurator`) — code relying on `.telemetry` being exactly the real configurator should read tracked items back through `.telemetry.adapter` (`getItems`/`waitForItem`) instead of asserting against a real telemetry backend.
132
+
133
+ - 2836e0b: Add a `.http` accessor to `FrameworkMockConfigurator`, backed by the real `IHttpClientConfigurator` — fake a response by registering a short-circuiting middleware through `.http.addMiddleware(...)` instead of swapping the module out:
134
+
135
+ ```ts
136
+ const configurator = new FrameworkMockConfigurator();
137
+ configurator.http.addMiddleware(async (uri, init, next) =>
138
+ uri === "https://api.example.com/items"
139
+ ? Response.json([{ id: 1 }])
140
+ : next(uri, init),
141
+ );
142
+ ```
143
+
144
+ See `@equinor/fusion-framework-module-http`'s `addMiddleware` changeset for the full API.
145
+
146
+ ### Patch Changes
147
+
148
+ - e8aae1f: Internal: publish every package on the `next` pre-release tag so the whole framework can be installed as a coherent set.
149
+
150
+ Packages without their own changes are bumped only to receive a `-next.N` version and the `next` dist-tag on npm. Install with:
151
+
152
+ ```bash
153
+ pnpm add @equinor/fusion-framework-react-app@next
154
+ ```
155
+
156
+ - 2836e0b: `init` no longer throws when no DOM is present.
157
+
158
+ The running instance is published as `window.Fusion` for portal shells and widgets. That assignment was unguarded, so initializing the framework anywhere without a `window` — a test runner using the `node` environment, or a server-side render — failed with `ReferenceError: window is not defined`.
159
+
160
+ The assignment is now skipped when `window` is undefined. Browser behaviour is unchanged.
161
+
162
+ - Updated dependencies [e8aae1f]
163
+ - Updated dependencies [2836e0b]
164
+ - Updated dependencies [2836e0b]
165
+ - Updated dependencies [2836e0b]
166
+ - Updated dependencies [2836e0b]
167
+ - Updated dependencies [2836e0b]
168
+ - Updated dependencies [2836e0b]
169
+ - Updated dependencies [2836e0b]
170
+ - Updated dependencies [2836e0b]
171
+ - Updated dependencies [2836e0b]
172
+ - Updated dependencies [2836e0b]
173
+ - Updated dependencies [2836e0b]
174
+ - Updated dependencies [2836e0b]
175
+ - Updated dependencies [2836e0b]
176
+ - Updated dependencies [2836e0b]
177
+ - Updated dependencies [2836e0b]
178
+ - Updated dependencies [2836e0b]
179
+ - Updated dependencies [2836e0b]
180
+ - Updated dependencies [2836e0b]
181
+ - Updated dependencies [2836e0b]
182
+ - Updated dependencies [2836e0b]
183
+ - Updated dependencies [2836e0b]
184
+ - Updated dependencies [2836e0b]
185
+ - @equinor/fusion-framework-module@6.1.3-next.0
186
+ - @equinor/fusion-framework-module-context@9.0.0-next.0
187
+ - @equinor/fusion-framework-module-event@6.1.0-next.0
188
+ - @equinor/fusion-framework-module-http@8.1.0-next.0
189
+ - @equinor/fusion-framework-module-msal@11.0.0-next.0
190
+ - @equinor/fusion-framework-module-service-discovery@10.1.0-next.0
191
+ - @equinor/fusion-framework-module-services@8.1.1-next.0
192
+ - @equinor/fusion-framework-module-telemetry@8.0.0-next.0
193
+
194
+ ## 8.0.15
195
+
196
+ ### Patch Changes
197
+
198
+ - Updated dependencies [73bfe52]
199
+ - @equinor/fusion-framework-module-context@8.0.3
200
+
3
201
  ## 8.0.14
4
202
 
5
203
  ### Patch Changes
package/README.md CHANGED
@@ -94,6 +94,36 @@ The framework ships with the following modules enabled by default:
94
94
  - **`@equinor/fusion-framework-module-context`** — application / project context selection
95
95
  - **`@equinor/fusion-framework-module-telemetry`** — telemetry, logging, and metadata
96
96
 
97
+ ## Testing
98
+
99
+ Import from `@equinor/fusion-framework/mock` to initialize the framework in a test without credentials, without network access and without configuration.
100
+
101
+ ```typescript
102
+ import { mockFramework } from '@equinor/fusion-framework/mock';
103
+
104
+ const fusion = await mockFramework((configurator) => {
105
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
106
+ configurator.serviceDiscovery.setBaseUri('http://localhost:6669');
107
+ });
108
+
109
+ const token = await fusion.modules.auth.acquireAccessToken();
110
+ const apps = await fusion.modules.serviceDiscovery.resolveService('apps');
111
+ ```
112
+
113
+ The **real** configure → initialize pipeline runs with the **real** built-in modules; only the boundaries that leave the process — the MSAL client and the service discovery client — are substituted. Module wiring, configuration validation and lifecycle hooks therefore behave as they do in production, so a test still catches wiring mistakes.
114
+
115
+ The entry point has **no test-runner dependency**, and ships no mocking API of its own — spying on an individual call is your test runner's job.
116
+
117
+ ## Documentation
118
+
119
+ | Guide | Covers |
120
+ | --- | --- |
121
+ | [Choosing a testing layer](./docs/testing-choosing-a-layer.md) | Which import to use for React app, framework, module, HTTP, and runner-level tests |
122
+ | [Testing](./docs/testing.md) | Choosing the user, signed-out tests, composing the service registry, spying, and configuring as an application does |
123
+ | [Testing — design](./docs/testing-design.md) | What is substituted and why, where mocks live, determinism, and test-runner support |
124
+ | [Testing — adding a mock for another module](./docs/testing-extending.md) | Giving your own module a test double, and which framework modules are not covered yet |
125
+ | [Testing — API](./docs/testing-api.md) | Every mock export and the package that owns it |
126
+
97
127
  ## Further reading
98
128
 
99
129
  📚 [Full documentation](https://equinor.github.io/fusion-framework/)
@@ -0,0 +1,53 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mockFramework } from '../../mock/index.js';
3
+ class InvoiceConfigurator {
4
+ #client;
5
+ setClient(client) {
6
+ this.#client = client;
7
+ }
8
+ createClient() {
9
+ if (!this.#client) {
10
+ throw new Error('An invoice client is required');
11
+ }
12
+ return this.#client;
13
+ }
14
+ }
15
+ const invoiceModule = {
16
+ name: 'invoices',
17
+ configure: () => new InvoiceConfigurator(),
18
+ initialize: ({ config }) => config.createClient(),
19
+ };
20
+ /** The team's own mock, following the pattern the built-in modules use. */
21
+ const enableInvoicesMock = (
22
+ // biome-ignore lint/suspicious/noExplicitAny: mirrors every enableX helper
23
+ configurator, options = {}) => {
24
+ configurator.addConfig({
25
+ module: invoiceModule,
26
+ configure: (builder) => builder.setClient({
27
+ getInvoice: async (id) => ({ id, total: options.total ?? 0 }),
28
+ }),
29
+ });
30
+ };
31
+ describe('application modules', () => {
32
+ it('fails to start without its mock, so the seam is real and not a no-op', async () => {
33
+ await expect(mockFramework((configurator) => {
34
+ configurator.addConfig({ module: invoiceModule });
35
+ })).rejects.toThrow(/invoice client is required/i);
36
+ });
37
+ it('composes with the built-in mocks and is typed without a cast', async () => {
38
+ const fusion = await mockFramework((configurator) => {
39
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
40
+ enableInvoicesMock(configurator, { total: 42 });
41
+ });
42
+ // No cast: `TModules` must flow through to `fusion.modules`.
43
+ const invoice = await fusion.modules.invoices.getInvoice('inv-1');
44
+ expect(invoice).toEqual({ id: 'inv-1', total: 42 });
45
+ expect(fusion.modules.auth.account?.name).toBe('Ada Lovelace');
46
+ });
47
+ it('is registered exactly as it is in production', async () => {
48
+ // The same helper, against a real FrameworkConfigurator, would behave identically.
49
+ const fusion = await mockFramework((configurator) => enableInvoicesMock(configurator, { total: 7 }));
50
+ await expect(fusion.modules.invoices.getInvoice('inv-2')).resolves.toMatchObject({ total: 7 });
51
+ });
52
+ });
53
+ //# sourceMappingURL=application-module.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-module.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/application-module.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { decodeJwtSegment } from '@equinor/fusion-framework-module-msal/mock';
3
+ import { init } from '../../init.js';
4
+ import { createMockService, FrameworkMockConfigurator, mockFramework } from '../../mock/index.js';
5
+ /**
6
+ * Pins the snippets in `docs/testing.md` and the package README to real
7
+ * behaviour, so documentation cannot drift away from the API it describes.
8
+ */
9
+ describe('documented usage', () => {
10
+ afterEach(() => vi.restoreAllMocks());
11
+ it('composes the service registry on the builder', async () => {
12
+ const fusion = await mockFramework((configurator) => {
13
+ configurator.serviceDiscovery.setBaseUri('http://localhost:6669');
14
+ configurator.serviceDiscovery.addService({ key: 'my-api' });
15
+ configurator.serviceDiscovery.removeService('bookmarks');
16
+ });
17
+ await expect(fusion.modules.serviceDiscovery.resolveService('my-api')).resolves.toMatchObject({
18
+ key: 'my-api',
19
+ });
20
+ });
21
+ it('replaces the baseline registry with setServices', async () => {
22
+ const fusion = await mockFramework((configurator) => {
23
+ configurator.serviceDiscovery.setServices([{ key: 'apps', uri: 'http://localhost:3000' }]);
24
+ });
25
+ await expect(fusion.modules.serviceDiscovery.resolveService('apps')).resolves.toMatchObject({
26
+ uri: 'http://localhost:3000',
27
+ });
28
+ });
29
+ it('keeps startup working when the registry is replaced, because unknown services synthesise', async () => {
30
+ // The context module resolves `context` while initializing, so a replaced
31
+ // registry that omits it would break start-up if synthesis were disabled.
32
+ const fusion = await mockFramework((configurator) => {
33
+ configurator.serviceDiscovery.setServices([{ key: 'apps', uri: 'http://localhost:3000' }]);
34
+ });
35
+ await expect(fusion.modules.serviceDiscovery.resolveService('people')).resolves.toBeDefined();
36
+ });
37
+ it('lets the test runner spy on the discovery client', async () => {
38
+ const fusion = await mockFramework();
39
+ vi.spyOn(fusion.modules.serviceDiscovery.client, 'resolveService').mockResolvedValue(createMockService({ key: 'apps', uri: 'http://spied' }));
40
+ await expect(fusion.modules.serviceDiscovery.resolveService('apps')).resolves.toMatchObject({
41
+ uri: 'http://spied',
42
+ });
43
+ });
44
+ it('lets the test runner spy on the auth client', async () => {
45
+ const fusion = await mockFramework();
46
+ const spy = vi.spyOn(fusion.modules.auth.client, 'acquireToken');
47
+ await fusion.modules.auth.acquireAccessToken({ request: { scopes: ['Files.Read'] } });
48
+ expect(spy).toHaveBeenCalled();
49
+ });
50
+ it('resolves the real scope through the real provider, not the test double', async () => {
51
+ const fusion = await mockFramework((configurator) => {
52
+ // The client is configured with what it talks to, exactly as in production
53
+ configurator.msal.setClientConfig({ auth: { clientId: 'my-app', tenantId: 'my-tenant' } });
54
+ });
55
+ const token = await fusion.modules.auth.acquireAccessToken();
56
+ const claims = JSON.parse(decodeJwtSegment(token?.split('.')[1] ?? ''));
57
+ // MsalProvider — not the mock — turns "no scopes requested" into `${clientId}/.default`
58
+ expect(claims.scp).toBe('my-app/.default');
59
+ });
60
+ it('signs nobody in when the account is signed out', async () => {
61
+ const fusion = await mockFramework((configurator) => {
62
+ configurator.msal.setAccount({ signedOut: true });
63
+ });
64
+ expect(fusion.modules.auth.account).toBeFalsy();
65
+ });
66
+ it('can be constructed directly and initialized with init', async () => {
67
+ const configurator = new FrameworkMockConfigurator();
68
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
69
+ const fusion = await init(configurator);
70
+ expect(fusion.modules.auth.account?.name).toBe('Ada Lovelace');
71
+ });
72
+ });
73
+ //# sourceMappingURL=documented-usage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documented-usage.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/documented-usage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { enableMsalMock } from '@equinor/fusion-framework-module-msal/mock';
3
+ import { enableServiceDiscoveryMock } from '@equinor/fusion-framework-module-service-discovery/mock';
4
+ import { TelemetryLevel } from '@equinor/fusion-framework-module-telemetry';
5
+ import { FrameworkConfigurator } from '../../FrameworkConfigurator.js';
6
+ import { init } from '../../init.js';
7
+ import { FrameworkMockConfigurator, mockFramework } from '../../mock/index.js';
8
+ /** A minimal configurator, standing in for an application's own. */
9
+ class WidgetsConfigurator {
10
+ #name = 'default';
11
+ setName(name) {
12
+ this.#name = name;
13
+ return this;
14
+ }
15
+ get name() {
16
+ return this.#name;
17
+ }
18
+ }
19
+ const widgetsModule = {
20
+ name: 'widgets',
21
+ configure: () => new WidgetsConfigurator(),
22
+ initialize: ({ config }) => ({ name: config.name }),
23
+ };
24
+ describe('mockFramework', () => {
25
+ it('initializes every built-in module without configuration', async () => {
26
+ const fusion = await mockFramework();
27
+ expect(fusion.modules.event).toBeDefined();
28
+ expect(fusion.modules.auth).toBeDefined();
29
+ expect(fusion.modules.http).toBeDefined();
30
+ expect(fusion.modules.serviceDiscovery).toBeDefined();
31
+ expect(fusion.modules.context).toBeDefined();
32
+ expect(fusion.modules.telemetry).toBeDefined();
33
+ });
34
+ it('signs in a default user so an application has an identity to read', async () => {
35
+ const fusion = await mockFramework();
36
+ expect(fusion.modules.auth.account?.name).toBe('Test User');
37
+ });
38
+ it('resolves the services a Fusion application needs at start-up', async () => {
39
+ const fusion = await mockFramework();
40
+ await expect(fusion.modules.serviceDiscovery.resolveService('apps')).resolves.toMatchObject({
41
+ key: 'apps',
42
+ });
43
+ });
44
+ it('passes a real FrameworkConfigurator to the callback', async () => {
45
+ expect.assertions(2);
46
+ await mockFramework((configurator) => {
47
+ expect(configurator).toBeInstanceOf(FrameworkMockConfigurator);
48
+ expect(configurator).toBeInstanceOf(FrameworkConfigurator);
49
+ });
50
+ });
51
+ it('awaits an asynchronous callback before initializing', async () => {
52
+ const fusion = await mockFramework(async (configurator) => {
53
+ await Promise.resolve();
54
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
55
+ });
56
+ expect(fusion.modules.auth.account?.name).toBe('Ada Lovelace');
57
+ });
58
+ });
59
+ describe('FrameworkMockConfigurator', () => {
60
+ it('exposes the same msal configurator the auth module is built from', async () => {
61
+ const fusion = await mockFramework((configurator) => {
62
+ configurator.msal.setAccount({ name: 'Ada Lovelace', username: 'ada@equinor.com' });
63
+ });
64
+ expect(fusion.modules.auth.account).toMatchObject({
65
+ name: 'Ada Lovelace',
66
+ username: 'ada@equinor.com',
67
+ });
68
+ });
69
+ it('lets the last declared account win', async () => {
70
+ const fusion = await mockFramework((configurator) => {
71
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
72
+ configurator.msal.setAccount({ name: 'Grace Hopper' });
73
+ });
74
+ expect(fusion.modules.auth.account?.name).toBe('Grace Hopper');
75
+ });
76
+ it('resolves an account callback when the config is built', async () => {
77
+ const fusion = await mockFramework((configurator) => {
78
+ configurator.msal.setAccount(async () => ({ name: 'Ada Lovelace' }));
79
+ });
80
+ expect(fusion.modules.auth.account?.name).toBe('Ada Lovelace');
81
+ });
82
+ it('builds the auth client when the module builds its config, not when the account is set', async () => {
83
+ const configurator = new FrameworkMockConfigurator();
84
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
85
+ // The account is configuration; nothing is constructed from it yet
86
+ expect(configurator.msal.getClient()).toBeUndefined();
87
+ const fusion = await init(configurator);
88
+ expect(fusion.modules.auth.account?.name).toBe('Ada Lovelace');
89
+ });
90
+ it('exposes the same service discovery configurator the module is built from', async () => {
91
+ const fusion = await mockFramework((configurator) => {
92
+ configurator.serviceDiscovery.setBaseUri('http://localhost:6669');
93
+ configurator.serviceDiscovery.addService({ key: 'my-api' });
94
+ });
95
+ const service = await fusion.modules.serviceDiscovery.resolveService('my-api');
96
+ expect(service.uri).toContain('http://localhost:6669');
97
+ });
98
+ it('lets a service be removed so its absence can be asserted', async () => {
99
+ const fusion = await mockFramework((configurator) => {
100
+ configurator.serviceDiscovery.setResolveUnknownServices(false);
101
+ configurator.serviceDiscovery.removeService('bookmarks');
102
+ });
103
+ await expect(fusion.modules.serviceDiscovery.resolveService('bookmarks')).rejects.toThrow();
104
+ });
105
+ it('accepts an enableX helper directly, because it is a real configurator', async () => {
106
+ const fusion = await mockFramework((configurator) => {
107
+ enableMsalMock(configurator, (builder) => builder.setAccount({ name: 'Grace Hopper' }));
108
+ enableServiceDiscoveryMock(configurator, (builder) => builder.addService({ key: 'my-api' }));
109
+ });
110
+ expect(fusion.modules.auth.account?.name).toBe('Grace Hopper');
111
+ await expect(fusion.modules.serviceDiscovery.resolveService('my-api')).resolves.toBeDefined();
112
+ });
113
+ it('registers a module through addModule', async () => {
114
+ const fusion = await mockFramework((configurator) => {
115
+ configurator.addModule((c) => enableMsalMock(c, (builder) => builder.setAccount({ name: 'Grace Hopper' })));
116
+ });
117
+ expect(fusion.modules.auth.account?.name).toBe('Grace Hopper');
118
+ });
119
+ it('returns itself from addModule so calls can be chained', () => {
120
+ const configurator = new FrameworkMockConfigurator();
121
+ expect(configurator.addModule(() => undefined)).toBe(configurator);
122
+ });
123
+ it('exposes the same http configurator the http module is built from', async () => {
124
+ const configurator = new FrameworkMockConfigurator();
125
+ configurator.http.configureClient('my-api', { baseUri: 'http://localhost:6669' });
126
+ const fusion = await init(configurator);
127
+ expect(fusion.modules.http.createClient('my-api')).toBeDefined();
128
+ });
129
+ it('exposes the same services configurator the services module is built from', () => {
130
+ const configurator = new FrameworkMockConfigurator();
131
+ expect(configurator.services).toBeDefined();
132
+ });
133
+ it('exposes the same context configurator the context module is built from', () => {
134
+ const configurator = new FrameworkMockConfigurator();
135
+ expect(configurator.context).toBeDefined();
136
+ });
137
+ it('seeds the initial context through the context module, so `.context` backs the real module', async () => {
138
+ const fusion = await mockFramework((configurator) => {
139
+ configurator.context.setCurrentContext({
140
+ id: 'my-ctx',
141
+ type: { id: 'ProjectMaster' },
142
+ value: {},
143
+ });
144
+ });
145
+ expect(fusion.modules.context.currentContext).toMatchObject({ id: 'my-ctx' });
146
+ });
147
+ it('exposes the same telemetry configurator the telemetry module is built from', () => {
148
+ const configurator = new FrameworkMockConfigurator();
149
+ expect(configurator.telemetry).toBeDefined();
150
+ });
151
+ it('collects a tracked event through the telemetry mock adapter, reaching no real endpoint', async () => {
152
+ let mockConfigurator;
153
+ const fusion = await mockFramework((configurator) => {
154
+ mockConfigurator = configurator;
155
+ });
156
+ fusion.modules.telemetry.trackEvent({
157
+ name: 'button-click',
158
+ level: TelemetryLevel.Information,
159
+ scope: [],
160
+ });
161
+ await vi.waitFor(() => {
162
+ expect(mockConfigurator?.telemetry.adapter.getItems('button-click')).toHaveLength(1);
163
+ });
164
+ });
165
+ it('lets a module supplied through TModules get the same kind of accessor as msal and serviceDiscovery', async () => {
166
+ // Standing in for an application subclassing FrameworkMockConfigurator to
167
+ // expose its own module the same way the built-ins are exposed.
168
+ class AppMockConfigurator extends FrameworkMockConfigurator {
169
+ constructor() {
170
+ super();
171
+ this._pin(widgetsModule);
172
+ }
173
+ get widgets() {
174
+ return this._getConfig('widgets');
175
+ }
176
+ }
177
+ const configurator = new AppMockConfigurator();
178
+ configurator.widgets.setName('Ada');
179
+ const fusion = await init(configurator);
180
+ expect(fusion.modules.widgets.name).toBe('Ada');
181
+ });
182
+ it('pins the same instance across repeated reads, so declarations accumulate on one configurator', () => {
183
+ class AppMockConfigurator extends FrameworkMockConfigurator {
184
+ constructor() {
185
+ super();
186
+ this._pin(widgetsModule);
187
+ }
188
+ get widgets() {
189
+ return this._getConfig('widgets');
190
+ }
191
+ }
192
+ const configurator = new AppMockConfigurator();
193
+ expect(configurator.widgets).toBe(configurator.widgets);
194
+ });
195
+ it('throws from _getConfig when nothing was pinned for that name', () => {
196
+ class AppMockConfigurator extends FrameworkMockConfigurator {
197
+ readMissing() {
198
+ return this._getConfig('widgets');
199
+ }
200
+ }
201
+ expect(() => new AppMockConfigurator().readMissing()).toThrow(/widgets/);
202
+ });
203
+ it('throws from _pin when the module declares no configure factory', () => {
204
+ class AppMockConfigurator extends FrameworkMockConfigurator {
205
+ pinMissingConfigure() {
206
+ this._pin({ ...widgetsModule, configure: undefined });
207
+ }
208
+ }
209
+ expect(() => new AppMockConfigurator().pinMissingConfigure()).toThrow(/configure factory/);
210
+ });
211
+ });
212
+ //# sourceMappingURL=mock-framework.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-framework.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/mock-framework.test.ts"],"names":[],"mappings":"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"}
package/dist/esm/init.js CHANGED
@@ -37,8 +37,13 @@ export const init = async (configurator, ref) => {
37
37
  const fusion = {
38
38
  modules,
39
39
  };
40
- // Expose globally so portal shells and widgets can access the running instance
41
- window.Fusion = fusion;
40
+ // Expose globally so portal shells and widgets can access the running instance.
41
+ // Guarded because the framework must also initialize where no DOM exists, such as
42
+ // a test runner or a server-side render.
43
+ if (typeof window !== 'undefined') {
44
+ // Global exposure predates strict typing on `Window.Fusion`; the shape is guaranteed by this function's own construction above
45
+ window.Fusion = fusion;
46
+ }
42
47
  modules.event.dispatchEvent('onFrameworkLoaded', { detail: fusion });
43
48
  // The generic TModules type is erased on the plain object above; restore it for the return type
44
49
  return fusion;
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/init.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EACvB,YAA6C,EAC7C,GAAU,EACiB,EAAE;IAC7B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAgB,GAAG,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG;QACb,OAAO;KACR,CAAC;IACF,+EAA+E;IAC/E,MAAM,CAAC,MAAM,GAAG,MAA2B,CAAC;IAC5C,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAErE,gGAAgG;IAChG,OAAO,MAAqC,CAAC;AAC/C,CAAC,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/init.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EACvB,YAA6C,EAC7C,GAAU,EACiB,EAAE;IAC7B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAgB,GAAG,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG;QACb,OAAO;KACR,CAAC;IACF,gFAAgF;IAChF,kFAAkF;IAClF,yCAAyC;IACzC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,+HAA+H;QAC/H,MAAM,CAAC,MAAM,GAAG,MAA2B,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAErE,gGAAgG;IAChG,OAAO,MAAqC,CAAC;AAC/C,CAAC,CAAC;AAEF,eAAe,IAAI,CAAC"}