@angular-architects/ngrx-toolkit 19.4.0 → 19.4.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.
Files changed (128) hide show
  1. package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +119 -0
  2. package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +1 -0
  3. package/fesm2022/angular-architects-ngrx-toolkit.mjs +2108 -0
  4. package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
  5. package/{src/index.ts → index.d.ts} +5 -32
  6. package/lib/assertions/assertions.d.ts +2 -0
  7. package/{src/lib/devtools/features/with-disabled-name-indicies.ts → lib/devtools/features/with-disabled-name-indicies.d.ts} +1 -5
  8. package/{src/lib/devtools/features/with-glitch-tracking.ts → lib/devtools/features/with-glitch-tracking.d.ts} +1 -6
  9. package/{src/lib/devtools/features/with-mapper.ts → lib/devtools/features/with-mapper.d.ts} +1 -7
  10. package/lib/devtools/internal/current-action-names.d.ts +1 -0
  11. package/lib/devtools/internal/default-tracker.d.ts +13 -0
  12. package/lib/devtools/internal/devtools-feature.d.ts +24 -0
  13. package/lib/devtools/internal/devtools-syncer.service.d.ts +39 -0
  14. package/lib/devtools/internal/glitch-tracker.service.d.ts +18 -0
  15. package/lib/devtools/internal/models.d.ts +24 -0
  16. package/{src/lib/devtools/provide-devtools-config.ts → lib/devtools/provide-devtools-config.d.ts} +4 -16
  17. package/lib/devtools/rename-devtools-name.d.ts +7 -0
  18. package/lib/devtools/update-state.d.ts +15 -0
  19. package/{src/lib/devtools/with-dev-tools-stub.ts → lib/devtools/with-dev-tools-stub.d.ts} +1 -2
  20. package/lib/devtools/with-devtools.d.ts +24 -0
  21. package/lib/flattening-operator.d.ts +14 -0
  22. package/lib/immutable-state/deep-freeze.d.ts +11 -0
  23. package/lib/immutable-state/is-dev-mode.d.ts +1 -0
  24. package/lib/immutable-state/with-immutable-state.d.ts +60 -0
  25. package/lib/mutation/http-mutation.d.ts +86 -0
  26. package/lib/mutation/mutation.d.ts +20 -0
  27. package/lib/mutation/rx-mutation.d.ts +76 -0
  28. package/{src/lib/shared/signal-store-models.ts → lib/shared/signal-store-models.d.ts} +4 -8
  29. package/lib/shared/throw-if-null.d.ts +1 -0
  30. package/lib/storage-sync/features/with-indexed-db.d.ts +2 -0
  31. package/lib/storage-sync/features/with-local-storage.d.ts +3 -0
  32. package/lib/storage-sync/internal/indexeddb.service.d.ts +29 -0
  33. package/lib/storage-sync/internal/local-storage.service.d.ts +8 -0
  34. package/lib/storage-sync/internal/models.d.ts +45 -0
  35. package/lib/storage-sync/internal/session-storage.service.d.ts +8 -0
  36. package/lib/storage-sync/with-storage-sync.d.ts +45 -0
  37. package/lib/with-call-state.d.ts +58 -0
  38. package/{src/lib/with-conditional.ts → lib/with-conditional.d.ts} +7 -31
  39. package/lib/with-data-service.d.ts +109 -0
  40. package/{src/lib/with-feature-factory.ts → lib/with-feature-factory.d.ts} +4 -32
  41. package/lib/with-mutations.d.ts +51 -0
  42. package/lib/with-pagination.d.ts +98 -0
  43. package/lib/with-redux.d.ts +147 -0
  44. package/lib/with-reset.d.ts +29 -0
  45. package/lib/with-undo-redo.d.ts +31 -0
  46. package/package.json +21 -4
  47. package/redux-connector/index.d.ts +2 -0
  48. package/redux-connector/src/lib/create-redux.d.ts +13 -0
  49. package/redux-connector/src/lib/model.d.ts +40 -0
  50. package/redux-connector/src/lib/rxjs-interop/redux-method.d.ts +14 -0
  51. package/redux-connector/src/lib/signal-redux-store.d.ts +11 -0
  52. package/redux-connector/src/lib/util.d.ts +5 -0
  53. package/eslint.config.cjs +0 -43
  54. package/jest.config.ts +0 -22
  55. package/ng-package.json +0 -7
  56. package/project.json +0 -37
  57. package/redux-connector/docs/README.md +0 -131
  58. package/redux-connector/index.ts +0 -6
  59. package/redux-connector/ng-package.json +0 -5
  60. package/redux-connector/src/lib/create-redux.ts +0 -102
  61. package/redux-connector/src/lib/model.ts +0 -89
  62. package/redux-connector/src/lib/rxjs-interop/redux-method.ts +0 -66
  63. package/redux-connector/src/lib/signal-redux-store.ts +0 -59
  64. package/redux-connector/src/lib/util.ts +0 -22
  65. package/src/lib/assertions/assertions.ts +0 -9
  66. package/src/lib/devtools/internal/current-action-names.ts +0 -1
  67. package/src/lib/devtools/internal/default-tracker.ts +0 -60
  68. package/src/lib/devtools/internal/devtools-feature.ts +0 -37
  69. package/src/lib/devtools/internal/devtools-syncer.service.ts +0 -202
  70. package/src/lib/devtools/internal/glitch-tracker.service.ts +0 -61
  71. package/src/lib/devtools/internal/models.ts +0 -29
  72. package/src/lib/devtools/rename-devtools-name.ts +0 -21
  73. package/src/lib/devtools/tests/action-name.spec.ts +0 -48
  74. package/src/lib/devtools/tests/basic.spec.ts +0 -111
  75. package/src/lib/devtools/tests/connecting.spec.ts +0 -37
  76. package/src/lib/devtools/tests/helpers.spec.ts +0 -43
  77. package/src/lib/devtools/tests/naming.spec.ts +0 -216
  78. package/src/lib/devtools/tests/provide-devtools-config.spec.ts +0 -25
  79. package/src/lib/devtools/tests/types.spec.ts +0 -19
  80. package/src/lib/devtools/tests/update-state.spec.ts +0 -29
  81. package/src/lib/devtools/tests/with-devtools.spec.ts +0 -5
  82. package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +0 -272
  83. package/src/lib/devtools/tests/with-mapper.spec.ts +0 -69
  84. package/src/lib/devtools/update-state.ts +0 -38
  85. package/src/lib/devtools/with-devtools.ts +0 -81
  86. package/src/lib/flattening-operator.ts +0 -42
  87. package/src/lib/immutable-state/deep-freeze.ts +0 -43
  88. package/src/lib/immutable-state/is-dev-mode.ts +0 -6
  89. package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +0 -260
  90. package/src/lib/immutable-state/with-immutable-state.ts +0 -115
  91. package/src/lib/mutation/http-mutation.spec.ts +0 -473
  92. package/src/lib/mutation/http-mutation.ts +0 -172
  93. package/src/lib/mutation/mutation.ts +0 -26
  94. package/src/lib/mutation/rx-mutation.spec.ts +0 -594
  95. package/src/lib/mutation/rx-mutation.ts +0 -208
  96. package/src/lib/shared/prettify.ts +0 -3
  97. package/src/lib/shared/throw-if-null.ts +0 -7
  98. package/src/lib/storage-sync/features/with-indexed-db.ts +0 -81
  99. package/src/lib/storage-sync/features/with-local-storage.ts +0 -58
  100. package/src/lib/storage-sync/internal/indexeddb.service.ts +0 -124
  101. package/src/lib/storage-sync/internal/local-storage.service.ts +0 -19
  102. package/src/lib/storage-sync/internal/models.ts +0 -62
  103. package/src/lib/storage-sync/internal/session-storage.service.ts +0 -18
  104. package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +0 -99
  105. package/src/lib/storage-sync/tests/with-storage-async.spec.ts +0 -308
  106. package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -268
  107. package/src/lib/storage-sync/with-storage-sync.ts +0 -233
  108. package/src/lib/with-call-state.spec.ts +0 -42
  109. package/src/lib/with-call-state.ts +0 -195
  110. package/src/lib/with-conditional.spec.ts +0 -125
  111. package/src/lib/with-data-service.spec.ts +0 -564
  112. package/src/lib/with-data-service.ts +0 -433
  113. package/src/lib/with-feature-factory.spec.ts +0 -69
  114. package/src/lib/with-mutations.spec.ts +0 -537
  115. package/src/lib/with-mutations.ts +0 -146
  116. package/src/lib/with-pagination.spec.ts +0 -90
  117. package/src/lib/with-pagination.ts +0 -353
  118. package/src/lib/with-redux.spec.ts +0 -258
  119. package/src/lib/with-redux.ts +0 -387
  120. package/src/lib/with-reset.spec.ts +0 -112
  121. package/src/lib/with-reset.ts +0 -62
  122. package/src/lib/with-undo-redo.spec.ts +0 -287
  123. package/src/lib/with-undo-redo.ts +0 -199
  124. package/src/test-setup.ts +0 -8
  125. package/tsconfig.json +0 -29
  126. package/tsconfig.lib.json +0 -17
  127. package/tsconfig.lib.prod.json +0 -9
  128. package/tsconfig.spec.json +0 -17
@@ -1,60 +0,0 @@
1
- import { effect, Injectable, signal } from '@angular/core';
2
- import { getState, StateSource } from '@ngrx/signals';
3
- import { Tracker, TrackerStores } from './models';
4
-
5
- @Injectable({ providedIn: 'root' })
6
- export class DefaultTracker implements Tracker {
7
- readonly #stores = signal<TrackerStores>({});
8
-
9
- get stores(): TrackerStores {
10
- return this.#stores();
11
- }
12
-
13
- #trackCallback: undefined | ((changedState: Record<string, object>) => void);
14
-
15
- #trackingEffect = effect(() => {
16
- if (this.#trackCallback === undefined) {
17
- throw new Error('no callback function defined');
18
- }
19
- const stores = this.#stores();
20
-
21
- const fullState = Object.entries(stores).reduce(
22
- (acc, [id, store]) => {
23
- return { ...acc, [id]: getState(store) };
24
- },
25
- {} as Record<string, object>,
26
- );
27
-
28
- this.#trackCallback(fullState);
29
- });
30
-
31
- track(id: string, store: StateSource<object>): void {
32
- this.#stores.update((value) => ({
33
- ...value,
34
- [id]: store,
35
- }));
36
- }
37
-
38
- onChange(callback: (changedState: Record<string, object>) => void): void {
39
- this.#trackCallback = callback;
40
- }
41
-
42
- removeStore(id: string) {
43
- this.#stores.update((stores) =>
44
- Object.entries(stores).reduce((newStore, [storeId, state]) => {
45
- if (storeId !== id) {
46
- newStore[storeId] = state;
47
- }
48
- return newStore;
49
- }, {} as TrackerStores),
50
- );
51
- }
52
-
53
- notifyRenamedStore(id: string): void {
54
- if (this.#stores()[id]) {
55
- this.#stores.update((stores) => {
56
- return { ...stores };
57
- });
58
- }
59
- }
60
- }
@@ -1,37 +0,0 @@
1
- import { Tracker } from './models';
2
-
3
- export const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');
4
-
5
- export type Mapper = (state: object) => object;
6
-
7
- export type DevtoolsOptions = {
8
- indexNames?: boolean; // defines if names should be indexed.
9
- map?: Mapper; // defines a mapper for the state.
10
- tracker?: new () => Tracker; // defines a tracker for the state
11
- };
12
-
13
- export type DevtoolsInnerOptions = {
14
- indexNames: boolean;
15
- map: Mapper;
16
- tracker: Tracker;
17
- };
18
-
19
- /**
20
- * A DevtoolsFeature adds or modifies the behavior of the
21
- * devtools extension.
22
- *
23
- * We use them (function calls) instead of a config object,
24
- * because of tree-shaking.
25
- */
26
- export type DevtoolsFeature = {
27
- [DEVTOOLS_FEATURE]: true;
28
- } & Partial<DevtoolsOptions>;
29
-
30
- export function createDevtoolsFeature(
31
- options: DevtoolsOptions,
32
- ): DevtoolsFeature {
33
- return {
34
- [DEVTOOLS_FEATURE]: true,
35
- ...options,
36
- };
37
- }
@@ -1,202 +0,0 @@
1
- import { isPlatformBrowser } from '@angular/common';
2
- import { inject, Injectable, OnDestroy, PLATFORM_ID } from '@angular/core';
3
- import { StateSource } from '@ngrx/signals';
4
- import { throwIfNull } from '../../shared/throw-if-null';
5
- import { REDUX_DEVTOOLS_CONFIG } from '../provide-devtools-config';
6
- import { currentActionNames } from './current-action-names';
7
- import { DevtoolsInnerOptions } from './devtools-feature';
8
- import { Connection, StoreRegistry, Tracker } from './models';
9
-
10
- const dummyConnection: Connection = {
11
- send: () => void true,
12
- };
13
-
14
- /**
15
- * A service provided by the root injector is
16
- * required because the synchronization runs
17
- * globally.
18
- *
19
- * The SignalStore could be provided in a component.
20
- * If the effect starts in the injection
21
- * context of the SignalStore, the complete sync
22
- * process would shut down once the component gets
23
- * destroyed.
24
- */
25
- @Injectable({ providedIn: 'root' })
26
- export class DevtoolsSyncer implements OnDestroy {
27
- /**
28
- * Stores all SignalStores that are connected to the
29
- * DevTools along their options, names and id.
30
- */
31
- #stores: StoreRegistry = {};
32
- readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
33
- readonly #trackers = [] as Tracker[];
34
- readonly #devtoolsConfig = {
35
- name: 'NgRx SignalStore',
36
- ...inject(REDUX_DEVTOOLS_CONFIG, { optional: true }),
37
- };
38
-
39
- /**
40
- * Maintains the current states of all stores to avoid conflicts
41
- * between glitch-free and glitched trackers when used simultaneously.
42
- *
43
- * The challenge lies in ensuring that glitched trackers do not
44
- * interfere with the synchronization process of glitch-free trackers.
45
- * Specifically, glitched trackers could cause the synchronization to
46
- * read the current state of stores managed by glitch-free trackers.
47
- *
48
- * Therefore, the synchronization process doesn't read the state from
49
- * each store, but relies on #currentState.
50
- *
51
- * Please note, that here the key is the name and not the id.
52
- */
53
- #currentState: Record<string, object> = {};
54
- #currentId = 1;
55
-
56
- readonly #connection: Connection = this.#isBrowser
57
- ? window.__REDUX_DEVTOOLS_EXTENSION__
58
- ? window.__REDUX_DEVTOOLS_EXTENSION__.connect(this.#devtoolsConfig)
59
- : dummyConnection
60
- : dummyConnection;
61
-
62
- constructor() {
63
- if (!this.#isBrowser) {
64
- return;
65
- }
66
- }
67
-
68
- ngOnDestroy(): void {
69
- currentActionNames.clear();
70
- }
71
-
72
- syncToDevTools(changedStatePerId: Record<string, object>) {
73
- const mappedChangedStatePerName = Object.entries(changedStatePerId).reduce(
74
- (acc, [id, store]) => {
75
- const { options, name } = this.#stores[id];
76
- acc[name] = options.map(store);
77
- return acc;
78
- },
79
- {} as Record<string, object>,
80
- );
81
- this.#currentState = {
82
- ...this.#currentState,
83
- ...mappedChangedStatePerName,
84
- };
85
-
86
- const names = Array.from(currentActionNames);
87
- const type = names.length ? names.join(', ') : 'Store Update';
88
- currentActionNames.clear();
89
-
90
- this.#connection.send({ type }, this.#currentState);
91
- }
92
-
93
- getNextId() {
94
- return String(this.#currentId++);
95
- }
96
-
97
- /**
98
- * Consumer provides the id. That is because we can only start
99
- * tracking the store in the init hook.
100
- * Unfortunately, methods for renaming having the final id
101
- * need to be defined already before.
102
- * That's why `withDevtools` requests first the id and
103
- * then registers itself later.
104
- */
105
- addStore(
106
- id: string,
107
- name: string,
108
- store: StateSource<object>,
109
- options: DevtoolsInnerOptions,
110
- ) {
111
- let storeName = name;
112
- const names = Object.values(this.#stores).map((store) => store.name);
113
-
114
- if (names.includes(storeName)) {
115
- // const { options } = throwIfNull(
116
- // Object.values(this.#stores).find((store) => store.name === storeName)
117
- // );
118
- if (!options.indexNames) {
119
- throw new Error(`An instance of the store ${storeName} already exists. \
120
- Enable automatic indexing via withDevTools('${storeName}', { indexNames: true }), or rename it upon instantiation.`);
121
- }
122
- }
123
-
124
- for (let i = 1; names.includes(storeName); i++) {
125
- storeName = `${name}-${i}`;
126
- }
127
- this.#stores[id] = { name: storeName, options };
128
-
129
- const tracker = options.tracker;
130
- if (!this.#trackers.includes(tracker)) {
131
- this.#trackers.push(tracker);
132
- }
133
-
134
- tracker.onChange((changedState) => this.syncToDevTools(changedState));
135
- tracker.track(id, store);
136
- }
137
-
138
- removeStore(id: string) {
139
- const name = this.#stores[id].name;
140
- this.#stores = Object.entries(this.#stores).reduce(
141
- (newStore, [storeId, value]) => {
142
- if (storeId !== id) {
143
- newStore[storeId] = value;
144
- }
145
- return newStore;
146
- },
147
- {} as StoreRegistry,
148
- );
149
-
150
- this.#currentState = Object.entries(this.#currentState).reduce(
151
- (newState, [storeName, state]) => {
152
- if (storeName !== name) {
153
- newState[name] = state;
154
- }
155
- return newState;
156
- },
157
- {} as Record<string, object>,
158
- );
159
-
160
- for (const tracker of this.#trackers) {
161
- tracker.removeStore(id);
162
- }
163
- }
164
-
165
- renameStore(oldName: string, newName: string) {
166
- const storeNames = Object.values(this.#stores).map((store) => store.name);
167
- const id = throwIfNull(
168
- Object.keys(this.#stores).find((id) => this.#stores[id].name === oldName),
169
- );
170
- if (storeNames.includes(newName)) {
171
- throw new Error(
172
- `NgRx Toolkit/DevTools: cannot rename from ${oldName} to ${newName}. ${newName} is already assigned to another SignalStore instance.`,
173
- );
174
- }
175
-
176
- this.#stores = Object.entries(this.#stores).reduce(
177
- (newStore, [id, value]) => {
178
- if (value.name === oldName) {
179
- newStore[id] = { ...value, name: newName };
180
- } else {
181
- newStore[id] = value;
182
- }
183
- return newStore;
184
- },
185
- {} as StoreRegistry,
186
- );
187
-
188
- // we don't rename in #currentState but wait for tracker to notify
189
- // us with a changed state that contains that name.
190
- this.#currentState = Object.entries(this.#currentState).reduce(
191
- (newState, [storeName, state]) => {
192
- if (storeName !== oldName) {
193
- newState[storeName] = state;
194
- }
195
- return newState;
196
- },
197
- {} as Record<string, object>,
198
- );
199
-
200
- this.#trackers.forEach((tracker) => tracker.notifyRenamedStore(id));
201
- }
202
- }
@@ -1,61 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
- import { getState, StateSource, watchState } from '@ngrx/signals';
3
- import { throwIfNull } from '../../shared/throw-if-null';
4
- import { Tracker, TrackerStores } from './models';
5
-
6
- type Stores = Record<
7
- string,
8
- { destroyWatcher: () => void; store: StateSource<object> }
9
- >;
10
-
11
- /**
12
- * Internal Service used by {@link withGlitchTracking}. It does not rely
13
- * on `effect` as {@link DefaultTracker} does but uses the NgRx function
14
- * `watchState` to track all state changes.
15
- */
16
- @Injectable({ providedIn: 'root' })
17
- export class GlitchTrackerService implements Tracker {
18
- #stores: Stores = {};
19
- #callback: ((changedState: Record<string, object>) => void) | undefined;
20
-
21
- get stores() {
22
- return Object.entries(this.#stores).reduce((acc, [id, { store }]) => {
23
- acc[id] = store;
24
- return acc;
25
- }, {} as TrackerStores);
26
- }
27
-
28
- onChange(callback: (changedState: Record<string, object>) => void): void {
29
- this.#callback = callback;
30
- }
31
-
32
- removeStore(id: string): void {
33
- this.#stores = Object.entries(this.#stores).reduce(
34
- (newStore, [storeId, value]) => {
35
- if (storeId !== id) {
36
- newStore[storeId] = value;
37
- } else {
38
- value.destroyWatcher();
39
- }
40
- return newStore;
41
- },
42
- {} as Stores,
43
- );
44
-
45
- throwIfNull(this.#callback)({});
46
- }
47
-
48
- track(id: string, store: StateSource<object>): void {
49
- const watcher = watchState(store, (state) => {
50
- throwIfNull(this.#callback)({ [id]: state });
51
- });
52
-
53
- this.#stores[id] = { destroyWatcher: watcher.destroy, store };
54
- }
55
-
56
- notifyRenamedStore(id: string): void {
57
- if (Object.keys(this.#stores).includes(id) && this.#callback) {
58
- this.#callback({ [id]: getState(this.#stores[id].store) });
59
- }
60
- }
61
- }
@@ -1,29 +0,0 @@
1
- import { StateSource } from '@ngrx/signals';
2
- import { ReduxDevtoolsConfig } from '../provide-devtools-config';
3
- import { DevtoolsInnerOptions } from './devtools-feature';
4
-
5
- export type Action = { type: string };
6
- export type Connection = {
7
- send: (action: Action, state: Record<string, unknown>) => void;
8
- };
9
- export type ReduxDevtoolsExtension = {
10
- connect: (options: ReduxDevtoolsConfig) => Connection;
11
- };
12
-
13
- export type StoreRegistry = Record<
14
- string,
15
- {
16
- options: DevtoolsInnerOptions;
17
- name: string;
18
- }
19
- >;
20
-
21
- export type Tracker = {
22
- track(id: string, store: StateSource<object>): void;
23
- onChange(callback: (changedState: Record<string, object>) => void): void;
24
- notifyRenamedStore(id: string): void;
25
- removeStore(id: string): void;
26
- get stores(): TrackerStores;
27
- };
28
-
29
- export type TrackerStores = Record<string, StateSource<object>>;
@@ -1,21 +0,0 @@
1
- import { StateSource } from '@ngrx/signals';
2
- import { renameDevtoolsMethodName } from './with-devtools';
3
-
4
- /**
5
- * Renames the name of a store how it appears in the Devtools.
6
- * @param store instance of the SignalStore
7
- * @param newName new name for the Devtools
8
- */
9
- export function renameDevtoolsName<State extends object>(
10
- store: StateSource<State>,
11
- newName: string,
12
- ): void {
13
- const renameMethod = (store as Record<string, (newName: string) => void>)[
14
- renameDevtoolsMethodName
15
- ];
16
- if (!renameMethod) {
17
- throw new Error("Devtools extensions haven't been added to this store.");
18
- }
19
-
20
- renameMethod(newName);
21
- }
@@ -1,48 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { signalStore, withMethods, withState } from '@ngrx/signals';
3
- import { updateState } from '../update-state';
4
- import { withDevtools } from '../with-devtools';
5
- import { setupExtensions } from './helpers.spec';
6
-
7
- describe('updateState', () => {
8
- it('should show the name of the action', () => {
9
- const { sendSpy } = setupExtensions();
10
- TestBed.inject(
11
- signalStore(
12
- { providedIn: 'root' },
13
- withDevtools('shop'),
14
- withState({ name: 'Car' }),
15
- ),
16
- );
17
- TestBed.flushEffects();
18
- expect(sendSpy).toHaveBeenCalledWith(
19
- { type: 'Store Update' },
20
- { shop: { name: 'Car' } },
21
- );
22
- });
23
-
24
- it('should set the action name', () => {
25
- const { sendSpy } = setupExtensions();
26
-
27
- const Store = signalStore(
28
- { providedIn: 'root' },
29
- withDevtools('shop'),
30
- withState({ name: 'Car' }),
31
- withMethods((store) => ({
32
- setName(name: string) {
33
- updateState(store, 'Set Name', { name });
34
- },
35
- })),
36
- );
37
- const store = TestBed.inject(Store);
38
- TestBed.flushEffects();
39
-
40
- store.setName('i4');
41
- TestBed.flushEffects();
42
-
43
- expect(sendSpy).lastCalledWith(
44
- { type: 'Set Name' },
45
- { shop: { name: 'i4' } },
46
- );
47
- });
48
- });
@@ -1,111 +0,0 @@
1
- import { createEnvironmentInjector, EnvironmentInjector } from '@angular/core';
2
- import { TestBed } from '@angular/core/testing';
3
- import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
4
- import { renameDevtoolsName } from '../rename-devtools-name';
5
- import { withDevtools } from '../with-devtools';
6
- import { setupExtensions } from './helpers.spec';
7
-
8
- describe('Devtools Basics', () => {
9
- it('should dispatch update', () => {
10
- const { sendSpy } = setupExtensions();
11
- TestBed.inject(
12
- signalStore(
13
- { providedIn: 'root' },
14
- withDevtools('shop'),
15
- withState({ name: 'Car' }),
16
- ),
17
- );
18
- TestBed.flushEffects();
19
- expect(sendSpy).toHaveBeenCalledWith(
20
- { type: 'Store Update' },
21
- { shop: { name: 'Car' } },
22
- );
23
- });
24
-
25
- it('should add multiple stores as feature stores', () => {
26
- const { sendSpy } = setupExtensions();
27
- for (const name of ['category', 'booking']) {
28
- TestBed.inject(signalStore({ providedIn: 'root' }, withDevtools(name)));
29
- }
30
- TestBed.flushEffects();
31
- expect(sendSpy).toHaveBeenLastCalledWith(
32
- { type: 'Store Update' },
33
- {
34
- category: {},
35
- booking: {},
36
- },
37
- );
38
- });
39
-
40
- it('should remove the state once destroyed', () => {
41
- const { sendSpy } = setupExtensions();
42
-
43
- const Store = signalStore(withDevtools('flight'));
44
- const childInjector = createEnvironmentInjector(
45
- [Store],
46
- TestBed.inject(EnvironmentInjector),
47
- );
48
-
49
- childInjector.get(Store);
50
- TestBed.flushEffects();
51
-
52
- expect(sendSpy).toHaveBeenCalledWith(
53
- { type: 'Store Update' },
54
- { flight: {} },
55
- );
56
-
57
- childInjector.destroy();
58
- TestBed.flushEffects();
59
- expect(sendSpy).toHaveBeenCalledWith({ type: 'Store Update' }, {});
60
- });
61
-
62
- it('should remove a renamed state once destroyed', () => {
63
- const { sendSpy } = setupExtensions();
64
-
65
- const Store = signalStore(withDevtools('flight'));
66
- const childInjector = createEnvironmentInjector(
67
- [Store],
68
- TestBed.inject(EnvironmentInjector),
69
- );
70
-
71
- const store = childInjector.get(Store);
72
- TestBed.flushEffects();
73
-
74
- expect(sendSpy).toHaveBeenCalledWith(
75
- { type: 'Store Update' },
76
- { flight: {} },
77
- );
78
-
79
- renameDevtoolsName(store, 'flights');
80
- childInjector.destroy();
81
- TestBed.flushEffects();
82
- expect(sendSpy).toHaveBeenCalledWith({ type: 'Store Update' }, {});
83
- });
84
-
85
- it('should group multiple patchState running before the synchronization', () => {
86
- const { sendSpy } = setupExtensions();
87
- const store = TestBed.inject(
88
- signalStore(
89
- { providedIn: 'root' },
90
- withDevtools('shop'),
91
- withState({ name: 'Car', amount: 0 }),
92
- withMethods((store) => ({
93
- increment() {
94
- patchState(store, (value) => ({
95
- ...value,
96
- amount: value.amount + 1,
97
- }));
98
- },
99
- })),
100
- ),
101
- );
102
-
103
- store.increment();
104
- store.increment();
105
- TestBed.flushEffects();
106
-
107
- expect(sendSpy.mock.calls).toEqual([
108
- [{ type: 'Store Update' }, { shop: { name: 'Car', amount: 2 } }],
109
- ]);
110
- });
111
- });
@@ -1,37 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { signalStore } from '@ngrx/signals';
3
- import { withDevtools } from '../with-devtools';
4
- import { setupExtensions } from './helpers.spec';
5
-
6
- describe('connect & send', () => {
7
- it('should connect', () => {
8
- const Store = signalStore({ providedIn: 'root' }, withDevtools('flight'));
9
- const { connectSpy } = setupExtensions();
10
- TestBed.inject(Store);
11
- expect(connectSpy).toHaveBeenCalledTimes(1);
12
- });
13
-
14
- it('should not connect if Redux Devtools are not available', () => {
15
- const { connectSpy } = setupExtensions(true, false);
16
- expect(connectSpy).toHaveBeenCalledTimes(0);
17
- });
18
-
19
- it('should not throw if it runs on the server', () => {
20
- setupExtensions(true, false);
21
- const Store = signalStore({ providedIn: 'root' }, withDevtools('flight'));
22
- expect(() => TestBed.inject(Store)).not.toThrowError();
23
- });
24
-
25
- it('should only send when store is initialized', () => {
26
- const { sendSpy } = setupExtensions();
27
- expect(sendSpy).toHaveBeenCalledTimes(0);
28
-
29
- const Store = signalStore({ providedIn: 'root' }, withDevtools('flight'));
30
- TestBed.flushEffects();
31
- expect(sendSpy).toHaveBeenCalledTimes(0);
32
-
33
- TestBed.inject(Store);
34
- TestBed.flushEffects();
35
- expect(sendSpy).toHaveBeenCalledTimes(1);
36
- });
37
- });
@@ -1,43 +0,0 @@
1
- import { PLATFORM_ID } from '@angular/core';
2
- import { TestBed } from '@angular/core/testing';
3
-
4
- export type Flight = {
5
- id: number;
6
- from: string;
7
- to: string;
8
- date: Date;
9
- delayed: boolean;
10
- };
11
-
12
- export function setupExtensions(
13
- isPlatformBrowser = true,
14
- isExtensionAvailable = true,
15
- ) {
16
- const sendSpy = jest.fn();
17
- const connection = {
18
- send: sendSpy,
19
- };
20
- const connectSpy = jest.fn(() => connection);
21
-
22
- if (isExtensionAvailable) {
23
- window.__REDUX_DEVTOOLS_EXTENSION__ = { connect: connectSpy };
24
- }
25
-
26
- if (isPlatformBrowser) {
27
- TestBed.configureTestingModule({
28
- providers: [
29
- {
30
- provide: PLATFORM_ID,
31
- useValue: isPlatformBrowser ? 'browser' : 'server',
32
- },
33
- ],
34
- });
35
- }
36
-
37
- return { sendSpy, connectSpy };
38
- }
39
-
40
- it('should initialize', () => {
41
- const { connectSpy } = setupExtensions();
42
- expect(connectSpy).not.toHaveBeenCalled();
43
- });