componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside);
}
/**
* Set the wrapper ref
*/
setWrapperRef(node) {
this.wrapperRef = node;
}
handleClickOutside(event) {
/* istanbul ignore next */
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
this.props.clickHandler();
}
}
How do i call the handleClickOutside function. How should I mimic the clickOutside here? Please help
it('lets click outside and close dropdown', () => {
const handleClickOutside = sinon.spy();
expect(handleClickOutside.called).to.be.true;
wrapper.unmount();
});
Assuming this is a HOC or render prop that renders other components as children (this.props.children) -- also the following is a Jest and Enzyme test, so it may be slightly different than what you're using.
components/__test__/ClickHandler.test.js
const clickHandler = jest.fn()
const initialProps = {
clickHandler,
children: <button className="example">Test</button>
...other props
};
const wrapper = shallow(<ClickHandler {...initialProps} />);
describe("Example", () => {
it('handles clicks inside of the component', () => { // this would test the event lisenter, the class method, and the clickHandler -- slightly overkill
const spy = jest.spyOn(wrapper.instance(), 'handleClickOutside');
wrapper.instance().forceUpdate();
wrapper.find('button').simulate('click');
expect(spy).toHaveBeenCalled();
expect(clickHandler).toHaveBeenCalledTimes(0);
spy.mockClear();
});
it('handles clicks outside of the component', () => { // this is a more straight forward test that assumes the event listener is already working
const event = { target: <div className="example">Outside Div</div> };
wrapper.instance().handleClickOutside(event);
expect(clickHandler).toHaveBeenCalled();
});
})
Related
I want to test a React component that, internally, uses a custom hook (Jest used). I successfully mock this hook but I can't find a way to test the calls on the functions that this hook returns.
Mocked hook
const useAutocomplete = () => {
return {
setQuery: () => {}
}
}
React component
import useAutocomplete from "#/hooks/useAutocomplete";
const MyComponent = () => {
const { setQuery } = useAutocomplete();
useEffect(() => {
setQuery({});
}, [])
...
}
Test
jest.mock("#/hooks/useAutocomplete");
it("sets the query with an empty object", () => {
render(<MyComponent />);
// I want to check the calls to setQuery here
// e.g. mockedSetQuery.mock.calls
});
CURRENT SOLUTION
I currently made the useAutocomplete hook an external dependency:
import useAutocomplete from "#/hooks/useAutocomplete";
const MyComponent = ({ autocompleteHook }) => {
const { setQuery } = autocompleteHook();
useEffect(() => {
setQuery({});
}, [])
...
}
MyConsole.defaultProps = {
autocompleteHook: useAutocomplete
}
And then I test like this:
const mockedSetQuery = jest.fn(() => {});
const useAutocomplete = () => ({
setQuery: mockedSetQuery,
});
it("Has access to mockedSetQuery", () => {
render(<MyComponent autocompleteHook={useAutocomplete} />);
// Do something
expect(mockedSetQuery.mock.calls.length).toBe(1);
})
You can mock the useAutocomplete's setQuery method to validate if it's invoked.
jest.mock("#/hooks/useAutocomplete");
it("sets the query with an empty object", () => {
const useAutocompleteMock = jest.requireMock("#/hooks/useAutocomplete");
const setQueryMock = jest.fn();
useAutocompleteMock.setQuery = setQueryMock;
render(<MyComponent />);
// The mock function is called twice
expect(setQueryMock.mock.calls.length).toBe(1);
});
I need to test the following component that consumes a custom hook of mine.
import { useMyHook } from 'hooks/useMyHook';
const MyComponent = () => {
const myHookObj = useMyHook();
const handler = () => {
myHookObj.myMethod(someValue)
}
return(
<button onClick={handler}>MyButton</button>
);
};
This is my test file:
jest.mock('hooks/useMyHook', () => {
return {
useMyHook: () => {
return {
myMethod: jest.fn(),
};
},
};
});
describe('<MyComponent />', () => {
it('calls the hook method when button is clicked', async () => {
render(<MyComponent {...props} />);
const button = screen.getByText('MyButton');
userEvent.click(button);
// Here I need to check that the `useMyHook.method`
// was called with some `value`
// How can I do this?
});
});
I need to check that the useMyHook.method was called with some value.
I also want to test it from multiple it cases and it might be called with different values on each test.
How can I do this?
This is how I was able to do it:
import { useMyHook } from 'hooks/useMyHook';
// Mock custom hook that it's used by the component
jest.mock('hooks/useMyHook', () => {
return {
useMyHook: jest.fn(),
};
});
// Mock the implementation of the `myMethod` method of the hook
// that is used by the Component
const myMethod = jest.fn();
(useMyHook as ReturnType<typeof jest.fn>).mockImplementation(() => {
return {
myMethod: myMethod,
};
});
// Reset mock state before each test
// Note: is needs to reset the mock call count
beforeEach(() => {
myMethod.mockReset();
});
Then, on the it clauses, I'm able to:
it (`does whatever`, async () => {
expect(myMethod).toHaveBeenCalledTimes(1);
expect(myMethod).toHaveBeenLastCalledWith(someValue);
});
I have this hook that should trigger beforeunload event when the compoenent is mounted and unmounted.
const UseHook = (fns: (e) => void) => {
const cb = useRef(fns);
useEffect(() => {
cb.current = fn;
}, [fn]);
useEffect(() => {
const onUnloadFN = (args: BeforeUnloadEvent) => cb.current?.(args);
window.addEventListener('beforeunload', onUnloadFN);
return () => {
window.removeEventListener('beforeunload', onUnloadFN);
};
}, []);
};
Now I want to test the hook using jest and enzyme:
import { mount } from 'enzyme';
import React from 'react';
const HookWrapper = () => {
useHook((e) => {
e.preventDefault();
e.returnValue = '';
});
return <div>component</div>;
};
describe('useHook', () => {
const location: Location = window.location;
delete window.location;
const mockPageReloading = jest.fn();
window.location = {
...location,
reload: mockPageReloading,
};
it('should mount', () => {
const mockedOnload = jest.fn();
window.addEventListener = jest.fn((event) => {
if (event === 'beforeunload') {
mockedOnload();
}
});
const wrapper = mount(<HookWrapper />);
expect(mockedOnload).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
console.log(wrapper.debug());
});
it('should unmount', () => {
const mockedOnload = jest.fn();
window.removeEventListener = jest.fn((event) => {
if (event === 'beforeunload') {
mockedOnload();
}
});
const wrapper = mount(<HookWrapper />);
wrapper.unmount();
expect(mockedOnload).toHaveBeenCalledTimes(1);
});
});
The first test pass, but the second retrieve that the event listener wasn't call on unmount (it was called 0 times).
Who can help with this?
Basically I want to test if the event was triggered on mount and also on unmount.
PS: this hook is also used to detect when user reload the page. If somebody has other idea how to test this hook, please let me know.
I don't know how to test key down event & prevent default. Test reached code but preventDefault has never been called: Received number of calls: 0
React Component - App.js
const onKeyDown = e => {
console.log("==== TEST REACHED HERE ====")
e.preventDefault(); // NEVER CALLED ???
};
useEffect(() => {
document.addEventListener("keydown", onKeyDown, false);
return () =>
document.removeEventListener("keydown", onKeyDown, false);
}, []);
Unit test
it("should prevent default action on key down", () => {
const { getByRole } = render(<App {...props} />);
const grid = getByRole("app");
const mockEvent = { preventDefault: jest.fn() };
fireEvent.keyDown(grid, mockEvent);
expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
});
I found a more explicit way than accepted answer. You can use createEvent from 'react-testing-library' to manually create the event before firing it.
it("should prevent default action on key down", () => {
const { getByRole } = render(<App {...props} />);
const grid = getByRole("app");
const keyDownEvent = createEvent.keyDown(grid);
fireEvent(grid, keyDownEvent);
expect(keyDownEvent.defaultPrevented).toBe(true);
});
I think this method can be reused to test other things than defaultPrevented !
It seems that you can not mock preventDefault property of the event with react testing library.
Under the hook, fireEvent is calling dispatchEvent, so you can take advantage of the fact that calling event.preventDefault returns false if the event is cancelled:
it("should prevent default action on key down", () => {
const { getByRole } = render(<App {...props} />);
const grid = getByRole("app");
const isPrevented = fireEvent.keyDown(grid);
expect(isPrevented).toBe(false);
});
I have a component that renders a third party component. When rendering, it stores this component's ref locally in localApi. When the button is clicked it calls the exportData function which returns some data in a callback.
const MyComponent = () => {
let localApi = React.useRef() as any;
const submit = () => {
localApi.exportData((data) => {
// do something with data
})
}
return (
<>
<ThirdParty ref={(api) => localApi = api} fullVersion={true}/>
<button onSubmit={submit}>Submit</button>
</>
)
}
The problem I'm facing is mocking this ThirdParty component's ref in Jest. Since my mock isn't working, upon simulating a clickon the button throws exportData is not defined error. I've tried the following to no avail:
jest.mock("third-party", () => ({
default: (props: any) => {
// tslint:disable-next-line: no-shadowed-variable
const React = require("react");
console.log(props); // this only returns the `fullVersion = true` when
const MockThirdParty: React.FC = () => {
const exportData = (callBack) => {
callBack("Mock data");
}
return <div>Third Party Component</div>;
}
return <MockThirdParty/>;
},
__esModule: true,
}));
How can I go about mocking it properly?