Skip to content

Commit daf1e64

Browse files
fix(effects): dispatch OnInitEffects action after registration (#2386)
Closes #2373
1 parent 188a765 commit daf1e64

File tree

3 files changed

+305
-157
lines changed

3 files changed

+305
-157
lines changed

modules/effects/spec/effect_sources.spec.ts

Lines changed: 68 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -69,89 +69,6 @@ describe('EffectSources', () => {
6969
expect(effectSources.next).toHaveBeenCalledWith(effectSource);
7070
});
7171

72-
it('should dispatch an action on ngrxOnInitEffects after being registered', () => {
73-
class EffectWithInitAction implements OnInitEffects {
74-
ngrxOnInitEffects() {
75-
return { type: '[EffectWithInitAction] Init' };
76-
}
77-
}
78-
const store = TestBed.get(Store);
79-
80-
effectSources.addEffects(new EffectWithInitAction());
81-
82-
expect(store.dispatch).toHaveBeenCalledTimes(1);
83-
expect(store.dispatch).toHaveBeenCalledWith({
84-
type: '[EffectWithInitAction] Init',
85-
});
86-
});
87-
88-
it('should dispatch an action on ngrxOnInitEffects after being registered (class has effects)', () => {
89-
class EffectWithInitActionAndEffects implements OnInitEffects {
90-
effectOne = createEffect(() => {
91-
return this.actions$.pipe(
92-
ofType('Action 1'),
93-
mapTo({ type: 'Action 1 Response' })
94-
);
95-
});
96-
effectTwo = createEffect(() => {
97-
return this.actions$.pipe(
98-
ofType('Action 2'),
99-
mapTo({ type: 'Action 2 Response' })
100-
);
101-
});
102-
103-
ngrxOnInitEffects() {
104-
return { type: '[EffectWithInitAction] Init' };
105-
}
106-
107-
constructor(private actions$: Actions) {}
108-
}
109-
const store = TestBed.get(Store);
110-
111-
effectSources.addEffects(new EffectWithInitActionAndEffects(new Subject()));
112-
113-
expect(store.dispatch).toHaveBeenCalledTimes(1);
114-
expect(store.dispatch).toHaveBeenCalledWith({
115-
type: '[EffectWithInitAction] Init',
116-
});
117-
});
118-
119-
it('should only dispatch an action on ngrxOnInitEffects once after being registered', () => {
120-
class EffectWithInitAction implements OnInitEffects {
121-
ngrxOnInitEffects() {
122-
return { type: '[EffectWithInitAction] Init' };
123-
}
124-
}
125-
const store = TestBed.get(Store);
126-
127-
effectSources.addEffects(new EffectWithInitAction());
128-
effectSources.addEffects(new EffectWithInitAction());
129-
130-
expect(store.dispatch).toHaveBeenCalledTimes(1);
131-
});
132-
133-
it('should dispatch an action on ngrxOnInitEffects multiple times after being registered with different identifiers', () => {
134-
let id = 0;
135-
class EffectWithInitAction implements OnInitEffects, OnIdentifyEffects {
136-
effectId = '';
137-
ngrxOnIdentifyEffects(): string {
138-
return this.effectId;
139-
}
140-
ngrxOnInitEffects() {
141-
return { type: '[EffectWithInitAction] Init' };
142-
}
143-
constructor() {
144-
this.effectId = (id++).toString();
145-
}
146-
}
147-
const store = TestBed.get(Store);
148-
149-
effectSources.addEffects(new EffectWithInitAction());
150-
effectSources.addEffects(new EffectWithInitAction());
151-
152-
expect(store.dispatch).toHaveBeenCalledTimes(2);
153-
});
154-
15572
describe('toActions() Operator', () => {
15673
function toActions(source: any): Observable<any> {
15774
source['errorHandler'] = mockErrorReporter;
@@ -413,6 +330,7 @@ describe('EffectSources', () => {
413330
const f = null;
414331
const i = { type: 'From Source Identifier' };
415332
const i2 = { type: 'From Source Identifier 2' };
333+
const initAction = { type: '[SourceWithInitAction] Init' };
416334

417335
let circularRef = {} as any;
418336
circularRef.circularRef = circularRef;
@@ -499,6 +417,35 @@ describe('EffectSources', () => {
499417
this.effectIdentifier = identifier;
500418
}
501419
}
420+
class SourceWithInitAction implements OnInitEffects, OnIdentifyEffects {
421+
effectIdentifier: string;
422+
423+
ngrxOnInitEffects() {
424+
return initAction;
425+
}
426+
427+
ngrxOnIdentifyEffects() {
428+
return this.effectIdentifier;
429+
}
430+
431+
effectOne = createEffect(() => {
432+
return this.actions$.pipe(
433+
ofType('Action 1'),
434+
mapTo({ type: 'Action 1 Response' })
435+
);
436+
});
437+
438+
effectTwo = createEffect(() => {
439+
return this.actions$.pipe(
440+
ofType('Action 2'),
441+
mapTo({ type: 'Action 2 Response' })
442+
);
443+
});
444+
445+
constructor(private actions$: Actions, identifier: string = '') {
446+
this.effectIdentifier = identifier;
447+
}
448+
}
502449

503450
it('should resolve effects from instances', () => {
504451
const sources$ = cold('--a--', { a: new SourceA() });
@@ -553,7 +500,44 @@ describe('EffectSources', () => {
553500
c: new SourceWithIdentifier('b'),
554501
d: new SourceWithIdentifier2('b'),
555502
});
556-
const expected = cold('--a--b--a--b--', { a: i, b: i2 });
503+
const expected = cold('--a--b--a--b--', {
504+
a: i,
505+
b: i2,
506+
});
507+
508+
const output = toActions(sources$);
509+
510+
expect(output).toBeObservable(expected);
511+
});
512+
513+
it('should start with an action after being registered with OnInitEffects', () => {
514+
const sources$ = cold('--a--', {
515+
a: new SourceWithInitAction(new Subject()),
516+
});
517+
const expected = cold('--a--', { a: initAction });
518+
519+
const output = toActions(sources$);
520+
521+
expect(output).toBeObservable(expected);
522+
});
523+
524+
it('should not start twice for the same instance', () => {
525+
const sources$ = cold('--a--a--', {
526+
a: new SourceWithInitAction(new Subject()),
527+
});
528+
const expected = cold('--a--', { a: initAction });
529+
530+
const output = toActions(sources$);
531+
532+
expect(output).toBeObservable(expected);
533+
});
534+
535+
it('should start twice for the same instance with a different key', () => {
536+
const sources$ = cold('--a--b--', {
537+
a: new SourceWithInitAction(new Subject(), 'a'),
538+
b: new SourceWithInitAction(new Subject(), 'b'),
539+
});
540+
const expected = cold('--a--a--', { a: initAction });
557541

558542
const output = toActions(sources$);
559543

0 commit comments

Comments
 (0)