0

I have a Lightning Web Component "dapUserList".

This is the HTML:

<template>
    <lightning-card title="User List" icon-name="utility:user">
        <c-errors errors={errors}></c-errors>
        <template lwc:if={showNoUsers}>
            <p>No users returned.</p>
        </template>
        <template lwc:else>
            <ul>
                <template for:each={users} for:item="user">
                    <c-dap-user-item key={user.id} user={user}></c-dap-user-item>
                </template>
            </ul>
        </template>
    </lightning-card>
</template>

and this is the JavaScript:

import { LightningElement, track } from 'lwc';
import { reduceErrors } from 'c/ldsUtilities';
import getDummyApiUsers from '@salesforce/apex/DAP_DummyApiProxyAuraCtrl.getDummyApiUsers';

export default class DapUserList extends LightningElement {
    @track users = [];
    @track errors = false;
    connectedCallback() {
                         getDummyApiUsers()
                         .then(users => {
                                 if (users) {
                                    this.users = users;
                                    this.errors = false;
                                 }
                            })
                         .catch(error => {
                                 this.users = [];
                                 this.errors = reduceErrors(error);
                             });
                     }

    get showNoUsers() {
        return !this.errors && !this.users?.length;
    }
}

I'm trying to prove an instance of c-dap-user-item occurs for each user returns from the server.

I've created these tests:

import { LightningElement, track } from 'lwc';
import { createElement } from 'lwc';
import { reduceErrors } from 'c/ldsUtilities';
import DapUserList from 'c/dapUserList';
import getDummyApiUsers from '@salesforce/apex/DAP_DummyApiProxyAuraCtrl.getDummyApiUsers';

const mockGetDummyApiUsersWithMultipleUsers = require('./data/getDummyApiUsersWithMultipleUsers.json');
const mockGetDummyApiUsersWithNoUsers = require('./data/getDummyApiUsersWithNoUsers.json');
const mockGetDummyApiUsersWithOneUser = require('./data/getDummyApiUsersWithOneUser.json');

jest.mock(
  '@salesforce/apex/DAP_DummyApiProxyAuraCtrl.getDummyApiUsers',
  () => {
    const {
      createApexTestWireAdapter
    } = require('@salesforce/sfdx-lwc-jest');
    return {
      default: createApexTestWireAdapter(jest.fn())
    };
  },
  { virtual: true }
);

const elementUnderTest = 'c-dap-user-list';
const childUserElement = 'c-dap-user-item';
describe(elementUnderTest, () => {
  afterEach(() => {
    while (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }

    jest.clearAllMocks();
  });

  describe('getDummyApiUsers @wire data', () => {
    it('renders one user', () => {
      // Arrange
     getDummyApiUsers.mockResolvedValue(mockGetDummyApiUsersWithOneUser);
     const element = createElement(elementUnderTest, {
        is: DapUserList
      });

      // Act
      document.body.appendChild(element);

      return Promise.resolve()
        .then(() => {
              // Assert
            const userElements = element.shadowRoot.querySelectorAll(childUserElement);
            expect(userElements.length).toBe(mockGetDummyApiUsersWithOneUser.length);
          });
    });

    it('renders multiple users', () => {
      // Arrange
      getDummyApiUsers.mockResolvedValue(mockGetDummyApiUsersWithMultipleUsers);
      const element = createElement(elementUnderTest, {
        is: DapUserList
      });

      // Act
      document.body.appendChild(element);

      return Promise.resolve()
        .then(() => {
              // Assert
            const userElements = element.shadowRoot.querySelectorAll(childUserElement);
            expect(userElements.length).toBe(mockGetDummyApiUsersWithMultipleUsers.length);
          });
    });

    it('renders no users', () => {
      // Arrange
      getDummyApiUsers.mockResolvedValue(mockGetDummyApiUsersWithNoUsers);
      const element = createElement(elementUnderTest, {
        is: DapUserList
      });

      // Act
      document.body.appendChild(element);

      return Promise.resolve()
        .then(() => {
              // Assert
              const userElements = element.shadowRoot.querySelectorAll(childUserElement);
              expect(userElements.length).toBe(mockGetDummyApiUsersWithNoUsers.length);

                const noUsersMessage = element.shadowRoot.querySelector('p');
                expect(noUsersMessage).not.toBeNull();
                expect(noUsersMessage.textContent).toBe('No users returned.');
          });
    });
  });
});

The first two fail because element.shadowRoot.querySelectorAll(childUserElement) never returns any elements.

I think this is because they will only be rendered after connectedCallback() is executed and I don't think this is executing.

I've tried including element.connectedCallback(); in the test, but this results in "TypeError: element.connectedCallback is not a function" ... It does not seem connectedCallback() is visible to the tests. If we have some JavaScript equivalent for @TestVisible` we could add, I'm not aware of it, and I expect using it would be considered a bad practice anyway.

So, what's the right way to fix these tests?

1 Answer 1

1

The equivalent of @TestVisible would actually just be @api, making the function public. And you're right, it would be a bad idea to do this.

Note that document.body.appendChild is what triggers connectedCallback. Your problem, I believe, is that you haven't waited long enough. The first Promise.resolve() just lets the "browser" render the DOM; the promise inside the connectedCallback() is still in a pending status. You need to wait for it to finish, too.

Try:

it('renders no users', async () => {
  // Arrange
  getDummyApiUsers.mockResolvedValue(mockGetDummyApiUsersWithNoUsers);
  const element = createElement(elementUnderTest, {
    is: DapUserList
  });

  // Act
  document.body.appendChild(element);
  await Promise.resolve();
  return Promise.resolve()
    .then(() => {
          // Assert
          const userElements = element.shadowRoot.querySelectorAll(childUserElement);
          expect(userElements.length).toBe(mockGetDummyApiUsersWithNoUsers.length);

            const noUsersMessage = element.shadowRoot.querySelector('p');
            expect(noUsersMessage).not.toBeNull();
            expect(noUsersMessage.textContent).toBe('No users returned.');
      });
});
1
  • Perfect! (Repeating Promise.resolve() looks strange, but I can confirm it is necessary.) Thanks! Commented Apr 13 at 4:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .