user35317
user35317

Reputation: 11

How to jest test lightning-pills in LWC

It's the first time I'm using jest tests and I'm trying to test a method that generates lightning pills from a query of groups with access permissions (array). The lightning pills get updated whenever a new group is either added or removed from an array.

I need to test the following scenarios, but I'm not sure if I created the tests correctly:

(these are already properly functioning in actual code).

Test#1: The returned array from query should be displayed as lightning pills

Test#2: Clicking the X icon in a pill should remove the selected pill from array

enter image description here

For Test#1, the jest test fails for a mock array of multiple or single elements. I'm not sure where is the problem, but debugging shows this line is undefined:

  const detailEls = element.shadowRoot.querySelectorAll('lightning-pill');

I'm using return method GroupController.getSpecificGroups:

public with sharing class GroupController {

/* Description: Gets the groups where the contact is being shared to
*
*/
@AuraEnabled
public static List<sObject> getSpecificGroups(String recordId){
    List<GroupShare> groupShareList = new List<GroupShare>();
    List<sObject> returnShareList = new List<sObject>();

    try{
        groupShareList = [SELECT Id, UserOrGroupId, UserOrGroup.Name, ContactId, Contact.Name,
                            FROM GroupShare 
                            WHERE ContactId =: recordId];

        if(groupShareList != NULL && !groupShareList.isEmpty()){
            for(GroupShare csBuff : groupShareList){
                returnShareList.add(csBuff);
            }
        }
    }
    catch(queryException qExcp){
    }
    return returnShareList;
}

}

Jest test is passed when testing if the method successfully passes recordId to the apex method call so I'm thinking there's no problem with method calls.

   it('passes the recordId to the Apex method correctly', () => {
        const RECORD_ID = '00AAABBBCCCDDD12345';
        const APEX_PARAMETERS = { recordId: RECORD_ID };

        // Assign mock value for resolved Apex promise
        getSpecificGroups.mockResolvedValue(APEX_GROUPS_SUCCESS);

        // Create initial element
        const element = createElement('c-cmm-specific-group-sharing', {
            is: Cmm_specificGroupSharing
        });
        element.recordId = RECORD_ID;
        document.body.appendChild(element);

        // Select button for executing Apex call
        const buttonEl = element.shadowRoot.querySelector('lightning-button');
        buttonEl.click();

        return flushPromises().then(() => {
            // Validate parameters of mocked Apex call
            expect(getSpecificGroups.mock.calls[0][0]).toEqual(APEX_PARAMETERS);
        });
    });

For Test#2, jest test for removing pills fail. The pill is not removed from array using dispatchEvent:

it('handleRemoveSelectedItem works', () => {
        // Create element
        const element = createElement('c-cmm-specific-group-sharing', {
            is: Cmm_specificGroupSharing
        });
        element.availableGroups = APEX_GROUPS_SUCCESS;
        document.body.appendChild(element);
    
        // Remove a selected item
        const selPills = element.shadowRoot.querySelectorAll('lightning-pill');
        selPills[0].dispatchEvent(new CustomEvent('remove'));
        // Check selection
        expect(element.availableGroups.length).toBe(0);
    });

Jest Test

import { createElement } from 'lwc';
import Cmm_specificGroupSharing from 'c/c-cmm-specific-group-sharing';
import getSpecificGroups from '@salesforce/apex/GroupController.getSpecificGroups';
import delete from "@salesforce/apex/GroupController.delete";

// Mocking  Apex method call
jest.mock(
    '@salesforce/apex/GroupController.getSpecificGroups',
    () => {
        return {
            default: jest.fn()
        };
    },
    { virtual: true }
);

// Sample data for Apex call
const APEX_GROUPS_SUCCESS = [
    {
        "Id": "000001112222DDDD001",
        "UserOrGroupId":  "00AAABBBCCCDDD12345", 
        "Name":  "Asia Pacific"
    }
];

describe('c-cmm-specific-group-sharing', () => {
    afterEach(() => {
        // The jsdom instance is shared across test cases in a single file so reset the DOM
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
        // Prevent data saved on mocks from leaking between tests
        jest.clearAllMocks();
    });

    // Helper function to wait until the microtask queue is empty. This is needed for promise
    // timing when calling imperative Apex.
    function flushPromises() {
        // eslint-disable-next-line no-undef
        return new Promise(resolve => setImmediate(resolve));
    }

   it('passes the recordId to the Apex method correctly', () => {
        const RECORD_ID = '00AAABBBCCCDDD12345';
        const APEX_PARAMETERS = { recordId: RECORD_ID };

        // Assign mock value for resolved Apex promise
        getSpecificGroups.mockResolvedValue(APEX_GROUPS_SUCCESS);

        // Create initial element
        const element = createElement('c-cmm-specific-group-sharing', {
            is: Cmm_specificGroupSharing
        });
        element.recordId = RECORD_ID;
        document.body.appendChild(element);

        // Select button for executing Apex call
        const buttonEl = element.shadowRoot.querySelector('lightning-button');
        buttonEl.click();

        return flushPromises().then(() => {
            // Validate parameters of mocked Apex call
            expect(getSpecificGroups.mock.calls[0][0]).toEqual(APEX_PARAMETERS);
        });
    });

    it('renders one sharing group', () => {
    
        // Assign mock value for resolved Apex promise
        getSpecificGroups.mockResolvedValue(APEX_GROUPS_SUCCESS);

        // Create initial element
        const element = createElement('c-cmm-specific-group-sharing', {
            is: Cmm_specificGroupSharing
        });
        document.body.appendChild(element);

     
        // Select button for executing Apex call
        const buttonEl = element.shadowRoot.querySelector('lightning-button');
        buttonEl.click();

        return flushPromises().then(() => {
      
            const detailEls = element.shadowRoot.querySelectorAll('lightning-pill');
            expect(detailEls.length).toBe(APEX_GROUPS_SUCCESS.length);
            expect(detailEls[0].label).toBe(
                APEX_GROUPS_SUCCESS[0].Name
            );
        });
    });

    it('handleRemoveSelectedItem works', () => {
        // Create element
        const element = createElement('c-cmm-specific-group-sharing', {
            is: Cmm_specificGroupSharing
        });
        element.availableGroups = APEX_GROUPS_SUCCESS;
        document.body.appendChild(element);
    
        // Remove a selected item
        const selPills = element.shadowRoot.querySelectorAll('lightning-pill');
        selPills[0].dispatchEvent(new CustomEvent('remove'));
        // Check selection
        expect(element.availableGroups.length).toBe(0);
    });
});

JS and HTML

import {
    LightningElement
    api
} from 'lwc';

import getSpecificGroups from '@salesforce/apex/GroupController.getSpecificGroups';
import deleteGroup from "@salesforce/apex/GroupController.deleteGroup";
 
export default class Cmm_specificGroupSharing extends LightningElement {

@api availableGroups;
    
@api
get recordId() {
    return this._recordId;
}
set recordId(value) {
    this._recordId = value;
}
connectedCallback() {
      this.getSpecificGroups();
  } 

getSpecificGroups() {
        this.availableGroups =[];
        getSpecificGroups({
                recordId: this._recordId,
            })
            .then(result => {
                result.map(gShare => {
                     let obj = {
                            'Id': gShare.Id,
                            'UserOrGroupId': gShare.UserOrGroupId,
                            'Name': gShare.UserOrGroup.Name
                        };  
                        this.availableGroups.push(obj);
                    return null;
                   
                })
                console.log('result' + JSON.stringify(result));
            })
            .catch((err) => {
            });
    }

handleRemoveSelectedItem(event) {
        const recordId = event.currentTarget.dataset.id;
        this.availableGroups = this. availableGroups.filter(item => item.id !== recordId);

        deleteGroup ({
            recordId: recordId
        })
        .then(() => {
            this.notifyUser('', this.deleteSuccessMsg, 'success');
        })
        .catch((err) => {
            this.error = err;
        });
    }

}
<template>
  <template for:each={availableGroups} for:item="groupShare">
      <lightning-pill
          data-id={groupShare.Id}
          key={groupShare.Id}
          label={groupShare.Name}
          onremove={handleRemoveSelectedItem}>
      </lightning-pill>
  </template> 
</template> 

I appreciate any help. Thanks!

Upvotes: 1

Views: 1366

Answers (1)

Pirata21
Pirata21

Reputation: 449

Please, could you please try in this way?:

it('handleRemoveSelectedItem works', () => {
    // Create element
    const element = createElement('c-cmm-specific-group-sharing', {
        is: Cmm_specificGroupSharing
    });
    element.availableGroups = APEX_GROUPS_SUCCESS;
    document.body.appendChild(element);

    return Promise.resolve()
    .then(() => {
         // Remove a selected item
        let selPills = element.shadowRoot.querySelectorAll('lightning-pill');
        selPills[0].dispatchEvent(new CustomEvent('remove'));
    }) // Move foward
    .then(() => {
        selPills = element.shadowRoot.querySelectorAll('lightning-pill');
        expect(selPills.length).toBe(0);
    });
});

Upvotes: 1

Related Questions