LWC Jest testing toasts

lightning-web-componentslwc-jesttoast

I'm having an issue with testing toasts. I have read a lot about this thanks to google and followed the instructions but I can't seem to test my toast.

When I step through the test, I have a breakpoint on the line where the toast is thrown. This is so I know that the code is reaching the statement and not throwing any error.

I have also made sure that I have the file at ./force-app/test/test-mocks/lightning/platformShowToastEvent.js

So I am wondering what I am doing wrong.

Here's the code

import {LightningElement, api, wire} from 'lwc';
import {NavigationMixin} from 'lightning/navigation';
import {getFieldValue, getRecord} from "lightning/uiRecordApi";
import {ShowToastEvent} from "lightning/platformShowToastEvent";

import addAdministratorRestCall
    from '@salesforce/apex/ALSLicenceController.addAdministrator';

export default class LicenceAdministratorsOnLicence extends NavigationMixin(LightningElement) {

    @api
    recordId;

    getAdministratorsResult;

    selectedContactId;

    @wire(getRecord, {recordId: '$recordId', fields: [LICENCE_GUID_FIELD]})
    licence;

    @wire(getRecord,
        {recordId: '$selectedContactId', fields: [CONTACT_GUID_FIELD]})
    selectedContact;

    handleAdd() {
        addAdministratorRestCall({
            licenceGuid: this.licenceGuid,
            contactGuid: this.selectedContactGuid
        })
        .then((result) => {
            this.throwSucceededToast('Licence Administrator added');
        })
        .catch((errors) => {
            this.throwFailedToast(errors);
        })
        .finally(() => {
            this.closeModal();
        });
    }

    throwSucceededToast(message) {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Success',
                message: message,
                variant: 'success'
            })
        );
    }

    throwFailedToast(errors) {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Error',
                message: 'this is bad',
                variant: 'error',
                mode: 'sticky'
            })
        );

        this.closeModal();
    }

    
    get licenceGuid() {
        return getFieldValue(this.licence.data, LICENCE_GUID_FIELD);
    }

    get selectedContactGuid() {
        return getFieldValue(this.selectedContact.data, CONTACT_GUID_FIELD);
    }
}

Here is the test

import {createElement} from 'lwc';
import {getRecord} from 'lightning/uiRecordApi';
import {ShowToastEventName} from 'lightning/platformShowToastEvent';

import {
    registerApexTestWireAdapter,
    registerLdsTestWireAdapter,
} from '@salesforce/sfdx-lwc-jest';


import getLicenceAdministrators from '@salesforce/apex/ALSLicenceController.getLicenceAdministrators';
import getUserPermissions from '@salesforce/apex/ALSLicenceController.getUserPermissions';

import LicenceAdministratorsOnLicence from 'c/licenceAdministratorsOnLicence';

const mockedResponses = require('./data/mockedResponses.json');
const getLicenceAdministratorsAdapter = registerApexTestWireAdapter(getLicenceAdministrators);
const getUserPermissionsAdapter = registerApexTestWireAdapter(getUserPermissions);
const getLicenceAdapter = registerLdsTestWireAdapter(getRecord);
const getContactAdapter = registerLdsTestWireAdapter(getRecord);

jest.mock(
    '@salesforce/apex/ALSLicenceController.addAdministrator',
    () => {
        return {
            default: jest.fn(() => Promise.resolve({ data: {} }))
        };
    },
    { virtual: true }
);

describe('c-licence-administrators-on-licence', () => {

    afterEach(() => {
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
    });

    it('submits and throws toast', () => {
        const element = createElement('c-licence-administrators-on-licence', {
            is: LicenceAdministratorsOnLicence
        });

        getLicenceAdministratorsAdapter.emit(mockedResponses.testAdministrators);
        getUserPermissionsAdapter.emit(mockedResponses.allowed);
        document.body.appendChild(element);

        const showToastHandler = jest.fn();
        element.addEventListener(ShowToastEventName, showToastHandler);

        getLicenceAdapter.emit(mockedResponses.testLicence);
        getContactAdapter.emit(mockedResponses.testContact);

        return Promise.resolve().then(() => {
            const addAdministratorButton = element.shadowRoot.querySelector("lightning-button[data-id=add-administrator]");
            addAdministratorButton.click();
        }).then(()=> {
            const submitButton = element.shadowRoot.querySelector("lightning-button[data-id=submit-button]");
            submitButton.click();
        }).then(()=> {
            expect(showToastHandler).toBeCalledTimes(1);
        })
    });

Best Answer

In this case, the call to the toast is in a chain of promises.

So in the end, this change is what worked:

return flushPromises().then(() => {
                expect(showToastHandler).toHaveBeenCalledTimes(1);
            });
        // })
    });

    function flushPromises() {
        return new Promise((resolve) => setImmediate(resolve));
    }

If there's a better way to do this, I'd love to hear it. There is an issue with this 'setImmediate' call that you can read about in many other posts.

If you come across any issue with it, be sure to add this to your imports:

import {setImmediate} from 'timers';
Related Topic