Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement: <adapter>.emit() should return a promise indicating when task is complete #16

Open
benschell opened this issue Nov 12, 2019 · 0 comments

Comments

@benschell
Copy link

I'm not sure this is even possible, but...

We (Platform Actions) have now encountered the second instance where the effects of calling .emit() were not complete by the time the Jest test proceeded. For example, we have the following in lwcQuickActionLayout.test.js:

import { registerLdsTestWireAdapter } from 'wire-service-jest-util';
import { getQuickActionRecordLayoutTemplate } from 'laf/templateApi';
const getQALTemplateAdapter = registerLdsTestWireAdapter(getQuickActionRecordLayoutTemplate);

[...]

   it('passes the transformed actionApiName to the QAL template wire', async () => {
        expect.assertions(2);
        const el = createComponentUnderTest(inputs);
        expect(el).toBeDefined();

        getQALTemplateAdapter.emit({ data: {} });

        await Promise.resolve();
        const lastConfig = getQALTemplateAdapter.getLastConfig();
        expect(lastConfig.actionApiName).toEqual("Global_LogACall");
    });

Locally, this test succeeds as expected; the wire and its effects are processed before const lastConfig = getQALTemplateAdapter.getLastConfig();.

On autobuilds, this fails. It appears that the internal implementation of the wire system does not succeed before the expectations of the test are processed. I'd argue that even the await Promise.resolve() is unnecessary but was probably added by the test author to get this to succeed locally.

As another example, we have this in our codebase now:

    it('renders standard buttons on the server-side', async () => {
        expect.assertions(3);

        const mockActionsReceivedListener = jest.fn((e) => {
            expect(e).toHaveProperty('detail');
            expect(e.detail.actions[0].render.useServerSideRendering).toBe(true);
        });
        const listeners = {};
        listeners[ACTIONS_RECEIVED_EVENT] = mockActionsReceivedListener;
        createComponentUnderTest({}, listeners);

        // Force microtask
        await Promise.resolve();
        const action = Object.assign({}, mockActions[0]);
        action.type = "StandardButton"; // It should already be a standard button, but double-check!
        getRecordActionsAdapter.emit({"actions": {"003xx000004WhCbAAK": {"actions": [action]}}});

        // Wait to allow time for the event to propagate
        // W-6543284: The test is passing locally but failing in autobuilds. I suspect this is due to a timing issue.
        // Introducing a timeout to give the event time to propagate
        // TODO: Revisit this to lower the timeout if possible.
        await (new Promise((resolve) => {
            // eslint-disable-next-line @lwc/lwc/no-async-operation
            setTimeout(resolve, 100);
        }));
        expect(mockActionsReceivedListener).toHaveBeenCalledTimes(1);
    });

The block between the comment referencing W-6543284 and the expect() call was added to get around the same kind of result: the effects of getRecordActionsAdapter.emit() did not complete before the next lines were processed.

In my opinion, these hacks should not be necessary in individual tests. Is it possible to improve the implementations of these wire adapter mocks to properly abstract away the internal implementation? Ideally this would be achieved by returning a promise one could await to know that the effects have been completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant