jest unit test to mock events - unit-testing

I have a utility file that determines the enter key-down and triggers the click event of all the button type submit present in the document.
I need help to simulate key-down of enter key, and mock buttons with type submit so as to trigger their click.
I am using jest and enzyme for testing .
Below is the utility code :
const runSubmitEventListener = () => {
document.onkeydown = (e) => {
e = e || window.event;
if(e.key && e.key.toLowerCase() === 'enter'){
document.querySelector('button[type="submit"]') && document.querySelectorAll('button[type="submit"]').click();
}
}
}
export default{
runSubmitEventListener
}
Also, have started writing the test cases for which below is the file. Please suggest further steps, as I am new to jest unit testing.
import events from './events';
describe('events utility', () => {
it('should have runSubmitEventListener to be defined', () => {
expect(events.runSubmitEventListener).toBeDefined();
});
it('should trigger click of button type submit on keydown enter key', () => {
const event = new keyBoardEvent('keydown', {'key': 'enter', 'keycode': 13});
document.dispatchEvent(event);
// expect(events.runSubmitEventListener).toHaveBeenCalled();
});
});

Related

How to test react component correctly?

Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.

How to unit test the checkbox in Angular2

I have a sample code for checkbox written with Angular2.
<div class="col-sm-7 align-left" *ngIf="Some-Condtion">
    <input type="checkbox" id="mob_Q1" value="Q1" />
    <label for="mob_Q1">Express</label>
</div>
I want to unit test the above checkbox. Like I want to recognize the checkbox and test whether it is check-able. How do I unit test this with Karma Jasmine?
Component, e.g. CheckboxComponent, contains input element. Unit test should looks like:
import {ComponentFixture, TestBed} from '#angular/core/testing';
import {By} from '#angular/platform-browser';
import {CheckboxComponent} from './checkbox.component';
describe('Checkbox test.', () => {
let comp: CheckboxComponent;
let fixture: ComponentFixture<CheckboxComponent>;
let input: Element;
beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [CheckboxComponent],
},
);
fixture = TestBed.createComponent(CheckboxComponent);
comp = fixture.componentInstance;
input = fixture.debugElement.query(By.css('#mob_Q1')).nativeElement;
});
it('should click change value', () => {
expect(input.checked).toBeFalsy(); // default state
input.click();
fixture.detectChanges();
expect(input.checked).toBeTruthy(); // state after click
});
});
IS there a need to write fixture.detectChanges()?
I went through the same test without this and it ends with success.
Button 1 is 'checked' by default
const button1 = debugElement.nativeElement.querySelector(selectorBtn1);
const button2 = debugElement.nativeElement.querySelector(selectorBtn2);
...
expect(button1.checked).toBeTruthy();
expect(button2.checked).toBeFalsy();
button2.click();
expect(button1.checked).toBeFalsy();
expect(button2.checked).toBeTruthy();
...
ngModel directive is async one and requires to use asynchronous capabilities of Angular unit testing. Adding async and whenStable functions.
it('checkbox is checked if value is true', async(() => {
component.model = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
const inEl = fixture.debugElement.query(By.css('#mob_Q1'));
expect(inEl.nativeElement.checked).toBe(true);
});
}));
Source LinkLink

Unable to simulate keypress event in Angular 2 unit test (Jasmine)

I am using a directive to get the data from input used as a filter text.
here is my hostlistener in the directive:
#HostListener('input', ['$event.target.value'])
public onChangeFilter(event: any): void {
console.log('input event fired, value: ' + event);
this.columnFiltering.filterString = event;
this.filterChanged.emit({filtering: this.columnFiltering});
}
this code is working perfectly, I am unable to unit test the same.
I have subscribed to the filterChanged EventEmitter, in my unit test to check the value.
I tried simulating keypress event to change value and also tried settings value attribute. None of these is working for me.
here is my spec file:
describe('Table View', () => {
let fixture: ComponentFixture<any>;
let context: TableComponent;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
TableComponent,
],
imports: [TableModule],
});
fixture = TestBed.createComponent(TableComponent);
context = fixture.componentInstance;
});
it('should allow filter', () => {
const element = fixture.nativeElement;
context.config = config;
fixture.detectChanges();
let tableChangeCount = 0;
let tableEvent: any;
context.tableChanged.subscribe((event: any) => {
tableChangeCount++;
tableEvent = event;
});
// Check if table exists
let inputElement = element.querySelectorAll('tr')[1].querySelector('input');
let e = new KeyboardEvent("keypress", {
key: "a",
bubbles: true,
cancelable: true,
});
inputElement.dispatchEvent(e);
});
});
I tried setting value:
let attrs = inputElement.attributes;
inputElement.setAttribute('value', 'abc');
for (let i = attrs.length - 1; i >= 0; i--) {
// Attribute value is set correctly
if (attrs[i].name === 'value') {
console.log(attrs[i].name + "->" + attrs[i].value);
}
}
Can anyone please help me, how can I unit test the same?
I've had some trouble simulating a keypress in a unit test also. But came across an answer by Seyed Jalal Hosseini. It might be what you're after.
If you're attempting to simulate a keypress you can trigger an event by calling dispatchEvent(new Event('keypress')); on the element.
Here is the answer I'm referring to which gives more detail : https://stackoverflow.com/a/37956877/4081730
If you want to set the key that was pressed, this can be done also.
const event = new KeyboardEvent("keypress",{
"key": "Enter"
});
el.dispatchEvent(event);
Further information I've just come across: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
If you wish to use a key code (or "which"), you can do this:
// #HostListener('document:keypress')
const escapeEvent: any = document.createEvent('CustomEvent');
escapeEvent.which = 27;
escapeEvent.initEvent('keypress', true, true);
document.dispatchEvent(escapeEvent);
it('should trigger a TAB keypress event on an element', () => {
const tabKeypress = new KeyboardEvent('keypress', {
// #ts-ignore
keyCode: 9, // Tab Key
cancelable: true
});
const myTableEle = debugEle.nativeElement.querySelector('.your-element');
myTableEle.dispatchEvent(tabKeypress);
fixture.detectChanges();
});
// #ts-ignore :- is to remove TS warning because keyCode is deprecated. Its not needed in case you want to set "key" property of KeyboardEvent.

Testing observable object angular 2 karma

I'm working on my unit test cases for Angular 2 with Karma, I got stuck with one of a function where I run the test for below line
expect(component.subscribeToEvents()).toBeTruthy();
and I view my coverage code, the lines inside the test file seems not covering anything inside the subscribe. I have tried using MockBackend in mocking the api call inside a function on service but I'm not sure how to do the mocking on a subscribed object, can somebody please help me?
The below is in test.component.ts
subscribeToEvents() {
this.subscription = this.czData.$selectedColorZone
.subscribe(items => {
this.resourceLoading = true;
if (!this.resourceData || (this.resourceData && this.resourceData.length === 0)) {
this.settings.layout.flypanel.display = false;
this.getAllResources(this.pagination.start, this.pagination.size);
}
else {
this.pagination.start = 1;
this.pagination.end = this.pagination.size;
this.getAllResources(1, this.pagination.size);
this.settings.layout.flypanel.display = true;
}
});
return true;
}
The screenshot of the coverage code
You can't do this, as the subscription is resolved asynchronously. So the synchronous test completes before the async task is resolved.
If all you want is coverage, you can just make the test async. This will cause the Angular test zone to wait until the async task is resolved, before completing the test
import { async } from '#angular/core/testing';
it('..', async(() => {
component.subscribeToEvents();
}))
You can't try to expect anything here, as there is no callback hook for when the task is resolved. So this is really a pointless test. It will give you coverage, but you aren't actually testing anything. For instance, you might want to test that the variables are set when the subscription is resolved.
Based on the code provided, what I would do instead is just mock the service, and make it synchronous. How can you do that? We you can make the mock something like
class CzDataSub {
items: any = [];
$selectedColorZone = {
subscribe: (callback: Function) => {
callback(this.items);
}
}
}
Then just configure it in the test
let czData: CzDataStub;
beforeEach(() => {
czData = new CzDataStub();
TestBed.configureTestingModule({
providers: [
{ provide: CzData, useValue: czData }
]
})
})
Now in your tests, you don't need to make it async, and you can provide any value you want by just setting the items property on the mock, and subscriber will get it
it('..', () => {
czData.items = something;
component.subscribeToEvents();
expect(component.settings.layout.flypanel.display).toBe(false);
})
UPDATE
I think I was half asleep when I wrote this post. One of the above statements is incorrect
You can't try to expect anything here, as there is no callback hook for when the task is resolved.
This is not completely true. This is what fixture.whenStable() is for. For instance if this is your service
class CzData {
_value = new Subject<>();
$selectedColorZone = this._value.asObservable();
setValue(value) {
this._value.next(value);
}
}
Then this is how you would make the test work
let czData: CzData;
let fixture: ComponentFixture<YourComponent>;
let component: YourComponent;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ CzData ],
declarations: [ YourComponent ]
});
fixture = TestBed.createComponent(YourComponent);
component = fixture.componentInstance;
czData = TestBed.get(czData);
})
it('..', async(() => {
component.subscribeToEvents();
czData.setValue(somevalue);
fixture.whenStable().then(() => {
expect(component.settings.layout.flypanel.display).toBe(false);
})
}))
We use fixture.whenStable() to to wait for the async tasks to complete.
This is not to say that using the mock is wrong. A lot of the time, using the mock would be the way to go. I just wanted to correct my statement, and show how it could be done.
Consider how Angular Outputs are tested since they are subscribed to during testing: https://angular.io/guide/testing#clicking
it('should raise selected event when clicked (triggerEventHandler)', () => {
let selected: Hero;
comp.selected.subscribe((hero: Hero) => selectedHero = hero);
heroDe.triggerEventHandler('click', null);
expect(selectedHero).toBe(expectedHero);
});
So try:
const expectedItem = {}; // mock the expected result from 'subscribeToEvents'
it('should raise selected event when clicked (triggerEventHandler)', () => {
let selectedItem: any; // change to expected type
component.subscribeToEvents.subscribe((item: any) => selectedItem = item);
// fixture.detectChanges(); // trigger change detection if necessary here, depending on what triggers 'subscribeToEvents'
expect(selectedItem).toBe(expectedItem);
});

Wait for external event in acceptance tests

In an Ember 2.3 app, I'm using stripe and I have this acceptance test :
it('can visit /user-subscription', () => {
visit('/');
andThen(() => {
click('#pay');
andThen(() => {
fillIn('#card_number', '4242424242424242');
fillIn('#cc-exp', '1299');
fillIn('#cc-csc', '444');
click('#submitButton');
andThen(() => {
done();
expect(currentPath()).to.equal('subscriptions.success');
});
});
});
});
When I click on pay button, it show the Stripe box with this code :
var checkout = StripeCheckout.configure({
key: "...",
locale: 'fr'
}).open({
email: owner.get('email'),
amount: price,
token: (result) => {
# ...
}
});
It loads and execute and external script. The test is failing before the box is displayed. The external script is not loaded completely when the test fail.
I'm using EmberCliMirage with this.passthrough('https://checkout.stripe.com/**');.
What can I do to make the test pass?
You can pause the test until you receive the event. https://api.qunitjs.com/async/
let eventReceived = assert.async();
// ... callback for that async event ...
function eventComplete() {
eventReceived();
// ... do some more assertions
}
// supply the callback to something that knows about the external event