sf-lwc-testing
Skill15 starsChanged 7 months agoMITView on GitHub
LWC Jest testing — component mounting, wire/Apex mocking, user interaction simulation, toast/navigation verification. Use when writing or debugging LWC Jest tests. Do NOT use for Apex or Flow testing.
- Installs packages
In this repository58 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- asideSkill
- checkpointSkill
- configure-sccSkill
- continuous-agent-loopSkill
- mcp-server-patternsSkill
- model-routeSkill
- prompt-optimizerSkill
- refactor-cleanSkill
- resume-sessionSkill
- save-sessionSkill
- search-firstSkill
- security-scanSkill
- sessionsSkill
- sf-2gp-security-reviewSkill
- sf-agentforce-developmentSkill
- sf-agentforce-mcp-actionsSkill
- sf-apex-async-patternsSkill
- sf-apex-best-practicesSkill
- sf-apex-constraintsSkill
- sf-apex-cursorSkill
- sf-apex-enterprise-patternsSkill
- sf-apex-testingSkill
- sf-api-designSkill
- sf-approval-processesSkill
- sf-aura-developmentSkill
- sf-build-fixSkill
- sf-cli-referenceSkill
- sf-data-modelingSkill
- sf-debuggingSkill
- sf-deployment-constraintsSkill
- sf-deploymentSkill
- sf-devops-ci-cdSkill
- sf-docs-lookupSkill
- sf-e2e-testingSkill
- sf-experience-cloudSkill
- sf-flow-developmentSkill
- sf-governor-limitsSkill
- sf-harness-auditSkill
- sf-helpSkill
- sf-integrationSkill
- sf-lwc-constraintsSkill
- sf-lwc-developmentSkill
- sf-metadata-managementSkill
- sf-platform-events-cdcSkill
- sf-quickstartSkill
- sf-security-constraintsSkill
- sf-securitySkill
- sf-soql-constraintsSkill
- sf-soql-optimizationSkill
- sf-tdd-workflowSkill
- sf-testing-constraintsSkill
- sf-trigger-constraintsSkill
- sf-trigger-frameworksSkill
- sf-visualforce-developmentSkill
- strategic-compactSkill
- update-docsSkill
- update-platform-docsSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 5 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
When writing Jest unit tests for Lightning Web Components
- Distinctive: line 14
When mocking Wire adapters, Apex imperative calls, or navigation in LWC tests
- Distinctive: line 15
When debugging flaky or async LWC tests
- Distinctive: line 16
When setting up LWC test infrastructure for a new project
- Very specific: line 100
Use jest.mock() with the wire adapter directly.
- Distinctive: line 294
Test LWC components in isolation without deploying to a scratch org.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
LWC Testing with Jest
LWC uses Jest as its test runner. Salesforce provides @salesforce/sfdx-lwc-jest to handle Salesforce-specific imports. Tests run in Node.js — no browser, no Salesforce org.
When to Use
- When writing Jest unit tests for Lightning Web Components
- When mocking Wire adapters, Apex imperative calls, or navigation in LWC tests
- When debugging flaky or async LWC tests
- When setting up LWC test infrastructure for a new project
@../_reference/LWC_PATTERNS.md @../_reference/TESTING_STANDARDS.md
Setup Procedure
Step 1 — Install
npm install --save-dev @salesforce/sfdx-lwc-jestStep 2 — jest.config.js
const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config');
module.exports = {
...jestConfig,
modulePathIgnorePatterns: ['<rootDir>/.localdevserver'],
testEnvironment: 'jsdom',
testMatch: ['**/__tests__/**/*.test.js'],
setupFiles: ['<rootDir>/jest.setup.js']
};Step 3 — jest.setup.js
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(), unobserve: jest.fn(), disconnect: jest.fn()
}));Step 4 — Test File Location
lwc/accountSearch/
accountSearch.html
accountSearch.js
__tests__/
accountSearch.test.jsBasic Component Rendering
import { createElement } from 'lwc';
import AccountSearch from 'c/accountSearch';
describe('c-account-search', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
it('renders the search input', () => {
const element = createElement('c-account-search', { is: AccountSearch });
document.body.appendChild(element);
const input = element.shadowRoot.querySelector('lightning-input[type="search"]');
expect(input).not.toBeNull();
expect(input.label).toBe('Search Accounts');
});
it('renders with public @api property', () => {
const element = createElement('c-account-search', { is: AccountSearch });
element.maxRecords = 25;
document.body.appendChild(element);
expect(element.maxRecords).toBe(25);
});
});Wire Adapter Mocking (Modern Pattern)
Use jest.mock() with the wire adapter directly. The deprecated registerApexTestWireAdapter pattern should not be used in new projects.
import { createElement } from 'lwc';
import AccountDetails from 'c/accountDetails';
import getAccountDetails from '@salesforce/apex/AccountsController.getAccountDetails';
jest.mock(
'@salesforce/apex/AccountsController.getAccountDetails',
() => ({ default: jest.fn() }),
{ virtual: true }
);
describe('c-account-details wire', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
it('displays account name when wire returns data', async () => {
getAccountDetails.mockResolvedValue({
Id: '001000000000001AAA', Name: 'Acme Corporation'
});
const element = createElement('c-account-details', { is: AccountDetails });
element.recordId = '001000000000001AAA';
document.body.appendChild(element);
await Promise.resolve();
await Promise.resolve();
expect(element.shadowRoot.querySelector('.account-name').textContent)
.toBe('Acme Corporation');
});
it('displays error state when wire returns error', async () => {
getAccountDetails.mockRejectedValue({
body: { message: 'Record not found' }, status: 404
});
const element = createElement('c-account-details', { is: AccountDetails });
element.recordId = '001000000000001AAA';
document.body.appendChild(element);
await Promise.resolve();
await Promise.resolve();
expect(element.shadowRoot.querySelector('.error-container')).not.toBeNull();
});
});Apex Imperative Call Mocking
jest.mock(
'@salesforce/apex/AccountSearchController.searchAccounts',
() => jest.fn(), // imperative: module IS the function
{ virtual: true }
);
import searchAccounts from '@salesforce/apex/AccountSearchController.searchAccounts';
it('calls Apex on search and displays results', async () => {
searchAccounts.mockResolvedValue([
{ Id: '001000000000001AAA', Name: 'Acme Corp' }
]);
const element = createElement('c-account-search', { is: AccountSearch });
document.body.appendChild(element);
// Trigger search
const input = element.shadowRoot.querySelector('lightning-input');
input.dispatchEvent(new CustomEvent('change', { detail: { value: 'Acme' } }));
element.shadowRoot.querySelector('lightning-button[label="Search"]').click();
await flushPromises();
expect(searchAccounts).toHaveBeenCalledWith({ searchTerm: 'Acme' });
const rows = element.shadowRoot.querySelectorAll('.account-row');
expect(rows).toHaveLength(1);
});Key distinction: for imperative Apex, mock as () => jest.fn(). For wired Apex, mock as () => ({ default: jest.fn() }).
Async Testing — flushPromises
LWC re-renders are asynchronous. Use flushPromises instead of chaining multiple Promise.resolve() calls.
function flushPromises() {
return new Promise(resolve => setTimeout(resolve, 0));
}User Interaction Simulation
Click Events and Custom Event Assertions
it('dispatches select event when button clicked', () => {
const element = createElement('c-account-card', { is: AccountCard });
element.account = { Id: '001000000000001AAA', Name: 'Test Corp' };
document.body.appendChild(element);
const handler = jest.fn();
element.addEventListener('accountselect', handler);
element.shadowRoot.querySelector('[data-id="view-btn"]').click();
expect(handler).toHaveBeenCalledTimes(1);
expect(handler.mock.calls[0][0].detail).toEqual({
accountId: '001000000000001AAA', accountName: 'Test Corp'
});
});Input Value Changes
const input = element.shadowRoot.querySelector('lightning-input[type="search"]');
input.dispatchEvent(new CustomEvent('change', { detail: { value: 'Acme' } }));
await Promise.resolve();Toast Notification Testing
import { ShowToastEventName } from 'lightning/platformShowToastEvent';
it('shows success toast after save', async () => {
const toastHandler = jest.fn();
element.addEventListener(ShowToastEventName, toastHandler);
element.shadowRoot.querySelector('[data-id="save-btn"]').click();
await flushPromises();
expect(toastHandler).toHaveBeenCalledTimes(1);
expect(toastHandler.mock.calls[0][0].detail.variant).toBe('success');
});Navigation Mocking
it('navigates to record page on view', () => {
const { navigate } = require('lightning/navigation');
const element = createElement('c-account-card', { is: AccountCard });
element.account = { Id: '001000000000001AAA' };
document.body.appendChild(element);
element.shadowRoot.querySelector('[data-id="view"]').click();
expect(navigate).toHaveBeenCalledWith(
expect.objectContaining({
type: 'standard__recordPage',
attributes: expect.objectContaining({ recordId: '001000000000001AAA' })
})
);
});What to Test vs What Not to Test
Test These
- Business logic in computed properties and event handlers
- Correct Apex method called with correct arguments
- Component response to wire data and errors
- User interactions and their effects on component state
- Edge cases: empty arrays, null values, error states
Do Not Test These
- LWC framework behavior (that
for:eachrenders items) - Base component rendering (that
lightning-buttonshows text) - CSS styling or implementation details (internal variable names)
Spring '26: Local Component Testing
Test LWC components in isolation without deploying to a scratch org.
npm install --save-dev @lwc/jest-preset
npx lwc-jest --watchAll=falseTypeScript LWC Testing (Spring '26 Experimental)
Spring '26 introduces experimental TypeScript support for LWC. Test .ts component files with these adjustments.
Jest Configuration
// jest.config.js — add ts transform
const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config');
module.exports = {
...jestConfig,
transform: {
...jestConfig.transform,
'^.+\\.ts$': ['@swc/jest'] // or 'ts-jest'
},
moduleFileExtensions: ['ts', 'js', 'html'],
testMatch: ['**/__tests__/**/*.test.(js|ts)']
};Install: npm install --save-dev @swc/jest @swc/core (faster) or npm install --save-dev ts-jest typescript.
Typed Wire Adapter Mocking
import { createElement } from 'lwc';
import AccountList from 'c/accountList';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
jest.mock('@salesforce/apex/AccountController.getAccounts',
() => ({ default: jest.fn() }), { virtual: true });
const mockGetAccounts = getAccounts as jest.MockedFunction<typeof getAccounts>;
describe('c-account-list (TypeScript)', () => {
afterEach(() => {
while (document.body.firstChild) document.body.removeChild(document.body.firstChild);
jest.clearAllMocks();
});
it('renders typed account data', async () => {
mockGetAccounts.mockResolvedValue([
{ Id: '001xx0001', Name: 'Typed Corp', Industry: 'Technology' }
]);
const element = createElement('c-account-list', { is: AccountList });
document.body.appendChild(element);
await Promise.resolve();
await Promise.resolve();
const rows = element.shadowRoot.querySelectorAll('.account-row');
expect(rows).toHaveLength(1);
});
});Typed Event Detail Assertions
it('dispatches typed custom event', () => {
const element = createElement('c-account-card', { is: AccountCard });
document.body.appendChild(element);
const handler = jest.fn();
element.addEventListener('select', handler);
element.shadowRoot.querySelector('[data-id="select-btn"]').click();
const detail = handler.mock.calls[0][0].detail as { accountId: string };
expect(detail.accountId).toBeDefined();
});Note: TypeScript LWC support is experimental in Spring '26. Type definitions and tooling may change in future releases. Pin
@salesforce/sfdx-lwc-jestto a known-good version.
Related
Guardrails
- sf-testing-constraints — Enforced rules for test coverage, assertions, and test data patterns
Agents
- sf-lwc-agent — For interactive, in-depth LWC review guidance
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

