diff --git a/force-app/main/default/classes/AccountController.cls b/force-app/main/default/classes/AccountController.cls index 9877c2a15..fa559b92f 100644 --- a/force-app/main/default/classes/AccountController.cls +++ b/force-app/main/default/classes/AccountController.cls @@ -4,6 +4,11 @@ public with sharing class AccountController { return [SELECT Id, Name FROM Account WITH USER_MODE LIMIT 10]; } + @AuraEnabled(cacheable=true) + public static Integer getTotalNumber() { + return [SELECT COUNT() FROM Account WITH USER_MODE]; + } + @AuraEnabled(cacheable=true) public static Account getSingleAccount() { return [ diff --git a/force-app/main/default/classes/TestAccountController.cls b/force-app/main/default/classes/TestAccountController.cls index 72d222edd..7dd52bce0 100644 --- a/force-app/main/default/classes/TestAccountController.cls +++ b/force-app/main/default/classes/TestAccountController.cls @@ -22,6 +22,17 @@ public class TestAccountController { Assert.areEqual(10, accts.size()); } + @isTest + static void getTotalNumber_returnsCount() { + TestAccountController.createAccounts(10); + + Test.startTest(); + Integer accountNumber = AccountController.getTotalNumber(); + Test.stopTest(); + + Assert.areEqual(10, accountNumber); + } + @isTest static void getSingleAccount() { TestAccountController.createAccounts(1); diff --git a/force-app/main/default/flexipages/Refresh_View.flexipage-meta.xml b/force-app/main/default/flexipages/Refresh_View.flexipage-meta.xml index 08d086960..8515167bc 100644 --- a/force-app/main/default/flexipages/Refresh_View.flexipage-meta.xml +++ b/force-app/main/default/flexipages/Refresh_View.flexipage-meta.xml @@ -45,6 +45,12 @@ Region + + + viewToRefresh + c_viewToRefresh + + region3 Region diff --git a/force-app/main/default/lwc/dispatchRefreshEvent/dispatchRefreshEvent.html b/force-app/main/default/lwc/dispatchRefreshEvent/dispatchRefreshEvent.html index 3488a1d50..a4dcac4b9 100644 --- a/force-app/main/default/lwc/dispatchRefreshEvent/dispatchRefreshEvent.html +++ b/force-app/main/default/lwc/dispatchRefreshEvent/dispatchRefreshEvent.html @@ -30,7 +30,8 @@ - Refresh data in a view without reloading the page thanks to the + Create an account and refresh data in a view without reloading the + page thanks to the { + const { + createApexTestWireAdapter + } = require('@salesforce/sfdx-lwc-jest'); + return { + default: createApexTestWireAdapter(jest.fn()) + }; + }, + { virtual: true } +); + +// Mock refreshApex module +jest.mock( + '@salesforce/apex', + () => { + return { + refreshApex: jest.fn(() => Promise.resolve()) + }; + }, + { virtual: true } +); + +describe('c-view-to-refresh', () => { + 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. + async function flushPromises() { + return Promise.resolve(); + } + + it('registers itself as refresh handler on connected callback', () => { + // Create component + const element = createElement('c-view-to-refresh', { + is: ViewToRefresh + }); + document.body.appendChild(element); + + // Validate if pubsub got registered after connected to the DOM + expect(registerRefreshHandler).toHaveBeenCalled(); + }); + + it('invokes getTotalNumber onload', async () => { + // Create component + const element = createElement('c-view-to-refresh', { + is: ViewToRefresh + }); + document.body.appendChild(element); + + // Emit data from @wire + getTotalNumber.emit(10); + + // Wait for any asynchronous DOM updates + await flushPromises(); + + // Check UI + const divEl = element.shadowRoot.querySelector('div.account-number'); + expect(divEl.textContent).toBe('Number of accounts: 10'); + }); + + it('invokes refreshApex when RefreshEvent is listened', async () => { + // Create component + const element = createElement('c-view-to-refresh', { + is: ViewToRefresh + }); + document.body.appendChild(element); + + // Emit data from @wire + getTotalNumber.emit(10); + + // Fire a RefreshEvent + element.dispatchEvent(new RefreshEvent()); + + // Wait for any asynchronous DOM updates + await flushPromises(); + + // Check refreshApex has been called + expect(refreshApex).toHaveBeenCalled(); + }); + + it('unregisters itself as refresh handler on disconnected callback', () => { + // Create component + const element = createElement('c-view-to-refresh', { + is: ViewToRefresh + }); + document.body.appendChild(element); + + document.body.removeChild(element); + + // Validate if pubsub got registered after connected to the DOM + expect(unregisterRefreshHandler).toHaveBeenCalled(); + }); + + it('is accessible', async () => { + // Create component + const element = createElement('c-view-to-refresh', { + is: ViewToRefresh + }); + document.body.appendChild(element); + + // Assign mock value for resolved Apex promise + getTotalNumber.mockResolvedValue(10); + + // Wait for any asynchronous DOM updates + await flushPromises(); + + // Check accessibility + await expect(element).toBeAccessible(); + }); +}); diff --git a/force-app/main/default/lwc/viewToRefresh/viewToRefresh.html b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.html new file mode 100644 index 000000000..203d44bd7 --- /dev/null +++ b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.html @@ -0,0 +1,18 @@ + + + + + + Number of accounts: {numOfAccounts.data} + + + + + + + + + Refresh a custom component when a Refresh View event is listened. + + + diff --git a/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js new file mode 100644 index 000000000..f96735118 --- /dev/null +++ b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js @@ -0,0 +1,29 @@ +import { LightningElement, wire } from 'lwc'; +import getTotalNumber from '@salesforce/apex/AccountController.getTotalNumber'; +import { + registerRefreshHandler, + unregisterRefreshHandler +} from 'lightning/refresh'; +import { refreshApex } from '@salesforce/apex'; + +export default class ViewToRefresh extends LightningElement { + refreshHandlerID; + + @wire(getTotalNumber) + numOfAccounts; + + connectedCallback() { + this.refreshHandlerID = registerRefreshHandler( + this, + this.refreshHandler + ); + } + + disconnectedCallback() { + unregisterRefreshHandler(this.refreshHandlerID); + } + + refreshHandler() { + refreshApex(this.numOfAccounts); + } +} diff --git a/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js-meta.xml b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js-meta.xml new file mode 100644 index 000000000..075c6d873 --- /dev/null +++ b/force-app/main/default/lwc/viewToRefresh/viewToRefresh.js-meta.xml @@ -0,0 +1,8 @@ + + + 60.0 + true + + lightning__AppPage + + diff --git a/force-app/test/jest-mocks/lightning/refresh.js b/force-app/test/jest-mocks/lightning/refresh.js index 5c78fabcc..c0ebdff31 100644 --- a/force-app/test/jest-mocks/lightning/refresh.js +++ b/force-app/test/jest-mocks/lightning/refresh.js @@ -1,10 +1,31 @@ /** * Refresh Event base class */ -export const RefreshEventName = 'lightning__refreshevent'; - +export const RefreshEventName = 'lightning__refresh'; export class RefreshEvent extends CustomEvent { constructor() { - super(RefreshEventName, { bubbles: true, composed: true }); + super(RefreshEventName, { + composed: true, + cancelable: true, + bubbles: true + }); } } + +let eventHandler; +let elementToRefresh; +export const registerRefreshHandler = jest.fn((element, handler) => { + elementToRefresh = element; + eventHandler = handler; + window.addEventListener( + RefreshEventName, + eventHandler.bind(elementToRefresh) + ); +}); + +export const unregisterRefreshHandler = jest.fn((id) => { + window.removeEventListener( + RefreshEventName, + eventHandler.bind(elementToRefresh) + ); +});