@equinor/fusion-framework-module-bookmark 4.0.3 → 4.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.
- package/CHANGELOG.md +59 -0
- package/README.md +42 -0
- package/dist/esm/BookmarkModuleConfigurator.js +2 -1
- package/dist/esm/BookmarkModuleConfigurator.js.map +1 -1
- package/dist/esm/__tests__/mock/bookmark-mock.test.js +151 -0
- package/dist/esm/__tests__/mock/bookmark-mock.test.js.map +1 -0
- package/dist/esm/mock/BookmarkMockClient.js +281 -0
- package/dist/esm/mock/BookmarkMockClient.js.map +1 -0
- package/dist/esm/mock/BookmarkMockConfigurator.js +138 -0
- package/dist/esm/mock/BookmarkMockConfigurator.js.map +1 -0
- package/dist/esm/mock/index.js +26 -0
- package/dist/esm/mock/index.js.map +1 -0
- package/dist/esm/mock/module.js +42 -0
- package/dist/esm/mock/module.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/BookmarkModuleConfigurator.d.ts +1 -0
- package/dist/types/__tests__/mock/bookmark-mock.test.d.ts +1 -0
- package/dist/types/mock/BookmarkMockClient.d.ts +121 -0
- package/dist/types/mock/BookmarkMockConfigurator.d.ts +86 -0
- package/dist/types/mock/index.d.ts +25 -0
- package/dist/types/mock/module.d.ts +43 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +20 -11
- package/src/BookmarkModuleConfigurator.ts +2 -1
- package/src/__tests__/mock/bookmark-mock.test.ts +199 -0
- package/src/mock/BookmarkMockClient.ts +310 -0
- package/src/mock/BookmarkMockConfigurator.ts +159 -0
- package/src/mock/index.ts +25 -0
- package/src/mock/module.ts +64 -0
- package/src/version.ts +1 -1
- package/vitest.config.ts +11 -0
|
@@ -15,6 +15,7 @@ import type { BookmarkModuleConfig } from './types';
|
|
|
15
15
|
*/
|
|
16
16
|
export declare class BookmarkModuleConfigurator extends BaseConfigBuilder<BookmarkModuleConfig> {
|
|
17
17
|
#private;
|
|
18
|
+
/** Default expiration time, in milliseconds, for bookmarks. */
|
|
18
19
|
defaultExpireTime: number;
|
|
19
20
|
/**
|
|
20
21
|
* Constructs a new `BookmarkModuleConfigurator`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type ObservableInput } from 'rxjs';
|
|
2
|
+
import type { BookmarkNew, BookmarksFilter, BookmarkUpdate, IBookmarkClient } from '../BookmarkClient.interface';
|
|
3
|
+
import type { Bookmark, BookmarkData, BookmarkWithoutData } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* In-memory {@link IBookmarkClient} backed by a `Map` of seeded bookmarks and a
|
|
6
|
+
* `Set` of favorite ids.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Used by {@link BookmarkMockConfigurator} to swap out the real API client
|
|
10
|
+
* while leaving `BookmarkProvider` and the `bookmark-flows/` epics running
|
|
11
|
+
* unmodified — every create, update, delete, and favorite call still goes
|
|
12
|
+
* through the real flow logic, only the underlying data source is in-memory.
|
|
13
|
+
*/
|
|
14
|
+
export declare class BookmarkMockClient implements IBookmarkClient {
|
|
15
|
+
#private;
|
|
16
|
+
/**
|
|
17
|
+
* Replaces the seeded bookmarks with the given collection, keyed by `id`.
|
|
18
|
+
*
|
|
19
|
+
* @param items - The bookmarks to seed. Replaces any previously seeded bookmarks.
|
|
20
|
+
*/
|
|
21
|
+
setBookmarks(items: Iterable<Bookmark>): void;
|
|
22
|
+
/**
|
|
23
|
+
* Marks a seeded bookmark as a favorite, or clears it.
|
|
24
|
+
*
|
|
25
|
+
* @param bookmarkId - The id of the bookmark to update.
|
|
26
|
+
* @param isFavorite - `true` to favorite the bookmark, `false` to clear it.
|
|
27
|
+
*/
|
|
28
|
+
setFavorite(bookmarkId: string, isFavorite: boolean): void;
|
|
29
|
+
/**
|
|
30
|
+
* Looks up a seeded bookmark by id without going through the `ObservableInput` API.
|
|
31
|
+
*
|
|
32
|
+
* @param bookmarkId - The id of the bookmark to look up.
|
|
33
|
+
* @returns The seeded bookmark, or `undefined` when no bookmark was seeded for that id.
|
|
34
|
+
*/
|
|
35
|
+
getBookmark(bookmarkId: string): Bookmark | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Returns every seeded bookmark that matches `filter`, without its payload.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* The real client strips payloads from list results (see `BookmarkClient.getAllBookmarks`),
|
|
41
|
+
* so the mock does the same here even though the interface's declared return type is `Bookmark[]`.
|
|
42
|
+
*
|
|
43
|
+
* @param filter - Optional constraints to narrow the returned bookmarks.
|
|
44
|
+
* @returns The matching bookmarks, without their payloads.
|
|
45
|
+
*/
|
|
46
|
+
getAllBookmarks(filter?: BookmarksFilter): ObservableInput<Array<Bookmark>>;
|
|
47
|
+
/**
|
|
48
|
+
* Returns a seeded bookmark by id, without its payload.
|
|
49
|
+
*
|
|
50
|
+
* @param bookmarkId - The id of the bookmark to look up.
|
|
51
|
+
* @returns The bookmark without its payload.
|
|
52
|
+
* @throws {Error} When no bookmark was seeded for `bookmarkId`.
|
|
53
|
+
*/
|
|
54
|
+
getBookmarkById(bookmarkId: string): ObservableInput<BookmarkWithoutData>;
|
|
55
|
+
/**
|
|
56
|
+
* Returns the payload data of a seeded bookmark.
|
|
57
|
+
*
|
|
58
|
+
* @template T - The shape of the bookmark's payload.
|
|
59
|
+
* @param bookmarkId - The id of the bookmark to look up.
|
|
60
|
+
* @returns The bookmark's payload.
|
|
61
|
+
* @throws {Error} When no bookmark was seeded for `bookmarkId`.
|
|
62
|
+
*/
|
|
63
|
+
getBookmarkData<T extends BookmarkData>(bookmarkId: string): ObservableInput<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Replaces the payload data of a seeded bookmark.
|
|
66
|
+
*
|
|
67
|
+
* @template T - The shape of the bookmark's payload.
|
|
68
|
+
* @param bookmarkId - The id of the bookmark to update.
|
|
69
|
+
* @param data - The new payload data.
|
|
70
|
+
* @returns The payload that was set.
|
|
71
|
+
* @throws {Error} When no bookmark was seeded for `bookmarkId`.
|
|
72
|
+
*/
|
|
73
|
+
setBookmarkData<T extends BookmarkData | null>(bookmarkId: string, data: T): ObservableInput<T>;
|
|
74
|
+
/**
|
|
75
|
+
* Marks a seeded bookmark as a favorite.
|
|
76
|
+
*
|
|
77
|
+
* @param bookmarkId - The id of the bookmark to favorite.
|
|
78
|
+
* @returns `true` once the bookmark is marked as a favorite.
|
|
79
|
+
* @throws {Error} When no bookmark was seeded for `bookmarkId`.
|
|
80
|
+
*/
|
|
81
|
+
addBookmarkToFavorites(bookmarkId: string): ObservableInput<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Clears a seeded bookmark's favorite status.
|
|
84
|
+
*
|
|
85
|
+
* @param bookmarkId - The id of the bookmark to unfavorite.
|
|
86
|
+
* @returns `true` once the bookmark is no longer a favorite.
|
|
87
|
+
*/
|
|
88
|
+
removeBookmarkFromFavorites(bookmarkId: string): ObservableInput<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Checks whether a seeded bookmark is currently a favorite.
|
|
91
|
+
*
|
|
92
|
+
* @param bookmarkId - The id of the bookmark to check.
|
|
93
|
+
* @returns `true` when the bookmark is a favorite.
|
|
94
|
+
*/
|
|
95
|
+
isBookmarkFavorite(bookmarkId: string): ObservableInput<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Seeds a new bookmark from create input, generating its id and audit fields.
|
|
98
|
+
*
|
|
99
|
+
* @template T - The shape of the bookmark's payload.
|
|
100
|
+
* @param bookmark - The data to create the bookmark from.
|
|
101
|
+
* @returns The created bookmark.
|
|
102
|
+
*/
|
|
103
|
+
createBookmark<T extends BookmarkData>(bookmark: BookmarkNew<T>): ObservableInput<Bookmark<T>>;
|
|
104
|
+
/**
|
|
105
|
+
* Applies updates to a seeded bookmark, stamping new audit fields.
|
|
106
|
+
*
|
|
107
|
+
* @template T - The shape of the bookmark's payload.
|
|
108
|
+
* @param bookmarkId - The id of the bookmark to update.
|
|
109
|
+
* @param updates - The fields to update.
|
|
110
|
+
* @returns The updated bookmark.
|
|
111
|
+
* @throws {Error} When no bookmark was seeded for `bookmarkId`.
|
|
112
|
+
*/
|
|
113
|
+
updateBookmark<T extends BookmarkData>(bookmarkId: string, updates: BookmarkUpdate<T>): ObservableInput<Bookmark<T>>;
|
|
114
|
+
/**
|
|
115
|
+
* Removes a seeded bookmark and clears its favorite status.
|
|
116
|
+
*
|
|
117
|
+
* @param bookmarkId - The id of the bookmark to delete.
|
|
118
|
+
* @returns `true` when a bookmark was seeded for `bookmarkId` and was removed.
|
|
119
|
+
*/
|
|
120
|
+
deleteBookmark(bookmarkId: string): ObservableInput<boolean>;
|
|
121
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ConfigBuilderCallbackArgs } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { BookmarkModuleConfigurator } from '../BookmarkModuleConfigurator';
|
|
3
|
+
import type { Bookmark, BookmarkModuleConfig } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* The real bookmark configurator, backed by an in-memory {@link BookmarkMockClient}.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Extends {@link BookmarkModuleConfigurator} directly, so the whole builder API
|
|
9
|
+
* (source system, filters, resolvers, `setParent`) stays available. Only a
|
|
10
|
+
* default client is added — through the same
|
|
11
|
+
* {@link BookmarkModuleConfigurator.setClient | setClient} seam a caller would
|
|
12
|
+
* use to plug in their own client — so an explicit `setClient()` call still
|
|
13
|
+
* replaces it outright, exactly as it would on the real configurator.
|
|
14
|
+
*
|
|
15
|
+
* Seeded bookmarks and favorites flow through the real `BookmarkProvider` and
|
|
16
|
+
* `bookmark-flows/` epics unmodified: create, update, delete, and favorite
|
|
17
|
+
* calls all reach {@link BookmarkMockClient}, not a stand-in.
|
|
18
|
+
*
|
|
19
|
+
* @example Seed bookmarks and a current bookmark
|
|
20
|
+
* ```typescript
|
|
21
|
+
* enableBookmarkMock(configurator, (builder) => {
|
|
22
|
+
* builder.setBookmarks([myBookmark]);
|
|
23
|
+
* builder.setCurrentBookmark(myBookmark.id);
|
|
24
|
+
* builder.setFavorite(myBookmark.id, true);
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example Take full control of the client
|
|
29
|
+
* ```typescript
|
|
30
|
+
* enableBookmarkMock(configurator, (builder) => {
|
|
31
|
+
* builder.setClient(myOwnBookmarkClient);
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class BookmarkMockConfigurator extends BookmarkModuleConfigurator {
|
|
36
|
+
#private;
|
|
37
|
+
/**
|
|
38
|
+
* Seeds the bookmarks the mock client resolves through `getAllBookmarks`,
|
|
39
|
+
* `getBookmarkById`, and `getBookmarkData`.
|
|
40
|
+
*
|
|
41
|
+
* @param items - The bookmarks to seed. Replaces any previously seeded bookmarks.
|
|
42
|
+
* @returns `this`, for chaining.
|
|
43
|
+
*/
|
|
44
|
+
setBookmarks(items: Iterable<Bookmark>): this;
|
|
45
|
+
/**
|
|
46
|
+
* Declares which seeded bookmark `IBookmarkProvider.currentBookmark` reports
|
|
47
|
+
* as active once the module initializes.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* `BookmarkProvider` only reads its initial current bookmark from a parent
|
|
51
|
+
* provider (see {@link BookmarkModuleConfigurator.setParent}), so this
|
|
52
|
+
* registers a synthetic parent exposing the seeded bookmark — the same seam
|
|
53
|
+
* a real nested-portal provider would use, just standing in for one.
|
|
54
|
+
*
|
|
55
|
+
* @param bookmarkId - The id of a bookmark previously passed to {@link setBookmarks},
|
|
56
|
+
* or `undefined` to leave the current bookmark unset.
|
|
57
|
+
* @returns `this`, for chaining.
|
|
58
|
+
*/
|
|
59
|
+
setCurrentBookmark(bookmarkId: string | undefined): this;
|
|
60
|
+
/**
|
|
61
|
+
* Marks a seeded bookmark as a favorite, or clears it, reflected through
|
|
62
|
+
* `IBookmarkClient.isBookmarkFavorite`.
|
|
63
|
+
*
|
|
64
|
+
* @param bookmarkId - The id of the bookmark to update.
|
|
65
|
+
* @param isFavorite - `true` to favorite the bookmark, `false` to clear it. Defaults to `true`.
|
|
66
|
+
* @returns `this`, for chaining.
|
|
67
|
+
*/
|
|
68
|
+
setFavorite(bookmarkId: string, isFavorite?: boolean): this;
|
|
69
|
+
/**
|
|
70
|
+
* Installs the mock client and a synthetic current-bookmark parent before
|
|
71
|
+
* building the configuration, unless the caller already declared their own.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* Also falls back to trivial application/context resolvers when neither an
|
|
75
|
+
* `app` nor a `context` module is registered alongside this mock — both are
|
|
76
|
+
* required by {@link BookmarkModuleConfig}'s schema, but a standalone test
|
|
77
|
+
* has no reason to pull in either module just to satisfy it. Registering a
|
|
78
|
+
* real `app`/`context` module still wins, exactly as it does on the real
|
|
79
|
+
* configurator.
|
|
80
|
+
*
|
|
81
|
+
* @param init - The config builder callback arguments.
|
|
82
|
+
* @param initial - An optional initial config to merge into the returned config.
|
|
83
|
+
* @returns The observable configuration, produced by the real configurator.
|
|
84
|
+
*/
|
|
85
|
+
protected _createConfig(init: ConfigBuilderCallbackArgs, initial?: Partial<BookmarkModuleConfig>): import("rxjs").ObservableInput<BookmarkModuleConfig>;
|
|
86
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock bookmark module for tests: real provider, real configurator, in-memory client.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Substituting `IBookmarkClient` is the smallest change that removes the
|
|
6
|
+
* Fusion Core Services backend from a test. Everything above it — the
|
|
7
|
+
* `BookmarkProvider` store, `bookmark-flows/` epics, payload generators, and
|
|
8
|
+
* events — is the production code path.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { enableBookmarkMock } from '@equinor/fusion-framework-module-bookmark/mock';
|
|
13
|
+
*
|
|
14
|
+
* enableBookmarkMock(configurator, (builder) => {
|
|
15
|
+
* builder.setBookmarks([myBookmark]);
|
|
16
|
+
* builder.setCurrentBookmark(myBookmark.id);
|
|
17
|
+
* builder.setFavorite(myBookmark.id, true);
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @packageDocumentation
|
|
22
|
+
*/
|
|
23
|
+
export { BookmarkMockClient } from './BookmarkMockClient';
|
|
24
|
+
export { BookmarkMockConfigurator } from './BookmarkMockConfigurator';
|
|
25
|
+
export { enableBookmarkMock, bookmarkMockModule, type BookmarkMockConfigFn } from './module';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AnyModule, IModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { type BookmarkModule } from '../bookmark-module';
|
|
3
|
+
import { BookmarkMockConfigurator } from './BookmarkMockConfigurator';
|
|
4
|
+
/**
|
|
5
|
+
* The bookmark module with a mock, in-memory client instead of a live
|
|
6
|
+
* `@equinor/fusion-framework-module-services` connection.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Only `configure` differs from the real module. `initialize` is the
|
|
10
|
+
* production one, untouched, so the real `BookmarkProvider` and
|
|
11
|
+
* `bookmark-flows/` epics run exactly as they do in production — a test
|
|
12
|
+
* observes the real create/update/delete/favorite flow, not a rehearsal of it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const bookmarkMockModule: BookmarkModule;
|
|
15
|
+
/**
|
|
16
|
+
* Configuration callback for {@link enableBookmarkMock}.
|
|
17
|
+
*
|
|
18
|
+
* @template TRef - Reference type forwarded to the callback, inferred from the configurator.
|
|
19
|
+
*/
|
|
20
|
+
export type BookmarkMockConfigFn<TRef = unknown> = (configurator: BookmarkMockConfigurator, ref?: TRef) => void | Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Enables the bookmark module against an in-memory mock client, so a test
|
|
23
|
+
* needs no network and no real Fusion Core Services backend.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Registered last, this replaces whichever bookmark module the configurator
|
|
27
|
+
* already carries, so it works on a `FrameworkConfigurator` that pre-registers
|
|
28
|
+
* the real one.
|
|
29
|
+
*
|
|
30
|
+
* @param configurator - The modules configurator to register on.
|
|
31
|
+
* @param configure - Optional callback to seed bookmarks, the current bookmark, or favorites.
|
|
32
|
+
* @template TModules - The array of module descriptors managed by `configurator`.
|
|
33
|
+
* @template TRef - Reference type forwarded to `configure`, inferred from `configurator`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* enableBookmarkMock(configurator, (builder) => {
|
|
38
|
+
* builder.setBookmarks([myBookmark]);
|
|
39
|
+
* builder.setCurrentBookmark(myBookmark.id);
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare const enableBookmarkMock: <TModules extends Array<AnyModule> = Array<AnyModule>, TRef = unknown>(configurator: IModulesConfigurator<TModules, TRef>, configure?: BookmarkMockConfigFn<TRef>) => void;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.0.
|
|
1
|
+
export declare const version = "4.1.0-next.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-bookmark",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.1.0-next.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./utils": {
|
|
14
14
|
"import": "./dist/esm/utils/index.js",
|
|
15
15
|
"types": "./dist/types/utils/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./mock": {
|
|
18
|
+
"import": "./dist/esm/mock/index.js",
|
|
19
|
+
"types": "./dist/types/mock/index.d.ts"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"typesVersions": {
|
|
@@ -22,6 +26,9 @@
|
|
|
22
26
|
],
|
|
23
27
|
"utils": [
|
|
24
28
|
"dist/types/utils/index.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"mock": [
|
|
31
|
+
"dist/types/mock/index.d.ts"
|
|
25
32
|
]
|
|
26
33
|
}
|
|
27
34
|
},
|
|
@@ -41,21 +48,23 @@
|
|
|
41
48
|
"immer": "^11.0.0",
|
|
42
49
|
"rxjs": "^7.8.1",
|
|
43
50
|
"uuid": "^14.0.0",
|
|
44
|
-
"@equinor/fusion-framework-module": "^6.1.
|
|
45
|
-
"@equinor/fusion-
|
|
46
|
-
"@equinor/fusion-
|
|
47
|
-
"@equinor/fusion-
|
|
51
|
+
"@equinor/fusion-framework-module": "^6.1.3-next.0",
|
|
52
|
+
"@equinor/fusion-log": "^2.0.3-next.0",
|
|
53
|
+
"@equinor/fusion-query": "^7.0.4-next.0",
|
|
54
|
+
"@equinor/fusion-observable": "^9.1.2-next.0"
|
|
48
55
|
},
|
|
49
56
|
"devDependencies": {
|
|
50
57
|
"typescript": "^7.0.2",
|
|
58
|
+
"vitest": "^4.1.10",
|
|
51
59
|
"zod": "^4.4.3",
|
|
52
|
-
"@equinor/fusion-framework-module-app": "^8.0.
|
|
53
|
-
"@equinor/fusion-framework-module-
|
|
54
|
-
"@equinor/fusion-framework-module-
|
|
55
|
-
"@equinor/fusion-framework-module-http": "^8.0.
|
|
56
|
-
"@equinor/fusion-framework-module-services": "^8.0
|
|
60
|
+
"@equinor/fusion-framework-module-app": "^8.1.0-next.0",
|
|
61
|
+
"@equinor/fusion-framework-module-context": "^9.0.0-next.0",
|
|
62
|
+
"@equinor/fusion-framework-module-event": "^6.1.0-next.0",
|
|
63
|
+
"@equinor/fusion-framework-module-http": "^8.1.0-next.0",
|
|
64
|
+
"@equinor/fusion-framework-module-services": "^8.1.1-next.0"
|
|
57
65
|
},
|
|
58
66
|
"scripts": {
|
|
59
|
-
"build": "tsc -b"
|
|
67
|
+
"build": "tsc -b",
|
|
68
|
+
"test": "vitest run"
|
|
60
69
|
}
|
|
61
70
|
}
|
|
@@ -43,7 +43,8 @@ const parseInitialBookmarkConfigConfig = (initial?: unknown): BookmarkModuleConf
|
|
|
43
43
|
*/
|
|
44
44
|
export class BookmarkModuleConfigurator extends BaseConfigBuilder<BookmarkModuleConfig> {
|
|
45
45
|
#log?: ILogger;
|
|
46
|
-
|
|
46
|
+
/** Default expiration time, in milliseconds, for bookmarks. */
|
|
47
|
+
defaultExpireTime = 1 * 60 * 1000;
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* Constructs a new `BookmarkModuleConfigurator`.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { firstValueFrom, from, type ObservableInput } from 'rxjs';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { ModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
5
|
+
|
|
6
|
+
import { BookmarkModuleConfigurator } from '../../BookmarkModuleConfigurator';
|
|
7
|
+
import { BookmarkProvider } from '../../BookmarkProvider';
|
|
8
|
+
import type { IBookmarkProvider } from '../../BookmarkProvider.interface';
|
|
9
|
+
import { module as realModule } from '../../bookmark-module';
|
|
10
|
+
import type { Bookmark } from '../../types';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
BookmarkMockClient,
|
|
14
|
+
BookmarkMockConfigurator,
|
|
15
|
+
bookmarkMockModule,
|
|
16
|
+
enableBookmarkMock,
|
|
17
|
+
} from '../../mock';
|
|
18
|
+
|
|
19
|
+
/** A fully-formed seeded bookmark, so tests only override what they care about. */
|
|
20
|
+
const createBookmark = (overrides: Partial<Bookmark> = {}): Bookmark => ({
|
|
21
|
+
id: 'bookmark-1',
|
|
22
|
+
name: 'My Bookmark',
|
|
23
|
+
appKey: 'test-app',
|
|
24
|
+
created: new Date('2024-01-01T00:00:00.000Z'),
|
|
25
|
+
createdBy: { id: 'seed-user', name: 'Seed User' },
|
|
26
|
+
...overrides,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initializes the mock module through the real module system.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Deliberately avoids hand-building initialization arguments — faking the
|
|
34
|
+
* module system is the very cost this mock removes.
|
|
35
|
+
*
|
|
36
|
+
* @param configure - Optional callback to seed bookmarks, current bookmark, or favorites.
|
|
37
|
+
* @returns The `IBookmarkProvider` the module produced.
|
|
38
|
+
*/
|
|
39
|
+
const initializeMockWith = async (
|
|
40
|
+
configure?: (builder: BookmarkMockConfigurator) => void,
|
|
41
|
+
): Promise<IBookmarkProvider> => {
|
|
42
|
+
const configurator = new ModulesConfigurator([]);
|
|
43
|
+
enableBookmarkMock(configurator, configure);
|
|
44
|
+
const instances = await configurator.initialize();
|
|
45
|
+
return (instances as unknown as { bookmark: IBookmarkProvider }).bookmark;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the first value of an `IBookmarkProvider` call.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* `IBookmarkProvider`'s methods are typed as `ObservableInput`, not `Observable`,
|
|
53
|
+
* so this wraps with `from()` before handing off to `firstValueFrom`.
|
|
54
|
+
*
|
|
55
|
+
* @template T - The value the observable input emits.
|
|
56
|
+
* @param input - The observable input to resolve.
|
|
57
|
+
* @returns A promise resolving to the first emitted value.
|
|
58
|
+
*/
|
|
59
|
+
const firstValue = <T>(input: ObservableInput<T>): Promise<T> => firstValueFrom(from(input));
|
|
60
|
+
|
|
61
|
+
describe('bookmarkMockModule', () => {
|
|
62
|
+
it('changes nothing but the configurator', () => {
|
|
63
|
+
expect(bookmarkMockModule.name).toBe(realModule.name);
|
|
64
|
+
expect(bookmarkMockModule.version).toBe(realModule.version);
|
|
65
|
+
// The production initializer, untouched — the mock has no lifecycle of its own
|
|
66
|
+
expect(bookmarkMockModule.initialize).toBe(realModule.initialize);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('builds a real BookmarkModuleConfigurator, so the whole builder stays available', () => {
|
|
70
|
+
const configurator = bookmarkMockModule.configure?.();
|
|
71
|
+
|
|
72
|
+
expect(configurator).toBeInstanceOf(BookmarkModuleConfigurator);
|
|
73
|
+
expect(configurator).toBeInstanceOf(BookmarkMockConfigurator);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('enableBookmarkMock', () => {
|
|
78
|
+
it('produces the real BookmarkProvider, not a stand-in', async () => {
|
|
79
|
+
const provider = await initializeMockWith();
|
|
80
|
+
|
|
81
|
+
expect(provider).toBeInstanceOf(BookmarkProvider);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('replaces a bookmark module that is already registered', async () => {
|
|
85
|
+
// A FrameworkConfigurator pre-registers the real bookmark module, so the
|
|
86
|
+
// mock is only useful if registering it afterwards wins
|
|
87
|
+
const configurator = new ModulesConfigurator([realModule]);
|
|
88
|
+
enableBookmarkMock(configurator, (builder) => {
|
|
89
|
+
builder.setBookmarks([createBookmark()]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const instances = await configurator.initialize();
|
|
93
|
+
const provider = (instances as unknown as { bookmark: IBookmarkProvider }).bookmark;
|
|
94
|
+
|
|
95
|
+
const bookmarks = await firstValue(provider.getAllBookmarks());
|
|
96
|
+
expect(bookmarks).toHaveLength(1);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('reflects seeded bookmarks with no real HTTP', async () => {
|
|
100
|
+
const seeded = createBookmark();
|
|
101
|
+
const provider = await initializeMockWith((builder) => {
|
|
102
|
+
builder.setBookmarks([seeded]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const bookmarks = await firstValue(provider.getAllBookmarks());
|
|
106
|
+
expect(bookmarks.map((b) => b.id)).toEqual([seeded.id]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('reflects the seeded current bookmark', async () => {
|
|
110
|
+
const seeded = createBookmark();
|
|
111
|
+
const provider = await initializeMockWith((builder) => {
|
|
112
|
+
builder.setBookmarks([seeded]);
|
|
113
|
+
builder.setCurrentBookmark(seeded.id);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(provider.currentBookmark?.id).toBe(seeded.id);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('leaves the current bookmark unset when none is seeded', async () => {
|
|
120
|
+
const provider = await initializeMockWith((builder) => {
|
|
121
|
+
builder.setBookmarks([createBookmark()]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(provider.currentBookmark).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('reflects a seeded favorite', async () => {
|
|
128
|
+
const seeded = createBookmark();
|
|
129
|
+
const provider = await initializeMockWith((builder) => {
|
|
130
|
+
builder.setBookmarks([seeded]);
|
|
131
|
+
builder.setFavorite(seeded.id, true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await expect(firstValue(provider.isBookmarkInFavorites(seeded.id))).resolves.toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('creates a bookmark through the real create flow', async () => {
|
|
138
|
+
const provider = await initializeMockWith();
|
|
139
|
+
// without a payload generator, generatePayload() never emits and the create
|
|
140
|
+
// flow would hang forever waiting on it
|
|
141
|
+
provider.addPayloadGenerator(() => {});
|
|
142
|
+
|
|
143
|
+
const created = await firstValue(
|
|
144
|
+
provider.createBookmark({ name: 'New Bookmark', appKey: 'test-app' }),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expect(created.name).toBe('New Bookmark');
|
|
148
|
+
const bookmarks = await firstValue(provider.getAllBookmarks());
|
|
149
|
+
expect(bookmarks.map((b) => b.id)).toContain(created.id);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('updates a bookmark through the real update flow', async () => {
|
|
153
|
+
const seeded = createBookmark();
|
|
154
|
+
const provider = await initializeMockWith((builder) => {
|
|
155
|
+
builder.setBookmarks([seeded]);
|
|
156
|
+
});
|
|
157
|
+
// the update reducer only applies to bookmarks already tracked in the store,
|
|
158
|
+
// so fetch once first to seed it
|
|
159
|
+
await firstValue(provider.getAllBookmarks());
|
|
160
|
+
|
|
161
|
+
const updated = await firstValue(
|
|
162
|
+
provider.updateBookmark(seeded.id, { name: 'Renamed' }, { excludePayloadGeneration: true }),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
expect(updated.name).toBe('Renamed');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('surfaces a missing bookmark as an observable error through the real fetch flow', async () => {
|
|
169
|
+
const provider = await initializeMockWith();
|
|
170
|
+
|
|
171
|
+
await expect(firstValue(provider.getBookmark('missing-bookmark'))).rejects.toThrow();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('deletes a bookmark through the real delete flow', async () => {
|
|
175
|
+
const seeded = createBookmark();
|
|
176
|
+
const provider = await initializeMockWith((builder) => {
|
|
177
|
+
builder.setBookmarks([seeded]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
await firstValue(provider.deleteBookmark(seeded.id));
|
|
181
|
+
|
|
182
|
+
const bookmarks = await firstValue(provider.getAllBookmarks());
|
|
183
|
+
expect(bookmarks.map((b) => b.id)).not.toContain(seeded.id);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('lets setClient() replace the mock client outright', async () => {
|
|
187
|
+
const ownClient = new BookmarkMockClient();
|
|
188
|
+
ownClient.setBookmarks([createBookmark({ id: 'own-bookmark' })]);
|
|
189
|
+
|
|
190
|
+
const provider = await initializeMockWith((builder) => {
|
|
191
|
+
// the escape hatch is inherited unchanged from BookmarkModuleConfigurator
|
|
192
|
+
builder.setBookmarks([createBookmark({ id: 'ignored-bookmark' })]);
|
|
193
|
+
builder.setClient(ownClient);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const bookmarks = await firstValue(provider.getAllBookmarks());
|
|
197
|
+
expect(bookmarks.map((b) => b.id)).toEqual(['own-bookmark']);
|
|
198
|
+
});
|
|
199
|
+
});
|