I am trying to write a unit test, for a custom library.
This custom library have a drag function.
The first issues is that the width of my screen.element seems to be equal to 0, and I don't understand why.
Here is the test:
test("Drag Test", async () => {
const user = userEvent.setup();
render(<TestDrag />);
const draggable = screen.getByRole("thumb");
console.log(draggable.getBoundingClientRect());
expect(draggable.getBoundingClientRect().x).toBeGreaterThan(0);
await screen.findByDisplayValue(20);
user.pointer({
target: draggable,
coords: { offsetX: 20 },
keys: "[MouseLeft]",
});
user.pointer({ target: draggable, keys: "[/MouseLeft]" });
await screen.findByDisplayValue(40);
});
Related
I am having a tough time writing a test for one feature. I am using context and useReducer.
The test is for a form. I need to change the field and then validate that the button is enabled. My problem is that on dispatch the state does not update. I'm thinking it is because I have to mock the implementation somehow for that action but not sure how to achieve this. Can someone please guide me in the right direction? Below is my whole test:
Below is my whole test
describe('Form Container', () => {
const mockUserState = {
user: {
firstName: '',
lastName: ''
}
}
const userContext = [
mockUserState,
{
userDispatch: jest.fn()
}
]
it('button in enabled on input value change', () => {
const wrapper = mount(
<UserProvider state={userContext}>
<FormContainer />
</UserProvider>
)
const submitBtn = wrapper.find('button[name="submitBtn"]');
expect(submitBtn.props()['disabled']).toBe(true);
const firstNameInput = wrapper.find('input[name="firstName"]');
firstNameInput.simulate('change', { target: { value: 'Test First Name'}});
expect(submitBtn.props()['disabled']).toBe(false);
});
})
After the dispatch I expect the button to be enabled but the following keeps failing.
expect(submitBtn.props()['disabled']).toBe(false);
Hello I have checked the behaviour in application and it works with same data from api as I'm providing in mocked api call Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data })); However, the value of hasWatermark computed is false - while it should be true.
How can I debug this? Is it possible to check computed in tests? This is my test:
function createWrapper() {
const i18n = new VueI18n({
locale: "en",
missing: jest.fn(),
});
return mount(EmployeeContract, {
i18n,
localVue,
store,
mocks: { $route: { query: {}, params: { id: "123" } }, $buefy: { toast: { open: jest.fn() } } },
stubs: ["spinner", "router-link", "b-switch"],
});
it("should add watermark for preview once it has rejected status", async () => {
const data = singleContract;
Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data }));
const wrapper = createWrapper();
await flushPromises();
expect(wrapper.vm.hasWatermark).toBeTruthy();
});
I am trying to test AsyncTypeahead from react-bootstrap-typeahead.
I have a very simple test component :
class AsyncTypeahead2 extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isLoading: false,
};
}
render() {
return ( <AsyncTypeahead
isLoading={this.state.isLoading}
onSearch={query => {
this.setState({isLoading: true});
fetch("http://www.myHTTPenpoint.com")
.then(resp => resp.json())
.then(json => this.setState({
isLoading: false,
options: json.items,
}));
}}
options={this.state.options}
labelKey={option => `${option.stateName}`}
/> )
}
}
const url = "http://www.myHTTPenpoint.com"
fetchMock
.reset()
.get(
url,
{
items: [
{id:1, stateName:"Alaska"},
{id:2, stateName:"Alabama"}
]
},
);
(Note that the URL is mocked to return two elements)
When I run this in my storybook it looks fine :
But if I want to test it (with Enzyme) it does not recognise the < li > items that pop up.
let Compoment =
<div>Basic AsyncTypeahead Example
<AsyncTypeahead2/>
</div>
const wrapper = mount(Compoment);
let json = wrapper.html();
let sel = wrapper.find(".rbt-input-main").at(0)
sel.simulate('click');
sel.simulate('change', { target: { value: "al" } });
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
expect(wrapper.find(".dropdown-item").length).toBe(2) //but get just 1 element "Type to Search..."
Instead of finding two "dropdown-item" items there is just one item with the text "Type to Search...".
Is the AynchTypeahead not updating the DOM correctly with respect to Enzyme?
<AsyncTypeahead> is asynchronous. On the other hand simulate() is synchronous. So at the time you get to expect() AsyncTypeahead not even started to populate the dropdown with <li> elements. You need to wait for it.
It's not specified, but it looks like you are using fetch-mock package.
There is the flush function which
Returns a Promise that resolves once all fetches handled by fetch-mock have resolved
So this:
...
sel.simulate('click');
sel.simulate('change', { target: { value: "al" } });
await fetchMock.flush() // !!!
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
expect(wrapper.find(".dropdown-item").length).toBe(2)
should work.
...But probably it won't. Because
fetchMock.mock(...)
fetch(...)
await fetchMock.flush()
does work, but
fetchMock.mock(...)
setTimeout(() => fetch(...), 0)
await fetchMock.flush()
does not. await fetchMock.flush() returns right away if there was no call of fetch. And probably there won't be. Because <AsyncTypeahead> debounces.
(By the way, you can also try to mock fetch on a per-test basis. Just in case.)
So I see two options:
Use something else instead of fetch-mock package. Where you can resolve your own Promises on mocked requests completion.
https://tech.travelaudience.com/how-to-test-asynchronous-data-fetching-on-a-react-component-ff2ee7433d71
import waitUntil from 'async-wait-until';
...
test("test name", async () => {
let Compoment = <AsyncTypeahead2/>
...
await waitUntil(() => wrapper.state().isLoading === false);
// or even
// await waitUntil(() => wrapper.find(".dropdown-item").length === 2, timeout);
expect(...)
})
This options if not pretty. But maybe it's your only option - there is not only the fetch-mock you should worry about. setState also asynchronous... and it looks like there is no pretty way to check when it's done updating the state and the DOM without changing the real code (which is quite undesirable).
The exact solution to my problem is in the following code (copy and paste into a JS file to see it work).
Things to note :
I needed to use the waitUntil function from the async-wait-until library. fetch-mock on its own does not provide the functionality to test async code.
I needed to add an ugly hack at global.document.createRange because of some tooltip issue with react-bootstrap-typeahead and jest.
use waitUntil to wait on changes on the internal state of the component
It is very important to call wrapper.update() to update the DOM afterwards.
..
import React, {Component} from 'react';
import waitUntil from 'async-wait-until';
import {mount} from "enzyme";
import fetchMock from "fetch-mock";
import {AsyncTypeahead} from "react-bootstrap-typeahead";
describe('Autocomplete Tests ', () => {
test(' Asynch AutocompleteInput ', async () => {
class AsyncTypeaheadExample extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isLoading: false,
finished: false
};
}
render() {
return (<AsyncTypeahead
isLoading={this.state.isLoading}
onSearch={query => {
this.setState({isLoading: true});
fetch("http://www.myHTTPenpoint.com")
.then(resp => resp.json())
.then(json => this.setState({
isLoading: false,
options: json.items,
finished: true
}));
}}
options={this.state.options}
labelKey={option => `${option.stateName}`}
/>)
}
}
const url = "http://www.myHTTPenpoint.com"
fetchMock
.reset()
.get(
url,
{
items: [
{id: 1, stateName: "Alaska"},
{id: 2, stateName: "Alabama"}
]
},
);
let Compoment =
<AsyncTypeaheadExample/>
// ugly hacky patch to fix some tooltip bug
// https://github.com/mui-org/material-ui/issues/15726
global.document.createRange = () => ({
setStart: () => {
},
setEnd: () => {
},
commonAncestorContainer: {
nodeName: 'BODY',
ownerDocument: document,
},
});
let wrapper = mount(Compoment);
let sel = wrapper.find(".rbt-input-main").at(0)
sel.simulate('click');
sel.simulate('change', {target: {value: "al"}});
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
//now the async stuff is happening ...
await waitUntil(() => {
return wrapper.state().finished === true;
}, 3000); //wait about 3 seconds
wrapper.update() //need to update the DOM!
expect(wrapper.find(".dropdown-item").length).toBe(2) //but get just 1 element "Type to Search..."
})
});
UPDATE
I can also compare on wrapper items rather than doing a direct comparison on the state :
//now the async stuff is happening ...
await waitUntil(() => {
wrapper.update() //need to update the DOM!
return wrapper.find(".dropdown-item").length > 1
}, 3000); //wait about 3 seconds
This is probably better because it means i dont need to know about the component internals.
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.
I am trying to write test cases of react component in Mocha + Enzyme. I have used Sinon to spy document elements.
In a react component method i have below statement, where trying to add a value for Select element on Button click event.
handleClick(e){
e.preventDefault();
if(e.target.value!='select')
this.setState({
selectedValue: e.target.value
})
else
document.getElementsByName("foodSelector")[0].value="select";
}
So i tried mocking this method as below using Mocha,
describe('(Container) ReactContainer', () => {
beforeEach(function(){
wrapper = shallow(<SelectContainer/>);
})
it('should trigger change event when policy dropdown is clicked', function(){
const selectElt = wrapper.find('#foodSelector');
selectElt.simulate('change', { preventDefault() {}, target: { value: 'TestingValue' } });
selectElt.simulate('change', { preventDefault() {}, target: { value: 'select' } });
});
});
I am getting an error as 'TypeError: Cannot set property 'value' of undefined', so can anyone help me on how to mock the document.getElementById?
I tried using sinon as below,
var selectElt = document.createElement('select');
selectElt.id = 'foodSelector';
var f = sinon.stub(document, 'getElementById').withArgs('foodSelector').returns(selectElt);
expect(f.value).to.be.equal('select');
Even then i am getting the same error as below,
TypeError: Cannot set property 'value' of undefined