How to test a method dispatching custom event - javascript

I have a static method that creates a custom event and dispatches it:
class MyComponent extends {
static refreshComponent() {
const event = new Event('refreshComponent');
document.dispatchEvent(event);
}
render() {
MyComponent.refreshComponent();
}
}
I am trying test as below:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
const document = { dispatchEvent: jest.fn() };
wrapper.refreshGoalsComponent();
expect(document.dispatchEvent).toHaveBeenCalledWith('refreshComponent');
});
});
But the dispatchEvent is not called here, as there is no mock for 'new Event()'. Is there a way to mock it? Please help

It's a little clunky looking, but something like this should work if you want to verify an event with the expected type was dispatched:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
const dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
// Trigger component render here...
expect(dispatchEventSpy).toHaveBeenCalledWith(expect.any(Event));
expect(dispatchEventSpy.mock.calls[0][0].type).toBe('refreshComponent');
});
});

You can test dispatch event like this:
describe('refreshComponent', () => {
it('should dispatch an event', () => {
jest.spyOn(global, 'Event').mockImplementation((type: string, eventInit?: any) => ({ type, eventInit }));
const mockDispatchEvent = jest.spyOn(document, 'dispatchEvent').mockImplementation(() => true);
// render your component
expect(mockDispatchEvent).toHaveBeenCalledWith({
type: 'refreshComponent',
});
});
});
This way can expect event type and event init value. If you don't want to expect detail event, we does not need to mock Event and expect like:
expect(mockDispatchEvent).toHaveBeenCalledWith(expect.any(Event));

You can mock globals with Jest:
describe('your test', () => {
let EventBak
let documentBak
beforeAll(() => {
EventBak = global.Event
documentBak = global.document
global.Event = jest.fn()
global.document = {
...global.document,
dispatchEvent: jest.fn()
}
})
afterAll(() => {
global.Event = EventBak
global.document = documentBak
})
it('...', () => {
...
expect(global.document.dispatchEvent).toHaveBeenCalled()
})
})

Related

React Testing Library jest.fn() toHaveBeenCalled() not working

Component:
const MyComp = ({ handler }) => {
return <button onClick={handler}>Test</button>
};
Test:
it('Calls the handler', async () => {
const handler = jest.fn();
render(<MyComp handler={handler} />);
const button = screen.getByRole('button', { name: /Test/i });
await fireEvent(button, new MouseEvent('click'));
expect(handler).toHaveBeenCalled();
});
Expected number of calls: >= 1
Received number of calls: 0
Three options:
Make the event bubble, as shown in the example (it doesn't return a promise, no await needed):
fireEvent(button, new MouseEvent("click", { bubbles: true }));
Use the convenience method, which adds default event properties including bubbling (likewise):
fireEvent.click(button);
Use userEvent (does return a promise, as of v14):
await userEvent.click(button);
Try using userEvent.click, like this
it('Calls the handler', async () => {
const handler = jest.fn();
render(<MyComp handler={handler} />);
const button = screen.getByRole('button', { name: /Test/i });
await userEvent.click(button);
expect(handler).toHaveBeenCalled();
});

how to unit test if handler function is called when using middy

im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request.
like this:
const makeThirdPartyServiceRequest = middy(async ({ params }) => {
logger.info(`SENDING Request to ${endpoint} API`)
const url = `https://someurltoathirdpartyservice`
const options = {
method: 'POST',
body: params
}
return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))
However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:
jest.mock('../src/thirdPartyService', () => ({
__esModule: true,
default: {
...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
makeThirdPartyServiceRequest: jest.fn()
}
}))
export {}
import thirdPartyService from '../src/thirdPartyService'
And then in the test i say:
describe('makeThirdPartyServiceRequest()', () => {
it('should makeThirdPartyServiceRequest', async () => {
// Given
// })
const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
// When
const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
// Then
expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
})
})
However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?
You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.
import thirdPartyService from '../src/thirdPartyService'
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
}
}
})
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
hoangdv answer is also valid, but i will answer as well how i continued.
if you completely want to mock middy you mock like following:
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockImplementation(() => {
// ...use(ssm()) will return handler function
return {
before: jest.fn().mockReturnValue(handler)
}
})
}
}
})
However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:
jest.doMock('#middy/util', () => ({
...(jest.requireActual('#middy/util') as {}),
getInternal: jest.fn()
}))
import { getInternal } from '#middy/util'
and then in the test
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(getInternal).mockResolvedValue({
twilioSecrets: { accountSid: 'someSID', serviceId:
'someServiceID', token: 'someToken' }
})
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
this will mock the async part of middy.

Failed toHaveBeenCalled() jest test in Vue component

Not working function call with 'click' trigger in test.
I called function - it is working
I triggered click but test was failed.
describe("Message.test.js", () => {
let wrapper;
const createWrapper = propsData => mount(Message, { propsData
describe("Events", () => {
beforeEach(() => {
wrapper = createWrapper({ message: "Cat" });
});
//Working
it("calls handleClick", () => {
const spy = jest.spyOn(wrapper.vm, 'handleClick');
wrapper.vm.handleClick();
expect(spy).toHaveBeenCalled();
});
//NOT WORKING. WHY?
it("calls handleClick when click on message", () => {
wrapper.vm.handleClick = jest.fn();
//It is Ok
expect(wrapper.contains('.message')).toBe(true);
// #click="handleClick" on element
wrapper.find('.message').trigger('click');
expect(wrapper.vm.handleClick).toHaveBeenCalledTimes(1);
})
});
I added console.log to the function. During the test, I see what function was called.
it('calls handleClick when click on message', () => {
const handleClick = jest.fn()
wrapper.setMethods({ handleClick })
const el = wrapper.find('.message').trigger('click')
expect(handleClick).toBeCalled()
})
// stub
it('triggers a message-clicked event when a handleClick method is called', () => {
const stub = jest.fn()
wrapper.vm.$on('message-clicked', stub)
wrapper.vm.handleClick()
expect(stub).toBeCalledWith('Cat')
})

How do I mock this method chain in Jest?

zoomOut(callback) {
// Zooms out the current screen
this.view.current.zoomOut(300).done(() => {
(hasCallback(callback)) && callback();
});
}
I'm trying to test the function above but I keep getting the following error:
TypeError: this.view.current.zoomOut(...).done is not a function
How can I mock this method chain in Jest?
Thanks to BudgieInWA, I was able to solve this problem by returning done.
For those who are testing a React component with Enzyme, here's how you can do it:
it('should call callback', () => {
const wrapper = shallow(<Zoom {...minProps}/>);
const instance = wrapper.instance();
const callback = jest.fn();
instance.view = {
current: {
zoomOut: jest.fn(() => {
return {
done: jest.fn((callback) => {
callback();
})
};
})
}
};
expect(callback).toHaveBeenCalledTimes(0);
instance.zoomOut(callback);
expect(callback).toHaveBeenCalledTimes(1);
});
You could try this:
const mockZoomOut = jest.fn(() => ({ done(cb) { cb(); } }));
const mockThis = {
view: {
current: {
zoomOut: mockZoomOut,
},
},
};
test('it does', () => {
const cb = jest.fn();
zoomOut.apply(mockThis, [cb]);
expect(mockZoomOut).toHaveBeenCalledTimes(1);
expect(cb).toHaveBeenCalledTimes(1);
});
See Jest Mock Functions and fn.apply.
If you are testing the behaviour of the class as a whole, then you could set up the instance that you are testing to have this.view.current.zoomOut be mockZoomOut somehow.

spying a function called in a rejected promise

I would like to spy a function to test if this function is called in a catch block when a promise is rejected. My code is a react component like this
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
Here I want to test if the function displayErrorAlert has been called. I made this test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
wrapper.instance().handleSubmit({
preventDefault: () => {},
});
expect(displaySpy.calledOnce).toEqual(true);
});
The spy is called but asynchronously of course so my test always fails. I would like to find a way to test if the function has been called only once the catch block has been called and I have no idea how to do that.
Sinon provides you everything you need when handling promises, you can resolve and reject a stubbed promise using sinon.stub().
const resetPassword = sinon.stub();
const displayErrorAlert = sinon.spy();
const preventDefault = sinon.spy();
const props = {
resetPassword,
displayErrorAlert,
history: []
};
describe('Given a component', () => {
let component;
describe('when rendered', () => {
beforeAll(() => {
component = shallow(<ResetPassword {...props} />);
});
describe('and the form is submitted and there is an error reseting the password', () => {
beforeAll(() => {
resetPassword.rejects(new Error('Oops!'));
component.find('button').simulate('click', { preventDefault });
});
it('should invoke the displayErrorAlert function', () => {
expect(displayErrorAlert.calledOnce).toBeTruthy();
});
});
});
});
I found an other solution, I return the promise in the handleSubmit function and use it in my tests.
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
return this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
and my test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
expect.assertions(1);
const promise = wrapper.instance().handleSubmit({
preventDefault: () => {},
});
return promise.then(() => {
expect(displaySpy.calledOnce).toEqual(true);
});
});

Categories

Resources