I am working on an Angular App and need to test a Component with Jest; the app is communicating bi-directional with a Windows Desktop App. I´ve implemented a Service that listens to events from the Desktop app and that all works fine. My problem is the unit test. I know I need to mock and inject the global window object somehow, because in this way, Angular can receive events from Edge and again, that works fine; yet, my unit test is throwing the following error:
: Property 'newWorkItem' does not exist on type 'Window & typeof globalThis'.
expect(window.newWorkItem).toBeTruthy;
This is my .spec:
/* eslint-disable @typescript-eslint/typedef */
/* eslint-disable jasmine/expect-matcher */
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { IwdPluginCompComponent } from './iwd-plugin-comp.component';
import { WebViewService } from '../core/services/web-view.service';
import { AppModule } from '../app.module';
describe('AppComponent', () => {
let fixture: ComponentFixture<IwdPluginCompComponent>;
beforeEach(async () => {
const newWorkItem = jest.fn();
const myMock1 = jest.fn();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const a = new myMock1();
console.log(myMock1.mock.instances);
jest.mock('WebViewService');
Object.defineProperty(window, 'newWorkItem', newWorkItem);
await TestBed.configureTestingModule({
imports: [RouterTestingModule, AppModule, WebViewService],
providers: [{ provide: 'Window', useFactory: () => newWorkItem },
],
declarations: [IwdPluginCompComponent],
}).compileComponents();
fixture = TestBed.createComponent(IwdPluginCompComponent);
fixture.detectChanges();
// eslint-disable-next-line jasmine/prefer-toHaveBeenCalledWith
});
it('should have a defined component', () => {
expect(fixture).toBeTruthy;
fixture.detectChanges;
expect(window.newWorkItem).toBeTruthy;
});
});
This is the component I want to test:
import { ChangeDetectorRef, Component, Inject, Input, OnChanges, OnInit } from '@angular/core';
import { WebViewService } from '../core/services/web-view.service';
export interface IUserData {
FirstName: String;
LastName: String;
InteractionID: String;
}
@Component({
selector: 'iwd-iwd-plugin-comp',
templateUrl: './iwd-plugin-comp.component.html',
styleUrls: ['./iwd-plugin-comp.component.scss'],
providers: [{ provide: Window, useValue: window }],
})
export class IwdPluginCompComponent implements OnInit, OnChanges {
public userdata: IUserData = {} as IUserData;
@Input() public interactionID: unknown = '';
public constructor(
public readonly webviewservice: WebViewService,
@Inject(Window) public window: Window,
private readonly ref: ChangeDetectorRef
) {
window.newWorkItem = this.newWorkItem;
}
public ngOnInit(): void {
this.ref.detectChanges();
}
public ngOnChanges(): void {}
public newWorkItem(interactionID: String, userData: IUserData): void {
console.log('NewWorkItem call', interactionID, userData);
// eslint-disable-next-line @typescript-eslint/typedef
const event = new CustomEvent('wde.newWorkItem', {
detail: {
FirstName: userData.FirstName,
LastName: userData.LastName,
InteractionID: interactionID,
},
bubbles: true,
cancelable: true,
composed: false,
});
console.log('NewWorkItem event', event);
window.dispatchEvent(event);
}
}
This is the service I am injecting into the component:
/* eslint-disable @typescript-eslint/member-ordering */
/* eslint-disable @typescript-eslint/typedef */
import { Injectable } from '@angular/core';
import { connectable, fromEvent, map, Observable, shareReplay } from 'rxjs';
import { IUserData } from '../../iwd-plugin-comp/iwd-plugin-comp.component';
@Injectable({
providedIn: 'root',
})
export class WebViewService {
private readonly _webViewMessage$ = connectable(
fromEvent<CustomEvent<IUserData>>(window, 'wde.newWorkItem').pipe(map(ev => ev.detail))
);
public webViewMessage$: Observable<IUserData> = this._webViewMessage$.pipe(
shareReplay({ refCount: true, bufferSize: 1 })
);
constructor() {
this._webViewMessage$.connect();
}
}
// add chrome to the Window context so typescript stops complaining
declare global {
interface Window {
chrome: unknown;
}
}
This is the global window declaration that I want to test against, which works fine for the application:
import { IUserData } from '../app/iwd-plugin-comp/iwd-plugin-comp.component';
export {};
declare global {
interface Window {
newWorkItem(interactionID: String, userdata: IUserData): unknown;
}
}
As I am really stuck here, I am thankful for any hints or help. I just basically need to get this test running and not test all the details for the component in the first place; this will happen at a later point. Thanks in advance!
CodePudding user response:
I am thinking you need a handle on the Window
that is provided and not the one that you have quick access to.
Try this:
it('should have a defined component', () => {
expect(fixture).toBeTruthy;
fixture.detectChanges;
const providedWindow = TestBed.inject('Window');
expect(providedWindow.newWorkItem).toBeTruthy;
})
You should get the window that's provided into the component and not the window that's available to be used as we know it.