Skip to content

Commit 37b921d

Browse files
cexbrayatdevversion
authored andcommitted
refactor(core): test EventEmitter completion on destroy with outputToObservable (#58239)
A unit test has been added to check that `EventEmitter` properly completes upon component/directive destrouction when used with `outputToObservable`. It explains why `destroyRef` has to be injected in `EventEmitter` in the first place. PR Close #58239
1 parent d330f94 commit 37b921d

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

packages/core/rxjs-interop/test/output_to_observable_spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,30 @@ describe('outputToObservable()', () => {
4949
expect(subscription.closed).toBe(true);
5050
});
5151

52+
it('should complete EventEmitter upon directive destroy', () => {
53+
const eventEmitter = TestBed.runInInjectionContext(() => new EventEmitter<number>());
54+
const observable = outputToObservable(eventEmitter);
55+
56+
let completed = false;
57+
const subscription = observable.subscribe({
58+
complete: () => (completed = true),
59+
});
60+
61+
eventEmitter.next(1);
62+
eventEmitter.next(2);
63+
64+
expect(completed).toBe(false);
65+
expect(subscription.closed).toBe(false);
66+
expect(eventEmitter.observed).toBe(true);
67+
68+
// destroy `EnvironmentInjector`.
69+
TestBed.resetTestingModule();
70+
71+
expect(completed).toBe(true);
72+
expect(subscription.closed).toBe(true);
73+
expect(eventEmitter.observed).toBe(false);
74+
});
75+
5276
describe('with `outputFromObservable()` as source', () => {
5377
it('should allow subscription', () => {
5478
const subject = new Subject<number>();

packages/core/src/event_emitter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ class EventEmitter_ extends Subject<any> implements OutputRef<any> {
121121
// Attempt to retrieve a `DestroyRef` and `PendingTasks` optionally.
122122
// For backwards compatibility reasons, this cannot be required.
123123
if (isInInjectionContext()) {
124+
// `DestroyRef` is optional because it is not available in all contexts.
125+
// But it is useful to properly complete the `EventEmitter` if used with `outputToObservable`
126+
// when the component/directive is destroyed. (See `outputToObservable` for more details.)
124127
this.destroyRef = inject(DestroyRef, {optional: true}) ?? undefined;
125128
this.pendingTasks = inject(PendingTasks, {optional: true}) ?? undefined;
126129
}

0 commit comments

Comments
 (0)