The component itself, when loaded, queries some data from the database. Then, depending on what it finds, loads the specific template. One of those has a button, which will call the server again, and when it does it will display either a success or error toast on the interface.
I can mock the Apex calls with jest's mockRejectedValue
or mockResolvedvalue
alright, and I can return a Promise at the end of the test to wait for the first DOM change (that is, when the component is loaded, and it calls the server to fetch the records and decide what to render). But in this case, I need to force another DOM change.
- Component is set up
- Component is loaded (Promise used)
- Button is shown on the component after loading the database mocked result
- Click on the button is simulated
- The click fires a method on the component that calls another Apex method.
- Should I return another promise here? How?
I have the following test for this situation:
it('Should display a toast when the user checks in.', () => {
// mock the two calls to the database that returns
// a boolean each indicading wether the check in or check out
// is available - it is used to display the buttons
isCheckInAvailable.mockResolvedValue(CHECK_INOUT_AVAILABLE)
isCheckOutAvailable.mockResolvedValue(CHECK_INOUT_AVAILABLE)
// mock the database check-in
doCheckIn.mockRejectedValue(CHECK_IN_ERROR)
// create the component
const recordId = '00Q3F000002StmkUAC'
const comp = createElement('c-checkin', {
is: CheckIn
})
// set some of the attributes
comp.recordId = recordId
comp.markers = []
// create the event handler and assign it to the component
const eventHandler = jest.fn()
comp.addEventListener(ShowToastEventName, eventHandler)
// put the component on the "screen"
document.body.appendChild(comp)
// first promise to resolve the first Apex calls
return Promise.resolve(() => {
// markers shouldn't exist, since we specified an empty list,
// but it should have a length (it shouldn't be undefined)
expect(comp.markers).toHaveLength(0)
expect(comp.buttons.checkInEnabled).toBe(true)
// get the check-in button, which should be displayed on the
// screen now, after the component loads
comp.shadowRoot.querySelector('lightning-button[label="Check-IN"]').click()
}).then(() => {
// TEST FAILS HERE!
// "Expected mock function to have been called, but it was not called."
expect(eventHandler).toHaveBeenCalled()
expect(eventHandler.mock.calls[0][0].detail.variant).toBe('success')
expect(eventHandler.mock.calls[0][0].detail.message).toBe('Check-In realizado!')
expect(eventHandler.mock.calls[0][0].detail.title).toBe('OK')
})
})
Edit 6/3/2019
I expected the "Check-in" button to be displayed, and it seems that it is displayed (otherwise the "click" part in the end of the test would fail). The click button is tied to a lightning-button
on the HTML:
<lightning-button
variant="brand"
label="Check-IN"
onclick={handleCheckIn}
class="slds-m-around_small"
></lightning-button>
That click event calls the JS method:
handleCheckIn () {
var args = {}
if (this.coordinates.lat && this.coordinates.lon) {
args = {
recordId: this.recordId,
latitude: this.coordinates.lat,
longitude: this.coordinates.lon
}
} else {
args = { recordId: this.recordId }
}
doCheckIn(args)
.then(result => { // eslint-disable-line no-unused-vars
this.dispatchEvent(new ShowToastEvent({
title: 'OK',
message: 'You have checked in!',
variant: 'success'
}))
this.buttons.checkInEnabled = false
})
.catch(error => { // eslint-disable-line no-unused-vars
this.dispatchEvent(new ShowToastEvent({
title: 'Erro',
message: 'Something went wrong while doing the check-in.',
variant: 'error'
}))
})
.finally(() => {
this.showCloseButton = true
})
}
And the doCheckIn
is tied to the Apex code, which is what I'm trying to mock/test on the second part.
The idea here is that the component is loaded, then it calls Apex. It then presents information to the user. Then if the user clicks a button, it calls Apex again, and it displays a toast depending on the call result.
it.only('should show the check-in toast.', () => {
// mocks the result that enables the check-in button
isCheckInAvailable.mockResolvedValue(true)
// mocks the result that disables the check-out button
isCheckOutAvailable.mockResolvedValue(false)
// mocks the result of the check-in action
doCheckIn.mockResolvedValue({})
// set a random record Id
const recordId = '00Q3F000002StmkUAC'
// create the component
const comp = createElement('c-checkin', {
is: CheckIn
})
comp.recordId = recordId
// set up a mock for the toast event
const toastEventHandler = jest.fn()
// add the event listener to the component
comp.addEventListener(ShowToastEventName, toastEventHandler)
// spawn the component
document.body.appendChild(comp)
return Promise.resolve(() => {
// checkInEnabled is a property that is used on the `disabled` attribute
// on the lightning-button, it should be true for the button to be enabled
expect(comp.checkInEnabled).toBe(true)
// query the button, and click it
comp.shadowRoot.querySelector('lightning-button[label="Check-IN"]').click()
}).then(() => {
// when the button is clicked by the user, it should fire the doCheckIn method
// and call Apex, so I expect it to have been called.
expect(doCheckIn).toHaveBeenCalled()
// BUT IT DOES NOT! And the test fails here
})
})
.then
chaining, but with no success.