Test lightning-emp-api

Can we simulate an event subscription in LWC unit test? I have a lightning web component using lighting/empApi like,

export default class ExampleComponent extends LightningElement {
    connectedCallback() {
        const onMessageCallback = (response) => {
            if (this.isSomething) { //Not a public (@api) parameter
                //Do something
            }
        };

        subscribe('/event/ExamplePlatformEvent__e', -1, onMessageCallback).then((response) => {
            console.log('Successfully subscribed to : ', JSON.stringify(response.channel));
        });
    }
}

Now, the latest sfdx-lwc-jest has a stub of lightning/empApi and I can instantiate the above component with a mock implementation in a test.

describe('c-example-component', () => {
    it('test', () => {
        const mockResponse = {
            "id": "_1583742038741_4782",
            "channel": "/event/ExamplePlatformEvent__e",
            "replayId": -1
        }
        const mockEvent = {
            "data": {
                "schema": "a9SbAGsZvysbJq_U77Mv6Q",
                "payload": {
                    "CreatedById": "00556000004PKXTAA4",
                    "CreatedDate": "2020-03-09T14:05:35Z",
                    "SomethingField__c": "ABCDEFG"
                },
                "event": {
                    "replayId": 123
                }
            },
            "channel": "/event/ExamplePlatformEvent__e"
        }

        subscribe.mockImplementation((channel, replayId, onMessageCallback) => {
            onMessageCallback(mockEvent);
            return Promise.resolve(mockResponse);
        });

        const element = createElement('c-example-component', {
                is: ExampleComponent
            });
        document.body.appendChild(element);

        expect(subscribe.mock.calls[0][0]).toBe('/event/ExamplePlatformEvent__e');
        expect(subscribe.mock.calls[0][1]).toBe(-1);
    });
});

In the above test, mock subscribe() is called one time during connectedCallback(). But how to call onMessageCallback again after component is rendered? The parameter isSomething is false for the first time and it will be true by a user operation after component is rendered. So, inside the if clause in onMessageCallback cannot be covered in the current test.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Too bad this question didn’t get an answer earlier. I was able to make this work by using the Module Imports concept under Jest Test Patterns in the LWC Docs. Credit also goes to this StackExchange answer for clarifying how to flush all promises in Node.

Create the following files:

force-apptestjest-mockslightningempApi.js (this is your custom lightning/empApi stub)

// An object to store callbacks
const _channels = {};

// On subscribe, store the callback function and resolve the promise
export const subscribe = jest.fn((channel, replayId, onMessageCallback) => {
    _channels[channel] = { onMessageCallback };
    Promise.resolve({
        id: "_" + Date.now(),
        channel: channel,
        replayId: replayId
    });
});

// I'm using isEmpEnabled in my component, so I have it set to return true
export const isEmpEnabled = jest.fn().mockResolvedValue(true);

// A Jest-specific function for "publishing" your Platform Event
export const jestMockPublish = jest.fn((channel, message) => {
    if (
        _channels[channel] &&
        _channels[channel].onMessageCallback instanceof Function
    ) {
        _channels[channel].onMessageCallback(message);
    }
    Promise.resolve(true);
});

// I just copied these from the standard lightning/empApi stub
export const unsubscribe = jest.fn().mockResolvedValue({});
export const onError = jest.fn().mockResolvedValue(jest.fn());
export const setDebugFlag = jest.fn().mockResolvedValue();

jest.config.js (this overrides the stub from sfdx-lwc-jest and goes at the root of your project)

const { jestConfig } = require("@salesforce/sfdx-lwc-jest/config");
module.exports = {
    ...jestConfig,
    moduleNameMapper: {
        "^lightning/empApi$":
            "<rootDir>/force-app/test/jest-mocks/lightning/empApi"
    }
};

Now, if your component looks like this…

force-appmaindefaultlwcmyComponentmyComponent.js

import { LightningElement, api, track } from "lwc";
import {
    subscribe,
    unsubscribe,
    onError,
    isEmpEnabled
} from "lightning/empApi";

export default class MyComponent extends LightningElement {
    @api recordId;
    @track events = [];
    channelName = "/event/My_Platform_Event__e";
    subscription = {};

    async connectedCallback() {
        const empEnabled = await isEmpEnabled();
        if (empEnabled) {
            this.subscription = await subscribe(this.channelName, -2, event => {
                // Your event processing logic here
                console.log(event.data.payload);
            });
        }
    }
}

…you can test it like this…

import { createElement } from "lwc";
import myComponent from "c/myComponent";
import { jestMockPublish } from "lightning/empApi";
// eslint-disable-next-line no-undef
const flushPromises = () => new Promise(setImmediate);

describe("myComponent tests", () => {
    afterEach(() => {
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
    });

    // NOTE -- THIS IS ASYNC
    it("does what I expect", async () => {
        const element = createElement("my-component", {
            is: myComponent
        });
        element.recordId = "OPP001";
        // Add the component to the DOM
        document.body.appendChild(element);

        // Make sure async subscribe call in connectedCallback completes
        await flushPromises();

        // connectedCallback is now complete, but no Platform Events have 
        // been published yet.  Make assertions here about the state of your 
        // component prior to receiving the Platform Events.

        // Mock-publish a Platform Event and await the promise
        await jestMockPublish("/event/My_Platform_Event__e", {
            data: {
                payload: {
                    Opportunity__c: "OPP001",
                    My_Custom_Field__c: 123
                }
            }
        });
        // Now any DOM updates that depend on the Platform Event should 
        // have rendered; assert about them here
    });
});


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x