@angular-architects/ngrx-toolkit 20.0.0 → 20.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.cjs +43 -0
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +4 -21
- package/project.json +37 -0
- package/redux-connector/docs/README.md +131 -0
- package/redux-connector/index.ts +6 -0
- package/redux-connector/ng-package.json +5 -0
- package/redux-connector/src/lib/create-redux.ts +102 -0
- package/redux-connector/src/lib/model.ts +89 -0
- package/redux-connector/src/lib/rxjs-interop/redux-method.ts +66 -0
- package/redux-connector/src/lib/signal-redux-store.ts +59 -0
- package/redux-connector/src/lib/util.ts +22 -0
- package/src/index.ts +43 -0
- package/src/lib/assertions/assertions.ts +9 -0
- package/src/lib/devtools/features/with-disabled-name-indicies.ts +31 -0
- package/src/lib/devtools/features/with-glitch-tracking.ts +35 -0
- package/src/lib/devtools/features/with-mapper.ts +34 -0
- package/src/lib/devtools/internal/current-action-names.ts +1 -0
- package/src/lib/devtools/internal/default-tracker.ts +60 -0
- package/src/lib/devtools/internal/devtools-feature.ts +37 -0
- package/src/lib/devtools/internal/devtools-syncer.service.ts +202 -0
- package/src/lib/devtools/internal/glitch-tracker.service.ts +61 -0
- package/src/lib/devtools/internal/models.ts +29 -0
- package/src/lib/devtools/provide-devtools-config.ts +32 -0
- package/src/lib/devtools/rename-devtools-name.ts +21 -0
- package/src/lib/devtools/tests/action-name.spec.ts +48 -0
- package/src/lib/devtools/tests/basic.spec.ts +111 -0
- package/src/lib/devtools/tests/connecting.spec.ts +37 -0
- package/src/lib/devtools/tests/helpers.spec.ts +43 -0
- package/src/lib/devtools/tests/naming.spec.ts +216 -0
- package/src/lib/devtools/tests/provide-devtools-config.spec.ts +25 -0
- package/src/lib/devtools/tests/types.spec.ts +19 -0
- package/src/lib/devtools/tests/update-state.spec.ts +29 -0
- package/src/lib/devtools/tests/with-devtools.spec.ts +5 -0
- package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +272 -0
- package/src/lib/devtools/tests/with-mapper.spec.ts +69 -0
- package/src/lib/devtools/update-state.ts +38 -0
- package/src/lib/devtools/with-dev-tools-stub.ts +6 -0
- package/src/lib/devtools/with-devtools.ts +81 -0
- package/src/lib/immutable-state/deep-freeze.ts +43 -0
- package/src/lib/immutable-state/is-dev-mode.ts +6 -0
- package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +278 -0
- package/src/lib/immutable-state/with-immutable-state.ts +150 -0
- package/src/lib/shared/prettify.ts +3 -0
- package/src/lib/shared/signal-store-models.ts +30 -0
- package/src/lib/shared/throw-if-null.ts +7 -0
- package/src/lib/storage-sync/features/with-indexed-db.ts +81 -0
- package/src/lib/storage-sync/features/with-local-storage.ts +58 -0
- package/src/lib/storage-sync/internal/indexeddb.service.ts +124 -0
- package/src/lib/storage-sync/internal/local-storage.service.ts +19 -0
- package/src/lib/storage-sync/internal/models.ts +62 -0
- package/src/lib/storage-sync/internal/session-storage.service.ts +18 -0
- package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +99 -0
- package/src/lib/storage-sync/tests/with-storage-async.spec.ts +305 -0
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +273 -0
- package/src/lib/storage-sync/with-storage-sync.ts +236 -0
- package/src/lib/with-call-state.spec.ts +42 -0
- package/src/lib/with-call-state.ts +195 -0
- package/src/lib/with-conditional.spec.ts +125 -0
- package/src/lib/with-conditional.ts +74 -0
- package/src/lib/with-data-service.spec.ts +564 -0
- package/src/lib/with-data-service.ts +433 -0
- package/src/lib/with-feature-factory.spec.ts +69 -0
- package/src/lib/with-feature-factory.ts +56 -0
- package/src/lib/with-pagination.spec.ts +135 -0
- package/src/lib/with-pagination.ts +373 -0
- package/src/lib/with-redux.spec.ts +258 -0
- package/src/lib/with-redux.ts +387 -0
- package/src/lib/with-reset.spec.ts +112 -0
- package/src/lib/with-reset.ts +62 -0
- package/src/lib/with-undo-redo.spec.ts +274 -0
- package/src/lib/with-undo-redo.ts +200 -0
- package/src/test-setup.ts +6 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +17 -0
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +0 -119
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +0 -1
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +0 -1780
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +0 -1
- package/index.d.ts +0 -938
- package/redux-connector/index.d.ts +0 -59
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
|
3
|
+
import { signalStore, type } from '@ngrx/signals';
|
|
4
|
+
import { EntityId, withEntities } from '@ngrx/signals/entities';
|
|
5
|
+
import { delay, firstValueFrom, Observable, of } from 'rxjs';
|
|
6
|
+
import { withCallState } from './with-call-state';
|
|
7
|
+
import { DataService, withDataService } from './with-data-service';
|
|
8
|
+
|
|
9
|
+
// Since the resulting shape of entities in the store is a matter of the implementing services of `dataServiceType`,
|
|
10
|
+
// these tests are more so about verifying that each resulting method exists, with or without named collections.
|
|
11
|
+
// The expectations on the resulting shape of the data in the store following these tests merely asserts
|
|
12
|
+
// that the store was patched in the right generic shape and with respective call states.
|
|
13
|
+
|
|
14
|
+
describe('withDataService', () => {
|
|
15
|
+
it('should load from a service and set entities in the store', fakeAsync(() => {
|
|
16
|
+
TestBed.runInInjectionContext(() => {
|
|
17
|
+
const store = new Store();
|
|
18
|
+
|
|
19
|
+
tick();
|
|
20
|
+
expect(store.entities().length).toBe(0);
|
|
21
|
+
|
|
22
|
+
store.load();
|
|
23
|
+
tick();
|
|
24
|
+
|
|
25
|
+
expect(store.entities().length).toBe(1);
|
|
26
|
+
});
|
|
27
|
+
}));
|
|
28
|
+
it('should load from a service and set entities in the store (with named collection)', fakeAsync(() => {
|
|
29
|
+
TestBed.runInInjectionContext(() => {
|
|
30
|
+
const store = new StoreWithNamedCollection();
|
|
31
|
+
|
|
32
|
+
tick();
|
|
33
|
+
expect(store.flightEntities().length).toBe(0);
|
|
34
|
+
|
|
35
|
+
store.loadFlightEntities();
|
|
36
|
+
tick();
|
|
37
|
+
|
|
38
|
+
expect(store.flightEntities().length).toBe(1);
|
|
39
|
+
});
|
|
40
|
+
}));
|
|
41
|
+
it('should load by ID from a service and set entities in the store', fakeAsync(() => {
|
|
42
|
+
TestBed.runInInjectionContext(() => {
|
|
43
|
+
const store = new Store();
|
|
44
|
+
|
|
45
|
+
tick();
|
|
46
|
+
|
|
47
|
+
store.loadById(2);
|
|
48
|
+
|
|
49
|
+
tick();
|
|
50
|
+
|
|
51
|
+
expect(store.current()).toEqual(createFlight({ id: 2 }));
|
|
52
|
+
});
|
|
53
|
+
}));
|
|
54
|
+
it('should load by ID from a service and set entities in the store (with named collection)', fakeAsync(() => {
|
|
55
|
+
TestBed.runInInjectionContext(() => {
|
|
56
|
+
const store = new StoreWithNamedCollection();
|
|
57
|
+
|
|
58
|
+
tick();
|
|
59
|
+
|
|
60
|
+
store.loadFlightById(2);
|
|
61
|
+
|
|
62
|
+
tick();
|
|
63
|
+
|
|
64
|
+
expect(store.currentFlight()).toEqual(createFlight({ id: 2 }));
|
|
65
|
+
});
|
|
66
|
+
}));
|
|
67
|
+
it('should create from a service and set an entity in the store', fakeAsync(() => {
|
|
68
|
+
TestBed.runInInjectionContext(() => {
|
|
69
|
+
const store = new Store();
|
|
70
|
+
|
|
71
|
+
tick();
|
|
72
|
+
|
|
73
|
+
expect(store.entities().length).toBe(0);
|
|
74
|
+
|
|
75
|
+
store.create(createFlight({ id: 3 }));
|
|
76
|
+
|
|
77
|
+
tick();
|
|
78
|
+
|
|
79
|
+
expect(store.entities().length).toBe(1);
|
|
80
|
+
expect(store.current()).toEqual(createFlight({ id: 3 }));
|
|
81
|
+
});
|
|
82
|
+
}));
|
|
83
|
+
it('should create from a service and set an entity in the store (with named collection)', fakeAsync(() => {
|
|
84
|
+
TestBed.runInInjectionContext(() => {
|
|
85
|
+
const store = new StoreWithNamedCollection();
|
|
86
|
+
|
|
87
|
+
tick();
|
|
88
|
+
|
|
89
|
+
expect(store.flightEntities().length).toBe(0);
|
|
90
|
+
|
|
91
|
+
store.createFlight(createFlight({ id: 3 }));
|
|
92
|
+
|
|
93
|
+
tick();
|
|
94
|
+
|
|
95
|
+
expect(store.flightEntities().length).toBe(1);
|
|
96
|
+
expect(store.currentFlight()).toEqual(createFlight({ id: 3 }));
|
|
97
|
+
});
|
|
98
|
+
}));
|
|
99
|
+
it('should update from a service and update an entity in the store', fakeAsync(() => {
|
|
100
|
+
TestBed.runInInjectionContext(() => {
|
|
101
|
+
const store = new Store();
|
|
102
|
+
|
|
103
|
+
tick();
|
|
104
|
+
|
|
105
|
+
expect(store.entities().length).toBe(0);
|
|
106
|
+
|
|
107
|
+
store.create(createFlight({ id: 3, from: 'Wadena MN' }));
|
|
108
|
+
tick();
|
|
109
|
+
store.update(createFlight({ id: 3 }));
|
|
110
|
+
tick();
|
|
111
|
+
|
|
112
|
+
expect(store.current()).toEqual(createFlight({ id: 3 }));
|
|
113
|
+
});
|
|
114
|
+
}));
|
|
115
|
+
it('should update from a service and update an entity in the store (with named collection)', fakeAsync(() => {
|
|
116
|
+
TestBed.runInInjectionContext(() => {
|
|
117
|
+
const store = new StoreWithNamedCollection();
|
|
118
|
+
|
|
119
|
+
tick();
|
|
120
|
+
|
|
121
|
+
expect(store.flightEntities().length).toBe(0);
|
|
122
|
+
|
|
123
|
+
store.createFlight(createFlight({ id: 3, from: 'Wadena MN' }));
|
|
124
|
+
tick();
|
|
125
|
+
store.updateFlight(createFlight({ id: 3 }));
|
|
126
|
+
tick();
|
|
127
|
+
|
|
128
|
+
expect(store.currentFlight()).toEqual(createFlight({ id: 3 }));
|
|
129
|
+
});
|
|
130
|
+
}));
|
|
131
|
+
it('should update all from a service and update all entities in the store', fakeAsync(() => {
|
|
132
|
+
TestBed.runInInjectionContext(() => {
|
|
133
|
+
const store = new Store();
|
|
134
|
+
|
|
135
|
+
tick();
|
|
136
|
+
|
|
137
|
+
expect(store.entities().length).toBe(0);
|
|
138
|
+
|
|
139
|
+
store.create(createFlight({ id: 3, from: 'Wadena MN' }));
|
|
140
|
+
store.create(createFlight({ id: 4, from: 'Wadena MN' }));
|
|
141
|
+
tick();
|
|
142
|
+
store.updateAll([createFlight({ id: 3 }), createFlight({ id: 4 })]);
|
|
143
|
+
tick();
|
|
144
|
+
expect(store.entities().length).toBe(2);
|
|
145
|
+
expect(store.entities().at(0)).toEqual(createFlight({ id: 3 }));
|
|
146
|
+
expect(store.entities().at(1)).toEqual(createFlight({ id: 4 }));
|
|
147
|
+
});
|
|
148
|
+
}));
|
|
149
|
+
it('should update all from a service and update all entities in the store (with named collection)', fakeAsync(() => {
|
|
150
|
+
TestBed.runInInjectionContext(() => {
|
|
151
|
+
const store = new StoreWithNamedCollection();
|
|
152
|
+
|
|
153
|
+
tick();
|
|
154
|
+
|
|
155
|
+
expect(store.flightEntities().length).toBe(0);
|
|
156
|
+
|
|
157
|
+
store.createFlight(createFlight({ id: 3, from: 'Wadena MN' }));
|
|
158
|
+
store.createFlight(createFlight({ id: 4, from: 'Wadena MN' }));
|
|
159
|
+
tick();
|
|
160
|
+
store.updateAllFlight([createFlight({ id: 3 }), createFlight({ id: 4 })]);
|
|
161
|
+
tick();
|
|
162
|
+
expect(store.flightEntities().length).toBe(2);
|
|
163
|
+
expect(store.flightEntities().at(0)).toEqual(createFlight({ id: 3 }));
|
|
164
|
+
expect(store.flightEntities().at(1)).toEqual(createFlight({ id: 4 }));
|
|
165
|
+
});
|
|
166
|
+
}));
|
|
167
|
+
it('should delete from a service and update that entity in the store', fakeAsync(() => {
|
|
168
|
+
TestBed.runInInjectionContext(() => {
|
|
169
|
+
const store = new Store();
|
|
170
|
+
|
|
171
|
+
tick();
|
|
172
|
+
|
|
173
|
+
expect(store.entities().length).toBe(0);
|
|
174
|
+
|
|
175
|
+
store.create(createFlight({ id: 3 }));
|
|
176
|
+
tick();
|
|
177
|
+
expect(store.entities().length).toBe(1);
|
|
178
|
+
expect(store.entities().at(0)).toEqual(createFlight({ id: 3 }));
|
|
179
|
+
store.delete(createFlight({ id: 3 }));
|
|
180
|
+
tick();
|
|
181
|
+
expect(store.entities().length).toBe(0);
|
|
182
|
+
});
|
|
183
|
+
}));
|
|
184
|
+
it('should delete from a service and update that entity in the store (with named collection)', fakeAsync(() => {
|
|
185
|
+
TestBed.runInInjectionContext(() => {
|
|
186
|
+
const store = new StoreWithNamedCollection();
|
|
187
|
+
|
|
188
|
+
tick();
|
|
189
|
+
|
|
190
|
+
expect(store.flightEntities().length).toBe(0);
|
|
191
|
+
|
|
192
|
+
store.createFlight(createFlight({ id: 3 }));
|
|
193
|
+
tick();
|
|
194
|
+
expect(store.flightEntities().length).toBe(1);
|
|
195
|
+
expect(store.flightEntities().at(0)).toEqual(createFlight({ id: 3 }));
|
|
196
|
+
store.deleteFlight(createFlight({ id: 3 }));
|
|
197
|
+
tick();
|
|
198
|
+
expect(store.flightEntities().length).toBe(0);
|
|
199
|
+
});
|
|
200
|
+
}));
|
|
201
|
+
it('should update the selected flight of the store', fakeAsync(() => {
|
|
202
|
+
TestBed.runInInjectionContext(() => {
|
|
203
|
+
const store = new Store();
|
|
204
|
+
|
|
205
|
+
tick();
|
|
206
|
+
|
|
207
|
+
store.create(createFlight({ id: 3 }));
|
|
208
|
+
expect(store.selectedEntities().length).toBe(0);
|
|
209
|
+
|
|
210
|
+
store.updateSelected(3, true);
|
|
211
|
+
|
|
212
|
+
tick();
|
|
213
|
+
|
|
214
|
+
expect(store.selectedEntities().length).toBe(1);
|
|
215
|
+
expect(store.selectedEntities()).toContainEqual(createFlight({ id: 3 }));
|
|
216
|
+
});
|
|
217
|
+
}));
|
|
218
|
+
it('should update selected flight of the store (with named collection)', fakeAsync(() => {
|
|
219
|
+
TestBed.runInInjectionContext(() => {
|
|
220
|
+
const store = new StoreWithNamedCollection();
|
|
221
|
+
|
|
222
|
+
tick();
|
|
223
|
+
|
|
224
|
+
store.createFlight(createFlight({ id: 3 }));
|
|
225
|
+
expect(store.selectedFlightEntities().length).toBe(0);
|
|
226
|
+
|
|
227
|
+
store.updateSelectedFlightEntities(3, true);
|
|
228
|
+
|
|
229
|
+
tick();
|
|
230
|
+
|
|
231
|
+
expect(store.selectedFlightEntities().length).toBe(1);
|
|
232
|
+
expect(store.selectedFlightEntities()).toContainEqual(
|
|
233
|
+
createFlight({ id: 3 }),
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
}));
|
|
237
|
+
it('should update the filter of the service', fakeAsync(() => {
|
|
238
|
+
TestBed.runInInjectionContext(() => {
|
|
239
|
+
const store = new Store();
|
|
240
|
+
|
|
241
|
+
tick();
|
|
242
|
+
|
|
243
|
+
expect(store.filter()).toEqual({ from: 'Paris', to: 'New York' });
|
|
244
|
+
|
|
245
|
+
store.updateFilter({ from: 'Wadena MN', to: 'New York' });
|
|
246
|
+
|
|
247
|
+
tick();
|
|
248
|
+
|
|
249
|
+
expect(store.filter()).toEqual({ from: 'Wadena MN', to: 'New York' });
|
|
250
|
+
});
|
|
251
|
+
}));
|
|
252
|
+
it('should update the filter of the service (with named collection)', fakeAsync(() => {
|
|
253
|
+
TestBed.runInInjectionContext(() => {
|
|
254
|
+
const store = new StoreWithNamedCollection();
|
|
255
|
+
|
|
256
|
+
tick();
|
|
257
|
+
|
|
258
|
+
expect(store.flightFilter()).toEqual({ from: 'Paris', to: 'New York' });
|
|
259
|
+
|
|
260
|
+
store.updateFlightFilter({ from: 'Wadena MN', to: 'New York' });
|
|
261
|
+
|
|
262
|
+
tick();
|
|
263
|
+
|
|
264
|
+
expect(store.flightFilter()).toEqual({
|
|
265
|
+
from: 'Wadena MN',
|
|
266
|
+
to: 'New York',
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}));
|
|
270
|
+
it('should set the current entity', fakeAsync(() => {
|
|
271
|
+
TestBed.runInInjectionContext(() => {
|
|
272
|
+
const store = new Store();
|
|
273
|
+
tick();
|
|
274
|
+
|
|
275
|
+
store.create(createFlight({ id: 3 }));
|
|
276
|
+
|
|
277
|
+
store.setCurrent(createFlight({ id: 4 }));
|
|
278
|
+
|
|
279
|
+
expect(store.current()).toEqual(createFlight({ id: 4 }));
|
|
280
|
+
});
|
|
281
|
+
}));
|
|
282
|
+
it('should set the current entity (with named collection)', fakeAsync(() => {
|
|
283
|
+
TestBed.runInInjectionContext(() => {
|
|
284
|
+
const store = new StoreWithNamedCollection();
|
|
285
|
+
tick();
|
|
286
|
+
|
|
287
|
+
store.createFlight(createFlight({ id: 3 }));
|
|
288
|
+
|
|
289
|
+
store.setCurrentFlight(createFlight({ id: 4 }));
|
|
290
|
+
|
|
291
|
+
expect(store.currentFlight()).toEqual(createFlight({ id: 4 }));
|
|
292
|
+
});
|
|
293
|
+
}));
|
|
294
|
+
|
|
295
|
+
it('handles loading state', fakeAsync(() => {
|
|
296
|
+
TestBed.runInInjectionContext(() => {
|
|
297
|
+
const store = new StoreForLoading();
|
|
298
|
+
tick();
|
|
299
|
+
|
|
300
|
+
expect(store.loading()).toBe(false);
|
|
301
|
+
|
|
302
|
+
store.create(createFlight({ id: 3 }));
|
|
303
|
+
tick();
|
|
304
|
+
expect(store.loading()).toBe(true);
|
|
305
|
+
tick(3);
|
|
306
|
+
expect(store.loading()).toBe(false);
|
|
307
|
+
|
|
308
|
+
store.load();
|
|
309
|
+
tick();
|
|
310
|
+
expect(store.loading()).toBe(true);
|
|
311
|
+
tick(3);
|
|
312
|
+
expect(store.loading()).toBe(false);
|
|
313
|
+
|
|
314
|
+
store.loadById(3);
|
|
315
|
+
tick();
|
|
316
|
+
expect(store.loading()).toBe(true);
|
|
317
|
+
tick(3);
|
|
318
|
+
expect(store.loading()).toBe(false);
|
|
319
|
+
|
|
320
|
+
store.update(createFlight({ id: 3 }));
|
|
321
|
+
tick();
|
|
322
|
+
expect(store.loading()).toBe(true);
|
|
323
|
+
tick(3);
|
|
324
|
+
expect(store.loading()).toBe(false);
|
|
325
|
+
|
|
326
|
+
store.updateAll([createFlight({ id: 3 }), createFlight({ id: 4 })]);
|
|
327
|
+
tick();
|
|
328
|
+
expect(store.loading()).toBe(true);
|
|
329
|
+
tick(3);
|
|
330
|
+
expect(store.loading()).toBe(false);
|
|
331
|
+
|
|
332
|
+
store.delete(createFlight({ id: 3 }));
|
|
333
|
+
tick();
|
|
334
|
+
expect(store.loading()).toBe(true);
|
|
335
|
+
tick(3);
|
|
336
|
+
expect(store.loading()).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
it('handles loading state (with named collection)', fakeAsync(() => {
|
|
341
|
+
TestBed.runInInjectionContext(() => {
|
|
342
|
+
const store = new StoreWithNamedCollectionForLoading();
|
|
343
|
+
tick();
|
|
344
|
+
|
|
345
|
+
expect(store.flightLoading()).toBe(false);
|
|
346
|
+
|
|
347
|
+
store.createFlight(createFlight({ id: 3 }));
|
|
348
|
+
tick();
|
|
349
|
+
expect(store.flightLoading()).toBe(true);
|
|
350
|
+
tick(3);
|
|
351
|
+
expect(store.flightLoading()).toBe(false);
|
|
352
|
+
|
|
353
|
+
store.loadFlightEntities();
|
|
354
|
+
tick();
|
|
355
|
+
expect(store.flightLoading()).toBe(true);
|
|
356
|
+
tick(3);
|
|
357
|
+
expect(store.flightLoading()).toBe(false);
|
|
358
|
+
|
|
359
|
+
store.loadFlightById(3);
|
|
360
|
+
tick();
|
|
361
|
+
expect(store.flightLoading()).toBe(true);
|
|
362
|
+
tick(3);
|
|
363
|
+
expect(store.flightLoading()).toBe(false);
|
|
364
|
+
|
|
365
|
+
store.updateFlight(createFlight({ id: 3 }));
|
|
366
|
+
tick();
|
|
367
|
+
expect(store.flightLoading()).toBe(true);
|
|
368
|
+
tick(3);
|
|
369
|
+
expect(store.flightLoading()).toBe(false);
|
|
370
|
+
|
|
371
|
+
store.updateAllFlight([createFlight({ id: 3 }), createFlight({ id: 4 })]);
|
|
372
|
+
tick();
|
|
373
|
+
expect(store.flightLoading()).toBe(true);
|
|
374
|
+
tick(3);
|
|
375
|
+
expect(store.flightLoading()).toBe(false);
|
|
376
|
+
|
|
377
|
+
store.deleteFlight(createFlight({ id: 3 }));
|
|
378
|
+
tick();
|
|
379
|
+
expect(store.flightLoading()).toBe(true);
|
|
380
|
+
tick(3);
|
|
381
|
+
expect(store.flightLoading()).toBe(false);
|
|
382
|
+
});
|
|
383
|
+
}));
|
|
384
|
+
|
|
385
|
+
it('should ensure that collection name is provided in callState ', () => {
|
|
386
|
+
// @ts-expect-error should not allow `withCallState` without collection name if `withDataService` has it
|
|
387
|
+
signalStore(
|
|
388
|
+
withCallState(),
|
|
389
|
+
withEntities({
|
|
390
|
+
entity: type<Flight>(),
|
|
391
|
+
collection: 'flight',
|
|
392
|
+
}),
|
|
393
|
+
withDataService({
|
|
394
|
+
dataServiceType: MockFlightService,
|
|
395
|
+
filter: { from: 'Paris', to: 'New York' },
|
|
396
|
+
collection: 'flight',
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// TODO 3A: setting error state (without named collection)
|
|
402
|
+
// TODO 3B: setting error state (with named collection)
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Test helpers
|
|
406
|
+
let currentFlightId = 0;
|
|
407
|
+
const createFlight = (flight: Partial<Flight> = {}) => ({
|
|
408
|
+
...{
|
|
409
|
+
id: ++currentFlightId,
|
|
410
|
+
from: 'Paris',
|
|
411
|
+
to: 'New York',
|
|
412
|
+
date: new Date().toDateString(),
|
|
413
|
+
delayed: false,
|
|
414
|
+
},
|
|
415
|
+
...flight,
|
|
416
|
+
});
|
|
417
|
+
type Flight = {
|
|
418
|
+
id: number;
|
|
419
|
+
from: string;
|
|
420
|
+
to: string;
|
|
421
|
+
date: string;
|
|
422
|
+
delayed: boolean;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
type FlightFilter = {
|
|
426
|
+
from: string;
|
|
427
|
+
to: string;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
@Injectable({
|
|
431
|
+
providedIn: 'root',
|
|
432
|
+
})
|
|
433
|
+
class MockFlightService implements DataService<Flight, FlightFilter> {
|
|
434
|
+
loadById(id: EntityId): Promise<Flight> {
|
|
435
|
+
return firstValueFrom(this.findById('' + id));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
create(entity: Flight): Promise<Flight> {
|
|
439
|
+
return firstValueFrom(this.save(entity));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
update(entity: Flight): Promise<Flight> {
|
|
443
|
+
return firstValueFrom(this.save(entity));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
updateAll(entity: Flight[]): Promise<Flight[]> {
|
|
447
|
+
return firstValueFrom(of(entity));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
delete(entity: Flight): Promise<void> {
|
|
451
|
+
return firstValueFrom(this.remove(entity));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
load(filter: FlightFilter): Promise<Flight[]> {
|
|
455
|
+
return firstValueFrom(this.find(filter.from, filter.to));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private find(_from: string, _to: string): Observable<Flight[]> {
|
|
459
|
+
return of([createFlight()]);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private findById(id: string): Observable<Flight> {
|
|
463
|
+
return of(createFlight({ id: Number(id) }));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private save(flight: Flight): Observable<Flight> {
|
|
467
|
+
return of(flight);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private remove(_flight: Flight): Observable<void> {
|
|
471
|
+
return of(undefined);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@Injectable({
|
|
476
|
+
providedIn: 'root',
|
|
477
|
+
})
|
|
478
|
+
class MockFlightServiceForLoading implements DataService<Flight, FlightFilter> {
|
|
479
|
+
loadById(id: EntityId): Promise<Flight> {
|
|
480
|
+
return firstValueFrom(this.findById('' + id));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
create(entity: Flight): Promise<Flight> {
|
|
484
|
+
return firstValueFrom(this.save(entity));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
update(entity: Flight): Promise<Flight> {
|
|
488
|
+
return firstValueFrom(this.save(entity));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
updateAll(entity: Flight[]): Promise<Flight[]> {
|
|
492
|
+
return firstValueFrom(of(entity).pipe(delay(3)));
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
delete(entity: Flight): Promise<void> {
|
|
496
|
+
return firstValueFrom(this.remove(entity));
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
load(filter: FlightFilter): Promise<Flight[]> {
|
|
500
|
+
return firstValueFrom(this.find(filter.from, filter.to));
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private find(_from: string, _to: string): Observable<Flight[]> {
|
|
504
|
+
return of([createFlight({ id: 1 })]).pipe(delay(3));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private findById(id: string): Observable<Flight> {
|
|
508
|
+
return of(createFlight({ id: Number(id) })).pipe(delay(3));
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
private save(flight: Flight): Observable<Flight> {
|
|
512
|
+
return of(createFlight(flight)).pipe(delay(3));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
private remove(_flight: Flight): Observable<void> {
|
|
516
|
+
return of(undefined).pipe(delay(3));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const Store = signalStore(
|
|
521
|
+
withCallState(),
|
|
522
|
+
withEntities<Flight>(),
|
|
523
|
+
withDataService({
|
|
524
|
+
dataServiceType: MockFlightService,
|
|
525
|
+
filter: { from: 'Paris', to: 'New York' },
|
|
526
|
+
}),
|
|
527
|
+
);
|
|
528
|
+
const StoreWithNamedCollection = signalStore(
|
|
529
|
+
withCallState({
|
|
530
|
+
collection: 'flight',
|
|
531
|
+
}),
|
|
532
|
+
withEntities({
|
|
533
|
+
entity: type<Flight>(),
|
|
534
|
+
collection: 'flight',
|
|
535
|
+
}),
|
|
536
|
+
withDataService({
|
|
537
|
+
dataServiceType: MockFlightService,
|
|
538
|
+
filter: { from: 'Paris', to: 'New York' },
|
|
539
|
+
collection: 'flight',
|
|
540
|
+
}),
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
const StoreForLoading = signalStore(
|
|
544
|
+
withCallState(),
|
|
545
|
+
withEntities<Flight>(),
|
|
546
|
+
withDataService({
|
|
547
|
+
dataServiceType: MockFlightServiceForLoading,
|
|
548
|
+
filter: { from: 'Paris', to: 'New York' },
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
const StoreWithNamedCollectionForLoading = signalStore(
|
|
552
|
+
withCallState({
|
|
553
|
+
collection: 'flight',
|
|
554
|
+
}),
|
|
555
|
+
withEntities({
|
|
556
|
+
entity: type<Flight>(),
|
|
557
|
+
collection: 'flight',
|
|
558
|
+
}),
|
|
559
|
+
withDataService({
|
|
560
|
+
dataServiceType: MockFlightServiceForLoading,
|
|
561
|
+
filter: { from: 'Paris', to: 'New York' },
|
|
562
|
+
collection: 'flight',
|
|
563
|
+
}),
|
|
564
|
+
);
|