4

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.

1 Answer 1

5

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-app\test\jest-mocks\lightning\empApi.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 }; return 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); } return 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-app\main\default\lwc\myComponent\myComponent.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 }); }); 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.