@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.
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +119 -0
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +1 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +2108 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
- package/{src/index.ts → index.d.ts} +5 -32
- package/lib/assertions/assertions.d.ts +2 -0
- package/{src/lib/devtools/features/with-disabled-name-indicies.ts → lib/devtools/features/with-disabled-name-indicies.d.ts} +1 -5
- package/{src/lib/devtools/features/with-glitch-tracking.ts → lib/devtools/features/with-glitch-tracking.d.ts} +1 -6
- package/{src/lib/devtools/features/with-mapper.ts → lib/devtools/features/with-mapper.d.ts} +1 -7
- package/lib/devtools/internal/current-action-names.d.ts +1 -0
- package/lib/devtools/internal/default-tracker.d.ts +13 -0
- package/lib/devtools/internal/devtools-feature.d.ts +24 -0
- package/lib/devtools/internal/devtools-syncer.service.d.ts +39 -0
- package/lib/devtools/internal/glitch-tracker.service.d.ts +18 -0
- package/lib/devtools/internal/models.d.ts +24 -0
- package/{src/lib/devtools/provide-devtools-config.ts → lib/devtools/provide-devtools-config.d.ts} +4 -16
- package/lib/devtools/rename-devtools-name.d.ts +7 -0
- package/lib/devtools/update-state.d.ts +15 -0
- package/{src/lib/devtools/with-dev-tools-stub.ts → lib/devtools/with-dev-tools-stub.d.ts} +1 -2
- package/lib/devtools/with-devtools.d.ts +24 -0
- package/lib/flattening-operator.d.ts +14 -0
- package/lib/immutable-state/deep-freeze.d.ts +11 -0
- package/lib/immutable-state/is-dev-mode.d.ts +1 -0
- package/lib/immutable-state/with-immutable-state.d.ts +60 -0
- package/lib/mutation/http-mutation.d.ts +86 -0
- package/lib/mutation/mutation.d.ts +20 -0
- package/lib/mutation/rx-mutation.d.ts +76 -0
- package/{src/lib/shared/signal-store-models.ts → lib/shared/signal-store-models.d.ts} +4 -8
- package/lib/shared/throw-if-null.d.ts +1 -0
- package/lib/storage-sync/features/with-indexed-db.d.ts +2 -0
- package/lib/storage-sync/features/with-local-storage.d.ts +3 -0
- package/lib/storage-sync/internal/indexeddb.service.d.ts +29 -0
- package/lib/storage-sync/internal/local-storage.service.d.ts +8 -0
- package/lib/storage-sync/internal/models.d.ts +45 -0
- package/lib/storage-sync/internal/session-storage.service.d.ts +8 -0
- package/lib/storage-sync/with-storage-sync.d.ts +45 -0
- package/lib/with-call-state.d.ts +58 -0
- package/{src/lib/with-conditional.ts → lib/with-conditional.d.ts} +7 -31
- package/lib/with-data-service.d.ts +109 -0
- package/{src/lib/with-feature-factory.ts → lib/with-feature-factory.d.ts} +4 -32
- package/lib/with-mutations.d.ts +51 -0
- package/lib/with-pagination.d.ts +98 -0
- package/lib/with-redux.d.ts +147 -0
- package/lib/with-reset.d.ts +29 -0
- package/lib/with-undo-redo.d.ts +31 -0
- package/package.json +21 -4
- package/redux-connector/index.d.ts +2 -0
- package/redux-connector/src/lib/create-redux.d.ts +13 -0
- package/redux-connector/src/lib/model.d.ts +40 -0
- package/redux-connector/src/lib/rxjs-interop/redux-method.d.ts +14 -0
- package/redux-connector/src/lib/signal-redux-store.d.ts +11 -0
- package/redux-connector/src/lib/util.d.ts +5 -0
- package/eslint.config.cjs +0 -43
- package/jest.config.ts +0 -22
- package/ng-package.json +0 -7
- package/project.json +0 -37
- package/redux-connector/docs/README.md +0 -131
- package/redux-connector/index.ts +0 -6
- package/redux-connector/ng-package.json +0 -5
- package/redux-connector/src/lib/create-redux.ts +0 -102
- package/redux-connector/src/lib/model.ts +0 -89
- package/redux-connector/src/lib/rxjs-interop/redux-method.ts +0 -66
- package/redux-connector/src/lib/signal-redux-store.ts +0 -59
- package/redux-connector/src/lib/util.ts +0 -22
- package/src/lib/assertions/assertions.ts +0 -9
- package/src/lib/devtools/internal/current-action-names.ts +0 -1
- package/src/lib/devtools/internal/default-tracker.ts +0 -60
- package/src/lib/devtools/internal/devtools-feature.ts +0 -37
- package/src/lib/devtools/internal/devtools-syncer.service.ts +0 -202
- package/src/lib/devtools/internal/glitch-tracker.service.ts +0 -61
- package/src/lib/devtools/internal/models.ts +0 -29
- package/src/lib/devtools/rename-devtools-name.ts +0 -21
- package/src/lib/devtools/tests/action-name.spec.ts +0 -48
- package/src/lib/devtools/tests/basic.spec.ts +0 -111
- package/src/lib/devtools/tests/connecting.spec.ts +0 -37
- package/src/lib/devtools/tests/helpers.spec.ts +0 -43
- package/src/lib/devtools/tests/naming.spec.ts +0 -216
- package/src/lib/devtools/tests/provide-devtools-config.spec.ts +0 -25
- package/src/lib/devtools/tests/types.spec.ts +0 -19
- package/src/lib/devtools/tests/update-state.spec.ts +0 -29
- package/src/lib/devtools/tests/with-devtools.spec.ts +0 -5
- package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +0 -272
- package/src/lib/devtools/tests/with-mapper.spec.ts +0 -69
- package/src/lib/devtools/update-state.ts +0 -38
- package/src/lib/devtools/with-devtools.ts +0 -81
- package/src/lib/flattening-operator.ts +0 -42
- package/src/lib/immutable-state/deep-freeze.ts +0 -43
- package/src/lib/immutable-state/is-dev-mode.ts +0 -6
- package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +0 -260
- package/src/lib/immutable-state/with-immutable-state.ts +0 -115
- package/src/lib/mutation/http-mutation.spec.ts +0 -473
- package/src/lib/mutation/http-mutation.ts +0 -172
- package/src/lib/mutation/mutation.ts +0 -26
- package/src/lib/mutation/rx-mutation.spec.ts +0 -594
- package/src/lib/mutation/rx-mutation.ts +0 -208
- package/src/lib/shared/prettify.ts +0 -3
- package/src/lib/shared/throw-if-null.ts +0 -7
- package/src/lib/storage-sync/features/with-indexed-db.ts +0 -81
- package/src/lib/storage-sync/features/with-local-storage.ts +0 -58
- package/src/lib/storage-sync/internal/indexeddb.service.ts +0 -124
- package/src/lib/storage-sync/internal/local-storage.service.ts +0 -19
- package/src/lib/storage-sync/internal/models.ts +0 -62
- package/src/lib/storage-sync/internal/session-storage.service.ts +0 -18
- package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +0 -99
- package/src/lib/storage-sync/tests/with-storage-async.spec.ts +0 -308
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -268
- package/src/lib/storage-sync/with-storage-sync.ts +0 -233
- package/src/lib/with-call-state.spec.ts +0 -42
- package/src/lib/with-call-state.ts +0 -195
- package/src/lib/with-conditional.spec.ts +0 -125
- package/src/lib/with-data-service.spec.ts +0 -564
- package/src/lib/with-data-service.ts +0 -433
- package/src/lib/with-feature-factory.spec.ts +0 -69
- package/src/lib/with-mutations.spec.ts +0 -537
- package/src/lib/with-mutations.ts +0 -146
- package/src/lib/with-pagination.spec.ts +0 -90
- package/src/lib/with-pagination.ts +0 -353
- package/src/lib/with-redux.spec.ts +0 -258
- package/src/lib/with-redux.ts +0 -387
- package/src/lib/with-reset.spec.ts +0 -112
- package/src/lib/with-reset.ts +0 -62
- package/src/lib/with-undo-redo.spec.ts +0 -287
- package/src/lib/with-undo-redo.ts +0 -199
- package/src/test-setup.ts +0 -8
- package/tsconfig.json +0 -29
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -17
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
|
2
|
-
import { patchState, signalStore, withState } from '@ngrx/signals';
|
|
3
|
-
import { delay, Observable, of, Subject, switchMap, throwError } from 'rxjs';
|
|
4
|
-
import { concatOp, exhaustOp, mergeOp, switchOp } from './flattening-operator';
|
|
5
|
-
import { rxMutation } from './mutation/rx-mutation';
|
|
6
|
-
import { withMutations } from './with-mutations';
|
|
7
|
-
|
|
8
|
-
type Param =
|
|
9
|
-
| number
|
|
10
|
-
| {
|
|
11
|
-
value: number | Observable<number>;
|
|
12
|
-
delay?: number;
|
|
13
|
-
fail?: boolean;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type NormalizedParam = {
|
|
17
|
-
value: number | Observable<number>;
|
|
18
|
-
delay: number;
|
|
19
|
-
fail: boolean;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
async function asyncTick(): Promise<void> {
|
|
23
|
-
return new Promise((resolve) => {
|
|
24
|
-
setTimeout(() => {
|
|
25
|
-
resolve();
|
|
26
|
-
}, 0);
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function calcDouble(value: number, delayInMsec = 1000): Observable<number> {
|
|
31
|
-
return of(value * 2).pipe(delay(delayInMsec));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function fail(_value: number, delayInMsec = 1000): Observable<number> {
|
|
35
|
-
return of(null).pipe(
|
|
36
|
-
delay(delayInMsec),
|
|
37
|
-
switchMap(() => throwError(() => ({ error: 'Test-Error' }))),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function createTestSetup(flatteningOperator = concatOp) {
|
|
42
|
-
function normalizeParam(param: Param): NormalizedParam {
|
|
43
|
-
if (typeof param === 'number') {
|
|
44
|
-
return {
|
|
45
|
-
value: param,
|
|
46
|
-
delay: 1000,
|
|
47
|
-
fail: false,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
value: param.value,
|
|
53
|
-
delay: param.delay ?? 1000,
|
|
54
|
-
fail: param.fail ?? false,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type SuccessParams = { result: number; params: Param };
|
|
59
|
-
type ErrorParams = { error: unknown; params: Param };
|
|
60
|
-
|
|
61
|
-
let onSuccessCalls = 0;
|
|
62
|
-
let onErrorCalls = 0;
|
|
63
|
-
|
|
64
|
-
let lastOnSuccessParams: SuccessParams | undefined = undefined;
|
|
65
|
-
let lastOnErrorParams: ErrorParams | undefined = undefined;
|
|
66
|
-
|
|
67
|
-
return TestBed.runInInjectionContext(() => {
|
|
68
|
-
const Store = signalStore(
|
|
69
|
-
withState({ counter: 3 }),
|
|
70
|
-
withMutations((store) => ({
|
|
71
|
-
increment: rxMutation({
|
|
72
|
-
operation: (param: Param) => {
|
|
73
|
-
const normalized = normalizeParam(param);
|
|
74
|
-
|
|
75
|
-
if (normalized.value instanceof Observable) {
|
|
76
|
-
return normalized.value.pipe(
|
|
77
|
-
switchMap((value) => {
|
|
78
|
-
if (normalized.fail) {
|
|
79
|
-
return fail(value, normalized.delay);
|
|
80
|
-
}
|
|
81
|
-
return calcDouble(value, normalized.delay);
|
|
82
|
-
}),
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (normalized.fail) {
|
|
87
|
-
return fail(normalized.value, normalized.delay);
|
|
88
|
-
}
|
|
89
|
-
return calcDouble(normalized.value, normalized.delay);
|
|
90
|
-
},
|
|
91
|
-
operator: flatteningOperator,
|
|
92
|
-
onSuccess: (result, params) => {
|
|
93
|
-
lastOnSuccessParams = { result, params };
|
|
94
|
-
onSuccessCalls++;
|
|
95
|
-
patchState(store, (state) => ({
|
|
96
|
-
counter: state.counter + result,
|
|
97
|
-
}));
|
|
98
|
-
},
|
|
99
|
-
onError: (error, params) => {
|
|
100
|
-
lastOnErrorParams = { error, params };
|
|
101
|
-
onErrorCalls++;
|
|
102
|
-
},
|
|
103
|
-
}),
|
|
104
|
-
})),
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const store = new Store();
|
|
108
|
-
return {
|
|
109
|
-
store,
|
|
110
|
-
onSuccessCalls: () => onSuccessCalls,
|
|
111
|
-
onErrorCalls: () => onErrorCalls,
|
|
112
|
-
lastOnSuccessParams: () => lastOnSuccessParams,
|
|
113
|
-
lastOnErrorParams: () => lastOnErrorParams,
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
describe('withMutations with rxMutation', () => {
|
|
119
|
-
it('should update the state', fakeAsync(() => {
|
|
120
|
-
const testSetup = createTestSetup();
|
|
121
|
-
const store = testSetup.store;
|
|
122
|
-
|
|
123
|
-
expect(store.incrementStatus()).toEqual('idle');
|
|
124
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
125
|
-
|
|
126
|
-
store.increment(2);
|
|
127
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
128
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
129
|
-
|
|
130
|
-
tick(2000);
|
|
131
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
132
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
133
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
134
|
-
|
|
135
|
-
expect(store.counter()).toEqual(7);
|
|
136
|
-
}));
|
|
137
|
-
|
|
138
|
-
it('sets error', fakeAsync(() => {
|
|
139
|
-
const testSetup = createTestSetup();
|
|
140
|
-
const store = testSetup.store;
|
|
141
|
-
|
|
142
|
-
store.increment({ value: 2, fail: true });
|
|
143
|
-
|
|
144
|
-
tick(2000);
|
|
145
|
-
expect(store.incrementStatus()).toEqual('error');
|
|
146
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
147
|
-
expect(store.incrementError()).toEqual({
|
|
148
|
-
error: 'Test-Error',
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
expect(store.counter()).toEqual(3);
|
|
152
|
-
}));
|
|
153
|
-
|
|
154
|
-
it('starts two concurrent operations using concatMap: the first one fails and the second one succeeds', fakeAsync(() => {
|
|
155
|
-
const testSetup = createTestSetup(concatOp);
|
|
156
|
-
const store = testSetup.store;
|
|
157
|
-
|
|
158
|
-
store.increment({ value: 1, delay: 100, fail: true });
|
|
159
|
-
store.increment({ value: 2, delay: 200, fail: false });
|
|
160
|
-
|
|
161
|
-
tick(100);
|
|
162
|
-
|
|
163
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
164
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
165
|
-
expect(store.incrementError()).toEqual({
|
|
166
|
-
error: 'Test-Error',
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
tick(200);
|
|
170
|
-
|
|
171
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
172
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
173
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
174
|
-
|
|
175
|
-
expect(store.counter()).toEqual(7);
|
|
176
|
-
}));
|
|
177
|
-
|
|
178
|
-
it('starts two concurrent operations using mergeMap: the first one fails and the second one succeeds', fakeAsync(() => {
|
|
179
|
-
const testSetup = createTestSetup(mergeOp);
|
|
180
|
-
const store = testSetup.store;
|
|
181
|
-
|
|
182
|
-
store.increment({ value: 1, delay: 100, fail: true });
|
|
183
|
-
store.increment({ value: 2, delay: 200, fail: false });
|
|
184
|
-
|
|
185
|
-
tick(100);
|
|
186
|
-
|
|
187
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
188
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
189
|
-
expect(store.incrementError()).toEqual({
|
|
190
|
-
error: 'Test-Error',
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
tick(100);
|
|
194
|
-
|
|
195
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
196
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
197
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
198
|
-
|
|
199
|
-
expect(store.counter()).toEqual(7);
|
|
200
|
-
}));
|
|
201
|
-
|
|
202
|
-
it('deals with race conditions using switchMap', fakeAsync(() => {
|
|
203
|
-
const testSetup = createTestSetup(switchOp);
|
|
204
|
-
const store = testSetup.store;
|
|
205
|
-
|
|
206
|
-
store.increment(1);
|
|
207
|
-
|
|
208
|
-
tick(500);
|
|
209
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
210
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
211
|
-
|
|
212
|
-
store.increment(2);
|
|
213
|
-
tick(1000);
|
|
214
|
-
|
|
215
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
216
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
217
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
218
|
-
|
|
219
|
-
expect(store.counter()).toEqual(7);
|
|
220
|
-
expect(testSetup.onSuccessCalls()).toEqual(1);
|
|
221
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
222
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
223
|
-
params: 2,
|
|
224
|
-
result: 4,
|
|
225
|
-
});
|
|
226
|
-
}));
|
|
227
|
-
|
|
228
|
-
it('deals with race conditions using mergeMap', fakeAsync(() => {
|
|
229
|
-
const testSetup = createTestSetup(mergeOp);
|
|
230
|
-
const store = testSetup.store;
|
|
231
|
-
|
|
232
|
-
store.increment(1);
|
|
233
|
-
tick(500);
|
|
234
|
-
store.increment(2);
|
|
235
|
-
tick(500);
|
|
236
|
-
|
|
237
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
238
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
239
|
-
|
|
240
|
-
// expect(store.counter()).toEqual(7);
|
|
241
|
-
expect(testSetup.onSuccessCalls()).toEqual(1);
|
|
242
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
243
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
244
|
-
params: 1,
|
|
245
|
-
result: 2,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
tick(500);
|
|
249
|
-
|
|
250
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
251
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
252
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
253
|
-
|
|
254
|
-
expect(store.counter()).toEqual(9);
|
|
255
|
-
expect(testSetup.onSuccessCalls()).toEqual(2);
|
|
256
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
257
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
258
|
-
params: 2,
|
|
259
|
-
result: 4,
|
|
260
|
-
});
|
|
261
|
-
}));
|
|
262
|
-
|
|
263
|
-
it('deals with race conditions using mergeMap where the 2nd task starts after and finishes before the 1st one', fakeAsync(() => {
|
|
264
|
-
const testSetup = createTestSetup(mergeOp);
|
|
265
|
-
const store = testSetup.store;
|
|
266
|
-
|
|
267
|
-
store.increment({ value: 1, delay: 1000 });
|
|
268
|
-
tick(500);
|
|
269
|
-
|
|
270
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
271
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
272
|
-
|
|
273
|
-
store.increment({ value: 2, delay: 100 });
|
|
274
|
-
tick(500);
|
|
275
|
-
|
|
276
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
277
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
278
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
279
|
-
|
|
280
|
-
expect(store.counter()).toEqual(9);
|
|
281
|
-
expect(testSetup.onSuccessCalls()).toEqual(2);
|
|
282
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
283
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
284
|
-
params: { value: 1, delay: 1000 },
|
|
285
|
-
result: 2,
|
|
286
|
-
});
|
|
287
|
-
}));
|
|
288
|
-
|
|
289
|
-
it('deals with race conditions using concatMap', fakeAsync(() => {
|
|
290
|
-
const testSetup = createTestSetup(concatOp);
|
|
291
|
-
const store = testSetup.store;
|
|
292
|
-
|
|
293
|
-
store.increment({ value: 1, delay: 1000 });
|
|
294
|
-
tick(500);
|
|
295
|
-
store.increment({ value: 2, delay: 100 });
|
|
296
|
-
tick(500);
|
|
297
|
-
|
|
298
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
299
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
300
|
-
|
|
301
|
-
expect(store.counter()).toEqual(5);
|
|
302
|
-
expect(testSetup.onSuccessCalls()).toEqual(1);
|
|
303
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
304
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
305
|
-
params: { value: 1, delay: 1000 },
|
|
306
|
-
result: 2,
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
tick(500);
|
|
310
|
-
|
|
311
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
312
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
313
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
314
|
-
|
|
315
|
-
expect(store.counter()).toEqual(9);
|
|
316
|
-
expect(testSetup.onSuccessCalls()).toEqual(2);
|
|
317
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
318
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
319
|
-
params: { value: 2, delay: 100 },
|
|
320
|
-
result: 4,
|
|
321
|
-
});
|
|
322
|
-
}));
|
|
323
|
-
|
|
324
|
-
it('deals with race conditions using exhaustMap', fakeAsync(() => {
|
|
325
|
-
const testSetup = createTestSetup(exhaustOp);
|
|
326
|
-
const store = testSetup.store;
|
|
327
|
-
|
|
328
|
-
store.increment({ value: 1, delay: 1000 });
|
|
329
|
-
tick(500);
|
|
330
|
-
|
|
331
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
332
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
333
|
-
|
|
334
|
-
store.increment({ value: 2, delay: 100 });
|
|
335
|
-
tick(500);
|
|
336
|
-
|
|
337
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
338
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
339
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
340
|
-
|
|
341
|
-
expect(store.counter()).toEqual(5);
|
|
342
|
-
expect(testSetup.onSuccessCalls()).toEqual(1);
|
|
343
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
344
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
345
|
-
params: { value: 1, delay: 1000 },
|
|
346
|
-
result: 2,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
tick(500);
|
|
350
|
-
|
|
351
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
352
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
353
|
-
expect(store.incrementError()).toEqual(undefined);
|
|
354
|
-
|
|
355
|
-
expect(store.counter()).toEqual(5);
|
|
356
|
-
expect(testSetup.onSuccessCalls()).toEqual(1);
|
|
357
|
-
expect(testSetup.onErrorCalls()).toEqual(0);
|
|
358
|
-
expect(testSetup.lastOnSuccessParams()).toEqual({
|
|
359
|
-
params: { value: 1, delay: 1000 },
|
|
360
|
-
result: 2,
|
|
361
|
-
});
|
|
362
|
-
}));
|
|
363
|
-
|
|
364
|
-
it('informs about failed operation via the returned promise', async () => {
|
|
365
|
-
const testSetup = createTestSetup(switchOp);
|
|
366
|
-
const store = testSetup.store;
|
|
367
|
-
|
|
368
|
-
const p1 = store.increment({ value: 1, delay: 1, fail: false });
|
|
369
|
-
const p2 = store.increment({ value: 2, delay: 2, fail: true });
|
|
370
|
-
|
|
371
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
372
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
373
|
-
|
|
374
|
-
await asyncTick();
|
|
375
|
-
|
|
376
|
-
const result1 = await p1;
|
|
377
|
-
const result2 = await p2;
|
|
378
|
-
|
|
379
|
-
expect(result1.status).toEqual('aborted');
|
|
380
|
-
expect(result2).toEqual({
|
|
381
|
-
status: 'error',
|
|
382
|
-
error: {
|
|
383
|
-
error: 'Test-Error',
|
|
384
|
-
},
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
388
|
-
expect(store.incrementStatus()).toEqual('error');
|
|
389
|
-
expect(store.incrementError()).toEqual({
|
|
390
|
-
error: 'Test-Error',
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it('informs about successful operation via the returned promise', async () => {
|
|
395
|
-
const testSetup = createTestSetup();
|
|
396
|
-
const store = testSetup.store;
|
|
397
|
-
|
|
398
|
-
const resultPromise = store.increment({ value: 2, delay: 2, fail: false });
|
|
399
|
-
|
|
400
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
401
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
402
|
-
|
|
403
|
-
await asyncTick();
|
|
404
|
-
|
|
405
|
-
const result = await resultPromise;
|
|
406
|
-
|
|
407
|
-
expect(result).toEqual({
|
|
408
|
-
status: 'success',
|
|
409
|
-
value: 4,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
413
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
414
|
-
expect(store.incrementError()).toBeUndefined();
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('informs about aborted operation when using switchMap', async () => {
|
|
418
|
-
const testSetup = createTestSetup(switchOp);
|
|
419
|
-
const store = testSetup.store;
|
|
420
|
-
|
|
421
|
-
const p1 = store.increment({ value: 1, delay: 1, fail: false });
|
|
422
|
-
const p2 = store.increment({ value: 2, delay: 2, fail: false });
|
|
423
|
-
|
|
424
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
425
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
426
|
-
|
|
427
|
-
await asyncTick();
|
|
428
|
-
|
|
429
|
-
const result1 = await p1;
|
|
430
|
-
const result2 = await p2;
|
|
431
|
-
|
|
432
|
-
expect(result1.status).toEqual('aborted');
|
|
433
|
-
expect(result2).toEqual({
|
|
434
|
-
status: 'success',
|
|
435
|
-
value: 4,
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
439
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
440
|
-
expect(store.incrementError()).toBeUndefined();
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('informs about aborted operation when using exhaustMap', async () => {
|
|
444
|
-
const testSetup = createTestSetup(exhaustOp);
|
|
445
|
-
const store = testSetup.store;
|
|
446
|
-
|
|
447
|
-
const p1 = store.increment({ value: 1, delay: 1, fail: false });
|
|
448
|
-
const p2 = store.increment({ value: 2, delay: 1, fail: false });
|
|
449
|
-
|
|
450
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
451
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
452
|
-
|
|
453
|
-
await asyncTick();
|
|
454
|
-
|
|
455
|
-
const result1 = await p1;
|
|
456
|
-
const result2 = await p2;
|
|
457
|
-
|
|
458
|
-
expect(result1).toEqual({
|
|
459
|
-
status: 'success',
|
|
460
|
-
value: 2,
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
expect(result2.status).toEqual('aborted');
|
|
464
|
-
|
|
465
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
466
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
467
|
-
expect(store.incrementError()).toBeUndefined();
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('calls success handler per value in the stream and returns the final value via the promise', async () => {
|
|
471
|
-
const testSetup = createTestSetup(switchOp);
|
|
472
|
-
const store = testSetup.store;
|
|
473
|
-
|
|
474
|
-
const input$ = new Subject<number>();
|
|
475
|
-
const resultPromise = store.increment({
|
|
476
|
-
value: input$,
|
|
477
|
-
delay: 1,
|
|
478
|
-
fail: false,
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
482
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
483
|
-
|
|
484
|
-
input$.next(1);
|
|
485
|
-
input$.next(2);
|
|
486
|
-
input$.next(3);
|
|
487
|
-
input$.complete();
|
|
488
|
-
|
|
489
|
-
await asyncTick();
|
|
490
|
-
|
|
491
|
-
const result = await resultPromise;
|
|
492
|
-
|
|
493
|
-
expect(result).toEqual({
|
|
494
|
-
status: 'success',
|
|
495
|
-
value: 6,
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
expect(store.counter()).toEqual(9);
|
|
499
|
-
expect(testSetup.lastOnSuccessParams()).toMatchObject({
|
|
500
|
-
result: 6,
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
504
|
-
expect(store.incrementStatus()).toEqual('success');
|
|
505
|
-
expect(store.incrementError()).toBeUndefined();
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
it('informs about failed operation via the returned promise', async () => {
|
|
509
|
-
const testSetup = createTestSetup(switchOp);
|
|
510
|
-
const store = testSetup.store;
|
|
511
|
-
|
|
512
|
-
const p1 = store.increment({ value: 1, delay: 1, fail: false });
|
|
513
|
-
const p2 = store.increment({ value: 2, delay: 2, fail: true });
|
|
514
|
-
|
|
515
|
-
expect(store.incrementStatus()).toEqual('pending');
|
|
516
|
-
expect(store.incrementIsPending()).toEqual(true);
|
|
517
|
-
|
|
518
|
-
await asyncTick();
|
|
519
|
-
|
|
520
|
-
const result1 = await p1;
|
|
521
|
-
const result2 = await p2;
|
|
522
|
-
|
|
523
|
-
expect(result1.status).toEqual('aborted');
|
|
524
|
-
expect(result2).toEqual({
|
|
525
|
-
status: 'error',
|
|
526
|
-
error: {
|
|
527
|
-
error: 'Test-Error',
|
|
528
|
-
},
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
expect(store.incrementIsPending()).toEqual(false);
|
|
532
|
-
expect(store.incrementStatus()).toEqual('error');
|
|
533
|
-
expect(store.incrementError()).toEqual({
|
|
534
|
-
error: 'Test-Error',
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
});
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { Signal } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
EmptyFeatureResult,
|
|
4
|
-
signalStoreFeature,
|
|
5
|
-
SignalStoreFeature,
|
|
6
|
-
SignalStoreFeatureResult,
|
|
7
|
-
StateSignals,
|
|
8
|
-
StateSource,
|
|
9
|
-
withComputed,
|
|
10
|
-
withMethods,
|
|
11
|
-
WritableStateSource,
|
|
12
|
-
} from '@ngrx/signals';
|
|
13
|
-
import { Mutation, MutationStatus } from './mutation/mutation';
|
|
14
|
-
|
|
15
|
-
// NamedMutationMethods below will infer the actual parameter and return types
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
-
type MutationsDictionary = Record<string, Mutation<any, any>>;
|
|
18
|
-
|
|
19
|
-
// withMethods uses Record<string, Function> internally
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
21
|
-
export type MethodsDictionary = Record<string, Function>;
|
|
22
|
-
|
|
23
|
-
type NamedMutationProps<T extends MutationsDictionary> = {
|
|
24
|
-
[Prop in keyof T as `${Prop & string}IsPending`]: Signal<boolean>;
|
|
25
|
-
} & {
|
|
26
|
-
[Prop in keyof T as `${Prop & string}Status`]: Signal<MutationStatus>;
|
|
27
|
-
} & {
|
|
28
|
-
[Prop in keyof T as `${Prop & string}Error`]: Signal<Error | undefined>;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type NamedMutationMethods<T extends MutationsDictionary> = {
|
|
32
|
-
[Prop in keyof T as `${Prop & string}`]: T[Prop] extends Mutation<
|
|
33
|
-
infer P,
|
|
34
|
-
infer R
|
|
35
|
-
>
|
|
36
|
-
? Mutation<P, R>
|
|
37
|
-
: never;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type NamedMutationResult<T extends MutationsDictionary> =
|
|
41
|
-
EmptyFeatureResult & {
|
|
42
|
-
props: NamedMutationProps<T>;
|
|
43
|
-
methods: NamedMutationMethods<T>;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Adds mutation methods to the store. Also, for each mutation method, several
|
|
48
|
-
* Signals are added informing about the mutation's status and errors.
|
|
49
|
-
*
|
|
50
|
-
* ```typescript
|
|
51
|
-
* export type Params = {
|
|
52
|
-
* value: number;
|
|
53
|
-
* };
|
|
54
|
-
*
|
|
55
|
-
* export const CounterStore = signalStore(
|
|
56
|
-
* { providedIn: 'root' },
|
|
57
|
-
* withState({ counter: 0 }),
|
|
58
|
-
* withMutations((store) => ({
|
|
59
|
-
* increment: rxMutation({ ... }),
|
|
60
|
-
* })),
|
|
61
|
-
* );
|
|
62
|
-
* ```
|
|
63
|
-
*
|
|
64
|
-
* There are several types of mutations. In the example shown, an {@link module:rx-mutation.rxMutation | rxMutation}
|
|
65
|
-
* leveraging RxJS is used
|
|
66
|
-
*
|
|
67
|
-
* For the defined `increment` mutation, several the following properties and
|
|
68
|
-
* methods are added to the store:
|
|
69
|
-
* - `increment(params: Params): Promise<MutationResult<number>>`: The mutation method.
|
|
70
|
-
* - `incrementIsPending`: A signal indicating if the mutation is in progress.
|
|
71
|
-
* - `incrementStatus`: A signal representing the current status of the mutation.
|
|
72
|
-
* - `incrementError`: A signal containing any error that occurred during the mutation.
|
|
73
|
-
*
|
|
74
|
-
* @param mutationsFactory
|
|
75
|
-
*/
|
|
76
|
-
export function withMutations<
|
|
77
|
-
Input extends SignalStoreFeatureResult,
|
|
78
|
-
Result extends MutationsDictionary,
|
|
79
|
-
>(
|
|
80
|
-
mutationsFactory: (
|
|
81
|
-
store: Input['props'] &
|
|
82
|
-
Input['methods'] &
|
|
83
|
-
WritableStateSource<Input['state']> &
|
|
84
|
-
StateSignals<Input['state']>,
|
|
85
|
-
) => Result,
|
|
86
|
-
): SignalStoreFeature<Input, NamedMutationResult<Result>>;
|
|
87
|
-
|
|
88
|
-
export function withMutations<
|
|
89
|
-
Input extends SignalStoreFeatureResult,
|
|
90
|
-
Result extends MutationsDictionary,
|
|
91
|
-
>(
|
|
92
|
-
mutationsFactory: (
|
|
93
|
-
store: Input['props'] & Input['methods'] & StateSource<Input['state']>,
|
|
94
|
-
) => Result,
|
|
95
|
-
): SignalStoreFeature<Input> {
|
|
96
|
-
return (store) => {
|
|
97
|
-
// TODO: Is this the correct usage?
|
|
98
|
-
const source = store as StateSource<typeof store.stateSignals>;
|
|
99
|
-
const mutations = mutationsFactory({
|
|
100
|
-
...source,
|
|
101
|
-
...store.props,
|
|
102
|
-
...store.methods,
|
|
103
|
-
...store.stateSignals,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const feature = createMutationsFeature(mutations);
|
|
107
|
-
return feature(store);
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function createMutationsFeature<Result extends MutationsDictionary>(
|
|
112
|
-
mutations: Result,
|
|
113
|
-
) {
|
|
114
|
-
const keys = Object.keys(mutations);
|
|
115
|
-
|
|
116
|
-
const feature = signalStoreFeature(
|
|
117
|
-
withMethods(() =>
|
|
118
|
-
keys.reduce(
|
|
119
|
-
(acc, key) => ({
|
|
120
|
-
...acc,
|
|
121
|
-
[key]: async (params: never) => {
|
|
122
|
-
const mutation = mutations[key];
|
|
123
|
-
if (!mutation) {
|
|
124
|
-
throw new Error(`Mutation ${key} not found`);
|
|
125
|
-
}
|
|
126
|
-
const result = await mutation(params);
|
|
127
|
-
return result;
|
|
128
|
-
},
|
|
129
|
-
}),
|
|
130
|
-
{} as MethodsDictionary,
|
|
131
|
-
),
|
|
132
|
-
),
|
|
133
|
-
withComputed(() =>
|
|
134
|
-
keys.reduce(
|
|
135
|
-
(acc, key) => ({
|
|
136
|
-
...acc,
|
|
137
|
-
[`${key}IsPending`]: mutations[key].isPending,
|
|
138
|
-
[`${key}Status`]: mutations[key].status,
|
|
139
|
-
[`${key}Error`]: mutations[key].error,
|
|
140
|
-
}),
|
|
141
|
-
{} as NamedMutationProps<Result>,
|
|
142
|
-
),
|
|
143
|
-
),
|
|
144
|
-
);
|
|
145
|
-
return feature;
|
|
146
|
-
}
|