Skip to content

Commit

Permalink
Add (failing) tests for setting undefined as initial value in `deri…
Browse files Browse the repository at this point in the history
…vedFrom` (#435)

* test(derived-from): `undefined` initial value

add tests for setting `undefined` as initial value in `derivedFrom`

* fix(derived-from): use 'in' operator to detect initialValue instead of comparing against undefined

---------

Co-authored-by: Chau Tran <[email protected]>
  • Loading branch information
mattmoos and nartc authored Oct 8, 2024
1 parent a903d2e commit 9d44905
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
43 changes: 43 additions & 0 deletions libs/ngxtension/derived-from/src/derived-from.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,27 @@ describe(derivedFrom.name, () => {
expect(s()[0].toFixed(2)).toEqual('1.00'); // here we can call s()[0].toFixed(2) and will works!
});
}));
it(`for Observables that don't emit synchronously, you can pass undefined as options.initialValue to prevent error`, fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const late = of(1).pipe(delay(1000)); // late emit after 1s
expect(() =>
derivedFrom(
[late],
map(([it]) => it),
{ initialValue: undefined },
),
).not.toThrow();
const s = derivedFrom(
[late],
map(([it]) => it),
{ initialValue: undefined },
);
expect(s()).toEqual(undefined);
tick(1000); // wait 1s for late emit
expect(s()).toEqual(1); // now we have the real value
expect(s().toFixed(2)).toEqual('1.00'); // here we can call s()[0].toFixed(2) and will works!
});
}));
it('value inside array', () => {
TestBed.runInInjectionContext(() => {
const value = new BehaviorSubject(1);
Expand Down Expand Up @@ -149,6 +170,28 @@ describe(derivedFrom.name, () => {
expect(s().value.toUpperCase()).toEqual('A'); // here we can call s().value.toUpperCase() and will works!
});
}));
it('with real async value, you can pass undefined as options.initialValue to prevent error', fakeAsync(() => {
TestBed.runInInjectionContext(() => {
const value = new Promise<string>((resolve) =>
setTimeout(resolve, 1000, 'a'),
); //Promise that emit 'a' after 1s
expect(() =>
derivedFrom(
{ value },
map(({ value }) => value),
{ initialValue: undefined },
),
).not.toThrow();
const s = derivedFrom(
{ value },
map(({ value }) => value),
{ initialValue: undefined },
);
expect(s()).toEqual(undefined);
tick(1000); // wait 1s for late emit of Promise
expect(s()).toEqual('a'); // after 1s we have the resolved value
});
}));
it('with a primitive string (that is Iterable), interally converted with from(iter) will emit single value last char (maybe not expected!? -> I suggest using of() for primitives/array)', () => {
TestBed.runInInjectionContext(() => {
const iter = 'abcd';
Expand Down
2 changes: 1 addition & 1 deletion libs/ngxtension/derived-from/src/derived-from.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ function _normalizeArgs<Input, Output>(
);
if (!hasOperator) args.splice(1, 0, identity);
const [sources, operator, options] = args;
const hasInitValue = options?.initialValue !== undefined;
const hasInitValue = !!options && 'initialValue' in options;
const normalizedSources = Object.entries(sources).reduce(
(acc, [keyOrIndex, source]) => {
if (isSignal(source)) {
Expand Down

0 comments on commit 9d44905

Please sign in to comment.