Angular2, test directive, undefined-error - unit-testing

I'm trying to test a directive, called AceDirective (I'm using the ace-editor).
So first I've build a TestComponent called MockSearchDirective which has got this directive:
#Component({
selector: '[testAce]',
directives: [AceDirective],
template: '<div ace-editor></div>',
}) class TestAce {}
class MockSearchDirective {
}
Now if got my beforeEach and beforeEachProviders with the needed Injections:
beforeEachProviders( () => [
provide(SearchDirective, {useClass: MockSearchDirective}),
TestComponentBuilder,
provide(DataTransportService, {useClass: MockDataTransportService}),
]);
beforeEach( inject( [TestComponentBuilder], (_tcb : TestComponentBuilder) => {
this.searchDirective = new MockSearchDirective();
this._dataTransportService = new MockDataTransportService();
_tcb
.createAsync(TestAce)
.then( (fixture : ComponentFixture<TestAce>)=> {
console.log(fixture);
this.fixture = fixture;
});
}));
This console.log prints the correct fixture containing the Ace-Editor. But, in the specific test:
it('Check if editor will be initiated correctly', (done) => {
console.log(this.fixture);
// let testAce = this.fixture.componentInstance;
// let element = this.fixture.nativeElement;//.querySelector('div')
// let elementRef = this.fixture.elementRef;
//editor exists
expect(this.fixture.elementRef).toBeDefined();
done();
});
It fails. The console.log says, that this.fixture is undefined.
I also tried to inject the TextComponentBuilder in the test (and not via beforeEach):
it('Check if editor will be initiated correctly', inject( [TestComponentBuilder], (_tcb : TestComponentBuilder) => {
_tcb
.createAsync(TestAce)
.then( (fixture : ComponentFixture<TestAce>)=> {
console.log(fixture);
// let testAce = this.fixture.componentInstance;
// let element = this.fixture.nativeElement;//.querySelector('div')
// let elementRef = this.fixture.elementRef;
//editor exists
expect(fixture.elementRef).toBeDefined();
});
}));
but then I've got some timeout:
zone.js:461 Unhandled Promise rejection: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out
Does anyone know this error? And how to deal with it?
Thanks!
Update
You still have to use the return-statement, like:
beforeEach( inject( [TestComponentBuilder], (_tcb : TestComponentBuilder) => {
this.searchDirective = new MockSearchDirective();
this._dataTransportService = new MockDataTransportService();
return _tcb
.createAsync(TestAce)
.then( (fixture : ComponentFixture<TestAce>)=> {
this.fixture = fixture;
});
}));

Related

Angular unit testing Error: "Keyboard" plugin is not implemented on web

app.component.ts:->
`if (Capacitor.isPluginAvailable('Keyboard')) {
this.keypadOpenListner = Keyboard.addListener('keyboardDidShow', () => {
this.zone.run(() => {
this.isKeyboardShowing = true;
})
});
this.keypadOffListner = Keyboard.addListener('keyboardDidHide', () => {
this.zone.run(() => {
this.isKeyboardShowing = false;
})
});
}`
`
app.component.spec.ts:->
` describe('app compenent', () => {
let fixture: ComponentFixture<AppComponent>;
let component: AppComponent;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [AppComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
spyOn(Capacitor, 'isNativePlatform').and.returnValue(true);
fixture.detectChanges();
});`
to cover if condition I added spyOn(Capacitor, 'isNativePlatform').and.returnValue(true); in beforeEach condition is this correct? if yes I got warning "Keyboard" plugin is not implemented on web how can I overcomethe warning ?.

Jest: How to properly test void functions that include promises?

I'm writing an app with React Native. I use Firebase Cloud Messaging for real time communication. I'm currently writing the unit tests for the FCM code using jest. The problem is that I'm struggling to make it work, since it consists of void functions that contain promises. Let me give you the code:
fcm.js:
import { Alert } from "react-native";
import firebase from "react-native-firebase";
export const checkNotificationsPermission = () => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions.
} else {
// User doesn't have permission.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePrepareForPermissionMessage,
[{ text: buttonTexts.ok, onPress: () => requestNotificationsPermission() }]
);
}
});
};
export const requestNotificationsPermission = () => {
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorised.
})
.catch(() => {
// User has rejected permissions.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePermissionDeniedMessage,
[{ text: buttonTexts.ok, onPress: () => {} }]
);
});
};
fcm.test.js:
import firebase from "react-native-firebase";
describe("checkNotificationsPermission", () => {
beforeEach(() => {
return checkNotificationsPermission();
});
afterEach(() => {
jest.clearAllMocks();
});
it("should call firebase's hasPermission", async () => {
expect(firebase.messaging().requestPermission).toHaveBeenCalledTimes(1);
});
});
Here is how I mocked firebase (__mocks__/react-native-firebase.js):
const firebase = {
messaging: jest.fn(() => ({
hasPermission: jest.fn(() => new Promise(resolve => resolve(true))),
requestPermission: jest.fn(() => new Promise(resolve => resolve(true)))
}))
};
export default firebase;
The test fails with Expected mock function to have been called one time, but it was called zero times..Since this wouldn't work and I had a similar question about promises which got answered I tried to apply what I learned there which resulted in the following code.
fcm.js:
import { Alert } from "react-native";
import firebase from "react-native-firebase";
export const checkNotificationsPermission = () =>
new Promise((resolve, reject) => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions.
resolve(true);
} else {
// User doesn't have permission.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePrepareForPermissionMessage,
[
{
text: buttonTexts.ok,
onPress: () =>
requestNotificationsPermission()
.then(() => resolve(true))
.catch(() => reject(false))
}
]
);
}
});
});
export const requestNotificationsPermission = () =>
new Promise((resolve, reject) => {
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorised.
resolve(true);
})
.catch(() => {
// User has rejected permissions.
reject(true);
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePermissionDeniedMessage,
[{ text: buttonTexts.ok, onPress: () => {} }]
);
});
});
fcm.test.js:
import firebase from "react-native-firebase";
import { requestNotifcationsPermission } from "./fcm";
describe("checkNotificationsPermission", () => {
afterEach(() => {
jest.clearAllMocks();
});
it("should call firebase's hasPermission", () => {
expect.assertions(1);
return checkNotificationsPermission().then(() => {
expect(firebase.messaging().requestPermission).toHaveBeenCalledTimes(1);
});
});
});
But for some reason these tests still fail. I empirically tested and ensured the code works. Just the unit tests won't pass.
Edit
I accidentally left out that both fcm.js also have the following imports:
import alertMessages from "../../config/constants/alertMessages";
import buttonTexts from "../../config/constants/buttonTexts";

Angular 2 testing components with observables

I am testing a angular component and the code is
ngOnInit(): void {
this.getNar();
}
getNar(): void {
let self = this;
this.dashboardService.getNar().subscribe(
res => self.narIds = res.narIds,
error => self.error = error,
function () {
self.narIds.forEach(element => {
// Some Code
});
}
);
}
The Service provider for this i.e Dashboard Service is
getNar(): Observable<any> {
return this.http.get(Config.Api.GetNar + '1/nar').map((res: Response) => res.json());
}
And my Test cases are:
let res = '"narIds":[{"id":1,"narId":"104034-1","narName":"SDLC Platform"},{"id":2,"narId":"64829-1","narName":"EMS-EMS"}]';
describe('Application Health Component', () => {
beforeEach( async(() => {
TestBed.configureTestingModule({
providers: [MockBackend, DashboardService],
imports: [ChartsModule, SlimScrollModule, HttpModule],
declarations: [CompletedFilterPipe, ApplicationHealthComponent]
})
.compileComponents()
.then(createComponent);
}));
it('should call the getNar when ngOnInit is called', async(() => {
spyOn(dashboardService, 'getNar').and.returnValue(Observable.of(res));
comp.ngOnInit();
expect(dashboardService.getNar).toHaveBeenCalled();
}));
});
function createComponent() {
fixture = TestBed.createComponent(ApplicationHealthComponent);
comp = fixture.componentInstance;
dashboardService = fixture.debugElement.injector.get(DashboardService);
};
The problem I am getting is the test case gives an error that forEach is undefined.
The error message is not that forEach function is not defined, it's that your object "self.narIds" is undefined. Fairly sure this is due to the way you declared your onComplete function in Observable.subscribe
related to this Rx Subscribe OnComplete fires but cannot use the data
change your
function () {
self.narIds.forEach(element => {
// Some Code
});
code to
() => {
self.narIds.forEach(element => {
// Some Code
});

Unit test and Assert http.get queryString call in Angular2

I have a DataService and I want to assert that the year is getting set in the query string correctly. Is there a way to spyOn the http.get call or to access it? I don't know the correct approach to testing this. I'm using Angular 2.2.0.
The DataService
constructor(private http: Http) { }
public getEnergyData(option: string): Promise<EnergyDataDto[]> {
return this.http.get(this.getEnergyDataApiUrl(option)).toPromise().then((response) => {
this.energyDataCache = this.parseEnergyDataResponse(response);
return this.energyDataCache;
}).catch(this.handleError);
}
protected getEnergyDataApiUrl(option: string) {
return `/api/solar?year=${option}`;
}
protected parseEnergyDataResponse(response: Response) {
return response.json().data;
}
dataservice.spec.ts
describe('Given the DataService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [DataService, { provide: XHRBackend, useClass: MockBackend }],
});
});
describe('When getting the energy data', () => {
let backend: MockBackend;
let service: EnergyDataService;
let fakeEnergyData: EnergyDataDto[];
let response: Response;
const makeEnergyData = () => {
let data = [];
let one = new EnergyDataDto();
one.year = 2007;
one.countryName = 'Denmark';
one.quantity = '100000';
data.push(one);
return data;
};
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new EnergyDataService(http);
fakeEnergyData = makeEnergyData();
let options = new ResponseOptions({ status: 200, body: { data: fakeEnergyData } });
response = new Response(options);
}));
it('should return fake values', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
service.getEnergyData('all').then(data => {
expect(data.length).toBe(1);
expect(data[0].countryName).toBe('Denmark');
});
})));
it('should use year in query string', async(inject([], () => {
spyOn(service, 'getEnergyDataApiUrl').and.callThrough();
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
service.getEnergyData('2007').then(data => {
// I was hoping to use backendend somehow instead, but it's not in scope when I debug it.
expect((<any>service).getEnergyDataApiUrl).toHaveBeenCalledWith('/api/solar?year=2007');
});
})));
You should do this in the mockBackend.connections subscription. This is when you have access to the URL from the MockConnection
backend.connections.subscribe((c: MockConnection) => {
expect(c.request.url).toBe(...)
c.mockRespond(response)
});

Angular 2 Observable testing Error: Cannot use setInterval from within an async zone test

I'm trying to test a component, which uses a service that makes async http calls. The service returns an Observable, which the component subscribes on.
Service code snippet:
getRecentMachineTemperatures(_machine_Id): Observable<IDeviceReadings[]> {
return this.http.get(TemperatureService.URL + _machine_Id)
.map(response => { return response.json(); })
.map((records: Array<any>) => {
let result = new Array<IDeviceReadings>();
if (records) {
records.forEach((record) => {
let device = new IDeviceReadings();
device.device_id = record.device_id;
if (record.d) {
record.d.forEach((t) => {
let temperature = new ITemperature();
temperature.timestamp = t.timestamp;
temperature.value = t.temperature;
device.temperatures.push(temperature);
});
}
result.push(device);
});
}
return result;
});
}
Component code snippet:
ngOnInit() {
this.getRecentTemperatures();
}
getRecentTemperatures() {
this.temperatureService.getRecentMachineTemperatures(this.machine_id)
.subscribe(
res => {
let device1 = res[0];
this.deviceId = device1.device_id;
this.initTemperatures(device1.temperatures);
this.updateChart();
},
error => console.log(error));
}
My Test sets up dependencies, spies on the service 'getRecentMachineTemperatures' and sets i to return some stub data. I've been googling around for ways to test this, thus resulting in 3 different test, trying to test the same thing. Each giving me a different error.
temperature.component.spec.ts:
let machine_id = 1;
let comp: TemperatureComponent;
let fixture: ComponentFixture<TemperatureComponent>;
let de: DebugElement;
let el: HTMLElement;
let temperatureService: TemperatureService;
let stubDevices: IDeviceReadings[];
let stubTemperatures: ITemperature[];
let spyRecentTemps: Function;
describe('Component: Temperature', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TemperatureComponent],
imports: [ ChartsModule ],
providers: [
MockBackend,
BaseRequestOptions,
{ provide: Http,
useFactory: (backend, defaultOptions) => {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]},
TemperatureService
]
});
stubDevices = new Array<IDeviceReadings>();
let stubDevice = new IDeviceReadings();
stubDevice.device_id = 'stub device';
stubDevice.temperatures = new Array<ITemperature>();
let stubTemp = new ITemperature();
stubTemp.timestamp = new Date().getTime();
stubTemp.value = 10;
stubDevice.temperatures.push(stubTemp);
stubDevices.push(stubDevice);
stubTemperatures = new Array<ITemperature>();
let stubTemp2 = new ITemperature();
stubTemp.timestamp = new Date().getTime() + 1;
stubTemp.value = 11;
stubTemperatures.push(stubTemp2);
fixture = TestBed.createComponent(TemperatureComponent);
comp = fixture.componentInstance;
temperatureService = fixture.debugElement.injector.get(TemperatureService);
spyRecentTemps = spyOn(temperatureService, 'getRecentMachineTemperatures')
.and.returnValue(Observable.of(stubDevices).delay(1));
// get the "temperature-component" element by CSS selector (e.g., by class name)
de = fixture.debugElement.query(By.css('.temperature-component'));
el = de.nativeElement;
});
it('should show device readings after getRecentTemperatures subscribe (fakeAsync)', fakeAsync(() => {
fixture.detectChanges();
expect(spyRecentTemps.calls.any()).toBe(true, 'getRecentTemperatures called');
tick(1000);
fixture.detectChanges();
expect(el.textContent).toContain(stubDevices[0].temperatures[0].timestamp);
expect(el.textContent).toContain(stubDevices[0].temperatures[0].value);
}));
it('should show device readings after getRecentTemperatures subscribe (async)', async(() => {
fixture.detectChanges();
expect(spyRecentTemps.calls.any()).toBe(true, 'getRecentTemperatures called');
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(el.textContent).toContain(stubDevices[0].temperatures[0].timestamp);
expect(el.textContent).toContain(stubDevices[0].temperatures[0].value);
});
}));
it('should show device readings after getRecentTemperatures subscribe (async) (done)', (done) => {
async(() => {
fixture.detectChanges();
expect(spyRecentTemps.calls.any()).toBe(true, 'getRecentTemperatures called');
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(el.textContent).toContain(stubDevices[0].temperatures[0].timestamp);
expect(el.textContent).toContain(stubDevices[0].temperatures[0].value);
}).then(done);
});
});
});
fakeAsync fails with: 'Error: 1 timer(s) still in the queue.'
async fails with: 'Error: Cannot use setInterval from within an async zone test.'
async (done) fails with: 'Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'
How would I go about testing components with a async service dependency?
From what I understand it might be something about the AsyncScheduler within the Rx library using Date().now instead of faked time (https://github.com/angular/angular/issues/10127). If so has this been fixed? Or anyone found a workaround?
I'm using angular-cli: 1.0.0-beta.16. node: 4.4.2. npm: 3.10.6. webpack 2.1.0-beta.22.
I had ..
import 'rxjs/add/operator/timeout';
return this.http[method](url, emit, this.options)
.timeout(Config.http.timeout, new Error('timeout'))
Which was causing this error. I believe under the hood RXJS .timeout is calling setInterval.
I fixed this by switching ...
it('blah', async(() => {
to
it('blah', (done) => {