fromEvent引发TypeError:角度测试中的事件目标无效



当我在测试中调用ngOnInit时,我得到以下错误:

  • 类型错误:无效的事件目标
    • 在setupSubscription(../node_modules/rxjs/internal/observable/fromEvent.js:52:1(
    • 在setupSubscription(../node_modules/rxjs/internal/observable/fromEvent.js:48:1(

我的猜测是我没有很好地模拟我的服务和类,或者我在TestBed中缺少了一些配置。我对Angular测试有点陌生,我真的很感激关于这个问题的一些建议。以下是我的代码:

粘滞登录按钮栏组件

export class StickyLoginButtonBarComponent implements OnInit {
cssClass = '';
isLoggedIn = false;
isMobileWidth = true;
showStickyButton = true;
private scrollSubscription: Subscription;
private loggedInSubscription: Subscription;
private readonly siteConfigSubscription: Subscription;
constructor(
private readonly authService: AuthService,
protected router: Router,
private readonly cd: ChangeDetectorRef,
private readonly winRef: WindowRef,
private readonly siteConfigService: SiteConfigurationDetailsService
) {}
ngOnInit() {
this.loggedInSubscription = this.authService
.getLoggedInObservable()
.subscribe(value => (this.isLoggedIn = value));
this.showStickyButton = !this.isLoggedIn;
const getScrollEvent$ = fromEvent(this.winRef.nativeWindow, 'scroll');
/* User is scrolling */
this.scrollSubscription = getScrollEvent$
.pipe(throttleTime(300))
.subscribe(() => {
if (this.cssClass === '' && this.isMobileWidth) {
this.cssClass = 'd-none';
this.updateCssClass();
}
});

/* User stopped scrolling */
this.scrollSubscription = getScrollEvent$
.pipe(
startWith(0),
debounceTime(400)
)
.subscribe(() => {
this.cssClass = '';
this.updateCssClass();
});
}
protected updateCssClass(): void {
if (!this.cd['destroyed']) {
this.cd.detectChanges();
}
}
}

粘滞登录按钮栏测试组件

describe('StickyLoginButtonBarComponent', () => {
let component: StickyLoginButtonBarComponent;
let fixture: ComponentFixture<StickyLoginButtonBarComponent>;
let windowRef: WindowRef;
@Component({
selector: 'login-button',
template: ''
})
class MockLoginButtonComponent {
@Input()
cssClass: string;
@Input()
buttonText: string;
}
const MockAuthService = {
getLoggedInObservable(): Observable<boolean> {
return of(false);
}
};
const mockConfigurationService = {
getNumberValueForKey(): Observable<number> {
return of(570);
}
};
class MockWinRef {
nativeWindow = new (class {
innerWidth = 430;
length = 100;
scroll(): any {}
});
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
providers: [
{ provide: AuthService, useValue: MockAuthService },
{
provide: SiteConfigurationDetailsService,
useValue: mockConfigurationService
},
{ provide: WindowRef, useClass: MockWinRef }
],
declarations: [
StickyLoginButtonBarComponent,
MockLoginButtonComponent
]
});
windowRef = TestBed.get(WindowRef);
fixture = TestBed.createComponent(StickyLoginButtonBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should call updateCssClass when user is scrolling', () => {
const input = fixture.debugElement.query(By.css('.login-bar'));
input.triggerEventHandler('scroll', windowRef.nativeWindow);
component.ngOnInit();
spyOn(windowRef.nativeWindow, 'scroll');
expect(windowRef.nativeWindow.scroll).toHaveBeenCalled();
});
});

如果有人也遇到过,我已经设法解决了我的问题
第一个问题是我没有很好地模拟WindowRef类,这导致了竞争条件错误
第二个问题是关于我试图在窗口上触发"滚动"事件的方式。以下是我对最初问题的解决方案:

describe('StickyLoginButtonBarComponent', () => {
let component: StickyLoginButtonBarComponent;
let fixture: ComponentFixture<StickyLoginButtonBarComponent>;
let windowRef: WindowRef;
let spyOnInit: jasmine.Spy;
let spyOnCdRef: jasmine.Spy;
@Component({
selector: 'login-button',
template: ''
})
class MockLoginButtonComponent {
@Input()
cssClass: string;
@Input()
buttonText: string;
}
const MockAuthService = {
getLoggedInObservable(): Observable<boolean> {
return of(false);
}
};
const mockConfigurationService = {
getNumberValueForKey(): Observable<number> {
return of(570);
}
};
class MockWinRef {
get nativeWindow(): any {
return window;
}
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
providers: [
{ provide: AuthService, useValue: MockAuthService },
{
provide: SiteConfigurationDetailsService,
useValue: mockConfigurationService
},
{ provide: WindowRef, useClass: MockWinRef }
],
declarations: [
StickyLoginButtonBarComponent,
MockLoginButtonComponent
]
});
windowRef = TestBed.get(WindowRef);
fixture = TestBed.createComponent(StickyLoginButtonBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should call updateCssClass when user is scrolling', () => {
spyOnInit = spyOn(component, 'ngOnInit');
spyOnCdRef = spyOn((component as any).cd, 'detectChanges');
component.cssClass = '';
component.isMobileWidth = true;
component.ngOnInit();
const e = document.createEvent('Event');
e.initEvent('scroll', true, true);
windowRef.nativeWindow.dispatchEvent(e);
expect(spyOnInit).toHaveBeenCalled();
expect(component.cssClass).toEqual('d-none');
expect(spyOnCdRef).toHaveBeenCalled();
});
});

任何有此问题的人都请尝试添加DeviceService

首先创建一个设备模拟文件

export const DeviceCommonServiceMock: any = {
browser: () => true,
isMobile: () => true,
isTablet: () => true,
isDesktop: () => true,
isDesktopSmall: () => true,
isDesktopMedium: () => true,
isDesktopLarge: () => true,
isMobileOrTablet: () => true,
getDeviceInfo: () => {
return {
browser: 'chrome'
};
},
getScreenWidth: () => 100,
getScreenHeight: () => 100,
setDisplayMode: () => {
},
is2D: () => true,
is3D: () => true,
scrollIntoView: () => true,
};

然后在TestBed.configureTestingModule提供商中添加

beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
providers: [
{ provide: AuthService, useValue: MockAuthService },
{
provide: SiteConfigurationDetailsService,
useValue: mockConfigurationService
},
{ provide: DeviceCommonService, useValue: DeviceCommonServiceMock },
{ provide: WindowRef, useClass: MockWinRef }
],
declarations: [
StickyLoginButtonBarComponent,
MockLoginButtonComponent
]
});
.....

最新更新