8
8
9
9
import {
10
10
ApplicationRef ,
11
+ computed ,
11
12
createEnvironmentInjector ,
13
+ effect ,
12
14
EnvironmentInjector ,
13
15
Injector ,
14
16
resource ,
@@ -140,16 +142,18 @@ describe('resource', () => {
140
142
} ) ;
141
143
142
144
TestBed . tick ( ) ;
143
- await backend . reject ( requestParam , 'Something went wrong....' ) ;
145
+ await backend . reject ( requestParam , new Error ( 'Something went wrong....' ) ) ;
144
146
145
147
expect ( echoResource . status ( ) ) . toBe ( 'error' ) ;
146
148
expect ( echoResource . isLoading ( ) ) . toBeFalse ( ) ;
147
149
expect ( echoResource . hasValue ( ) ) . toBeFalse ( ) ;
148
- expect ( echoResource . value ( ) ) . toEqual ( undefined ) ;
149
- expect ( echoResource . error ( ) ) . toEqual (
150
- jasmine . objectContaining ( { cause : 'Something went wrong....' } ) ,
151
- ) ,
152
- expect ( echoResource . error ( ) ! . message ) . toContain ( 'Resource' ) ;
150
+
151
+ const err = extractError ( ( ) => echoResource . value ( ) ) ! ;
152
+ expect ( err ) . not . toBeUndefined ( ) ;
153
+ expect ( err instanceof Error ) . toBeTrue ( ) ;
154
+ expect ( err ! . message ) . toContain ( 'Resource' ) ;
155
+ expect ( err . cause ) . toEqual ( new Error ( 'Something went wrong....' ) ) ;
156
+ expect ( echoResource . error ( ) ) . toEqual ( new Error ( 'Something went wrong....' ) ) ;
153
157
} ) ;
154
158
155
159
it ( 'should expose errors on reload' , async ( ) => {
@@ -183,8 +187,109 @@ describe('resource', () => {
183
187
expect ( echoResource . status ( ) ) . toBe ( 'error' ) ;
184
188
expect ( echoResource . isLoading ( ) ) . toBeFalse ( ) ;
185
189
expect ( echoResource . hasValue ( ) ) . toBeFalse ( ) ;
186
- expect ( echoResource . value ( ) ) . toEqual ( undefined ) ;
190
+ const err = extractError ( ( ) => echoResource . value ( ) ) ! ;
191
+ expect ( err ) . not . toBeUndefined ( ) ;
192
+ expect ( err . message ) . toContain ( 'Resource' ) ;
193
+ expect ( err . message ) . toContain ( 'KO' ) ;
194
+ expect ( err . cause ) . toEqual ( new Error ( 'KO' ) ) ;
195
+ expect ( echoResource . error ( ) ) . toEqual ( Error ( 'KO' ) ) ;
196
+
197
+ counter . update ( ( value ) => value + 1 ) ;
198
+ TestBed . tick ( ) ;
199
+ await backend . flush ( ) ;
200
+
201
+ expect ( echoResource . status ( ) ) . toBe ( 'resolved' ) ;
202
+ expect ( echoResource . isLoading ( ) ) . toBeFalse ( ) ;
203
+ expect ( echoResource . hasValue ( ) ) . toBeTrue ( ) ;
204
+ expect ( echoResource . value ( ) ) . toEqual ( 'ok' ) ;
205
+ expect ( echoResource . error ( ) ) . toBe ( undefined ) ;
206
+ } ) ;
207
+
208
+ it ( 'should not trigger consumers on every status change via hasValue()' , async ( ) => {
209
+ const params = signal < number | undefined > ( undefined ) ;
210
+ const testResource = resource ( {
211
+ params,
212
+ // Hanging promise, never resolves
213
+ loader : ( ) => new Promise ( ( ) => { } ) ,
214
+ injector : TestBed . inject ( Injector ) ,
215
+ } ) ;
216
+
217
+ let effectRuns = 0 ;
218
+ effect (
219
+ ( ) => {
220
+ testResource . hasValue ( ) ;
221
+ effectRuns ++ ;
222
+ } ,
223
+ { injector : TestBed . inject ( Injector ) } ,
224
+ ) ;
225
+
226
+ TestBed . tick ( ) ;
227
+ // Params are undefined, status should be idle.
228
+ expect ( testResource . status ( ) ) . toBe ( 'idle' ) ;
229
+ // Effect should run the first time.
230
+ expect ( effectRuns ) . toBe ( 1 ) ;
231
+
232
+ // Set params, causing the stauts to become 'loading'.
233
+ params . set ( 0 ) ;
234
+ expect ( testResource . status ( ) ) . toBe ( 'loading' ) ;
235
+ TestBed . tick ( ) ;
236
+ // The effect should not rerun.
237
+ expect ( effectRuns ) . toBe ( 1 ) ;
238
+ } ) ;
239
+
240
+ it ( 'should update computed signals' , async ( ) => {
241
+ const backend = new MockEchoBackend ( ) ;
242
+ const counter = signal ( 0 ) ;
243
+ const echoResource = resource ( {
244
+ params : ( ) => ( { counter : counter ( ) } ) ,
245
+ loader : ( params ) => {
246
+ if ( params . params . counter % 2 === 0 ) {
247
+ return Promise . resolve ( params . params . counter ) ;
248
+ } else {
249
+ throw new Error ( 'KO' ) ;
250
+ }
251
+ } ,
252
+ injector : TestBed . inject ( Injector ) ,
253
+ } ) ;
254
+ const computedValue = computed ( ( ) => {
255
+ if ( ! echoResource . hasValue ( ) ) {
256
+ return - 1 ;
257
+ }
258
+ return echoResource . value ( ) ;
259
+ } ) ;
260
+
261
+ TestBed . tick ( ) ;
262
+ await backend . flush ( ) ;
263
+
264
+ expect ( echoResource . status ( ) ) . toBe ( 'resolved' ) ;
265
+ expect ( echoResource . hasValue ( ) ) . toBeTrue ( ) ;
266
+ expect ( echoResource . value ( ) ) . toEqual ( 0 ) ;
267
+ expect ( computedValue ( ) ) . toEqual ( 0 ) ;
268
+ expect ( echoResource . error ( ) ) . toBe ( undefined ) ;
269
+
270
+ counter . update ( ( value ) => value + 1 ) ;
271
+ TestBed . tick ( ) ;
272
+ await backend . flush ( ) ;
273
+
274
+ expect ( echoResource . status ( ) ) . toBe ( 'error' ) ;
275
+ expect ( echoResource . hasValue ( ) ) . toBeFalse ( ) ;
276
+ const err = extractError ( ( ) => echoResource . value ( ) ) ! ;
277
+ expect ( err ) . not . toBeUndefined ( ) ;
278
+ expect ( err . message ) . toContain ( 'Resource' ) ;
279
+ expect ( err . message ) . toContain ( 'KO' ) ;
280
+ expect ( err . cause ) . toEqual ( new Error ( 'KO' ) ) ;
281
+ expect ( computedValue ( ) ) . toEqual ( - 1 ) ;
187
282
expect ( echoResource . error ( ) ) . toEqual ( Error ( 'KO' ) ) ;
283
+
284
+ counter . update ( ( value ) => value + 1 ) ;
285
+ TestBed . tick ( ) ;
286
+ await backend . flush ( ) ;
287
+
288
+ expect ( echoResource . status ( ) ) . toBe ( 'resolved' ) ;
289
+ expect ( echoResource . hasValue ( ) ) . toBeTrue ( ) ;
290
+ expect ( echoResource . value ( ) ) . toEqual ( 2 ) ;
291
+ expect ( computedValue ( ) ) . toEqual ( 2 ) ;
292
+ expect ( echoResource . error ( ) ) . toBe ( undefined ) ;
188
293
} ) ;
189
294
190
295
it ( 'should respond to a request that changes while loading' , async ( ) => {
@@ -231,7 +336,7 @@ describe('resource', () => {
231
336
expect ( res . value ( ) ) . toBe ( 1 ) ;
232
337
} ) ;
233
338
234
- it ( 'should return a default value if provided' , async ( ) => {
339
+ it ( 'should throw an error when getting a value even when provided with a default value ' , async ( ) => {
235
340
const DEFAULT : string [ ] = [ ] ;
236
341
const request = signal ( 0 ) ;
237
342
const res = resource ( {
@@ -256,7 +361,11 @@ describe('resource', () => {
256
361
request . set ( 2 ) ;
257
362
await TestBed . inject ( ApplicationRef ) . whenStable ( ) ;
258
363
expect ( res . error ( ) ) . not . toBeUndefined ( ) ;
259
- expect ( res . value ( ) ) . toBe ( DEFAULT ) ;
364
+ const err = extractError ( ( ) => res . value ( ) ) ! ;
365
+ expect ( err ) . not . toBeUndefined ( ) ;
366
+ expect ( err . message ) . toContain ( 'Resource' ) ;
367
+ expect ( err . message ) . toContain ( 'err' ) ;
368
+ expect ( err . cause ) . toEqual ( new Error ( 'err' ) ) ;
260
369
} ) ;
261
370
262
371
it ( 'should _not_ load if the request resolves to undefined' , ( ) => {
@@ -731,3 +840,12 @@ describe('resource', () => {
731
840
function flushMicrotasks ( ) : Promise < void > {
732
841
return new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) ;
733
842
}
843
+
844
+ function extractError ( fn : ( ) => unknown ) : Error | undefined {
845
+ try {
846
+ fn ( ) ;
847
+ return undefined ;
848
+ } catch ( err : unknown ) {
849
+ return err as Error ;
850
+ }
851
+ }
0 commit comments