Skip to content

Commit fa72532

Browse files
committed
fix(state): handle error emission in connect using ErrorHandler
1 parent 246f7e6 commit fa72532

File tree

3 files changed

+72
-23
lines changed

3 files changed

+72
-23
lines changed

libs/state/selections/src/lib/accumulation-observable.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function createAccumulationObservable<T extends object>({
2929
stateObservables = new Subject<Observable<Partial<T>>>(),
3030
stateSlices = new Subject<Partial<T>>(),
3131
accumulatorObservable = new BehaviorSubject(defaultAccumulator),
32-
handleError = (e: any) => console.error(e),
32+
handleError = (e: unknown) => console.error(e),
3333
} = {}): Accumulator<T> {
3434
const signal$ = merge(
3535
stateObservables.pipe(
@@ -49,7 +49,7 @@ export function createAccumulationObservable<T extends object>({
4949
(error) => handleError(error)
5050
),
5151
// @Notice We catch the error here as it get lost in between `publish` and `publishReplay`. We return empty to
52-
catchError((e) => EMPTY),
52+
catchError(() => EMPTY),
5353
publish()
5454
);
5555
const state$: Observable<T> = signal$.pipe(publishReplay(1));

libs/state/spec/rx-state.component.spec.ts

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import {
33
ErrorHandler,
44
Input,
55
Output,
6+
Type,
67
ViewChild,
78
} from '@angular/core';
8-
import { ComponentFixture, TestBed } from '@angular/core/testing';
9+
import { TestBed } from '@angular/core/testing';
910
import { RxState } from '@rx-angular/state';
1011
import { select } from '@rx-angular/state/selections';
1112
import { PrimitiveState } from '@test-helpers';
@@ -105,21 +106,17 @@ export class RxStateGlueContainerComponent extends RxState<PrimitiveState> {
105106

106107
describe('LocalProviderTestComponent', () => {
107108
let component: RxStateInjectionComponent;
108-
let fixture: ComponentFixture<RxStateInjectionComponent>;
109-
let errorHandlerSpy: any;
109+
let errorHandlerSpy: jest.Mock;
110110

111111
beforeEach(() => {
112-
const testBed = TestBed.configureTestingModule({
113-
declarations: [RxStateInjectionComponent],
112+
const { component: c, errorHandler: e } = setupTestComponent({
113+
testComponent: RxStateInjectionComponent,
114114
providers: [
115115
{ provide: ErrorHandler, useValue: { handleError: jest.fn() } },
116116
],
117-
teardown: { destroyAfterEach: true },
118117
});
119-
fixture = TestBed.createComponent(RxStateInjectionComponent);
120-
component = fixture.componentInstance;
121-
errorHandlerSpy = testBed.inject(ErrorHandler).handleError;
122-
fixture.detectChanges();
118+
component = c;
119+
errorHandlerSpy = e.handleError as jest.Mock;
123120
});
124121

125122
it('should create', () => {
@@ -137,21 +134,17 @@ describe('LocalProviderTestComponent', () => {
137134

138135
describe('InheritanceTestComponent', () => {
139136
let component: RxStateInheritanceComponent;
140-
let fixture: ComponentFixture<RxStateInheritanceComponent>;
141-
let errorHandlerSpy: any;
137+
let errorHandlerSpy: jest.Mock;
142138

143139
beforeEach(() => {
144-
const testBed = TestBed.configureTestingModule({
145-
declarations: [RxStateInheritanceComponent],
146-
teardown: { destroyAfterEach: true },
140+
const { component: c, errorHandler: e } = setupTestComponent({
141+
testComponent: RxStateInheritanceComponent,
147142
providers: [
148143
{ provide: ErrorHandler, useValue: { handleError: jest.fn() } },
149144
],
150145
});
151-
fixture = TestBed.createComponent(RxStateInheritanceComponent);
152-
component = fixture.componentInstance;
153-
errorHandlerSpy = testBed.inject(ErrorHandler).handleError;
154-
fixture.detectChanges();
146+
component = c;
147+
errorHandlerSpy = e.handleError as jest.Mock;
155148
});
156149

157150
it('should create', () => {
@@ -168,3 +161,59 @@ describe('InheritanceTestComponent', () => {
168161
});
169162
});
170163
});
164+
165+
describe('CustomErrorHandler', () => {
166+
let component: RxStateInheritanceComponent;
167+
let errorHandlerSpy: jest.SpyInstance;
168+
169+
class CustomErrorHandler implements ErrorHandler {
170+
private prod = true;
171+
handleError() {
172+
if (this.prod) {
173+
throw new Error('Prod error');
174+
}
175+
throw new Error('Dev error');
176+
}
177+
}
178+
179+
beforeEach(() => {
180+
const { component: c, errorHandler: e } = setupTestComponent({
181+
testComponent: RxStateInheritanceComponent,
182+
providers: [
183+
{
184+
provide: ErrorHandler,
185+
useClass: CustomErrorHandler,
186+
},
187+
],
188+
});
189+
component = c;
190+
errorHandlerSpy = jest.spyOn(e, 'handleError');
191+
});
192+
193+
describe('state.connect', () => {
194+
it('should handle error through CustomErrorHandler', () => {
195+
const error$ = throwError(() => new Error('whoops'));
196+
component.connect('str', error$);
197+
expect(errorHandlerSpy).toThrow(new Error('Prod error'));
198+
});
199+
});
200+
});
201+
202+
function setupTestComponent({
203+
testComponent,
204+
providers,
205+
}: {
206+
testComponent: Type<any>;
207+
providers: any[];
208+
}) {
209+
const testBed = TestBed.configureTestingModule({
210+
declarations: [testComponent],
211+
providers: [...providers],
212+
teardown: { destroyAfterEach: true },
213+
});
214+
const fixture = TestBed.createComponent(testComponent);
215+
const component = fixture.componentInstance;
216+
const errorHandler = testBed.inject(ErrorHandler);
217+
218+
return { fixture, component, errorHandler };
219+
}

libs/state/src/lib/rx-state.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ export type ProjectValueReducer<T, K extends keyof T, V> = (
5353
@Injectable()
5454
export class RxState<T extends object> implements OnDestroy, Subscribable<T> {
5555
private subscription = new Subscription();
56-
private handleError = inject(ErrorHandler).handleError;
56+
private errorHandler = inject(ErrorHandler);
5757
private accumulator = createAccumulationObservable<T>({
58-
handleError: this.handleError,
58+
handleError: this.errorHandler.handleError.bind(this.errorHandler),
5959
});
6060
private effectObservable = createSideEffectObservable();
6161

0 commit comments

Comments
 (0)