
Security News
pnpm 11.5 Adds Support for Recognizing npm Staged Publishes
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.
@leaflink/dom-testing-utils
Advanced tools
Leaflink repository to manage test utilities to be shared across front-end applications.
Requirements: Node.js >= 24
pnpm add -D @leaflink/dom-testing-utils
Releases are fully automated using semantic-release. When changes are pushed to a release branch (main, alpha, beta, canary, next, next-major, or [0-9]+.x), the CI workflow:
pnpm buildCommit types that trigger releases: feat, fix, perf, and revert. Other types (e.g. docs, chore) appear in changelogs but do not bump the version on their own.
For detailed commit conventions and manual release instructions, see CONTRIBUTING.md.
Dependency updates are handled by self-hosted Renovate, invoked from .github/workflows/renovate.yml using a PAT (RENOVATE_PAT) issued from the leaflink-automation account. Configuration lives in .github/renovate.json5. Tracking ticket: MKPL-1055.
minimumReleaseAge) before Renovate will propose it — buffer against compromised/withdrawn publishes.| Source | Update type | PR shape | Auto-merge |
|---|---|---|---|
devDependencies | any | grouped as one PR (chore(deps-dev): update dev dependencies) | ✅ |
dependencies / peerDependencies | patch | individual PR (chore(deps): ...) | ✅ |
dependencies / peerDependencies | minor / major | individual PR (chore(deps): ...) | ❌ awaits review |
github-actions | patch / minor / digest / pin | individual PR | ✅ |
github-actions | major | individual PR | ❌ awaits review |
Lockfile maintenance (transitive refresh of pnpm-lock.yaml) | weekly | Refresh ... (chore(deps-dev)) | ✅ |
Renovate PR bodies all link back to MKPL-1055 via prBodyNotes.
main requires quality-checks + release status checks and a CODEOWNERS review. The leaflink-automation account (whose PAT Renovate uses) is in main's bypass_pull_request_allowances, but Renovate itself won't call the merge API while the PR shows as BLOCKED in GitHub's API — even though the bypass would let it through.
To work around this, .github/workflows/renovate-merge.yml performs the merge on Renovate's behalf:
automerge: true adds addLabels: ['automerge'], so the label is the single source of truth for "Renovate intended to auto-merge this."PR Quality Checks and Release run on the PR.Renovate Merge workflow is triggered via workflow_run. It:
leaflink-automation with the automerge label,leaflink-automation.BLOCKED status.Human PRs are unaffected — they still require CODEOWNERS review.
The required-checks list in renovate-merge.yml (REQUIRED_CHECKS) is hard-coded and must stay in sync with branch protection on main.
dependencies) — single source of truth for what Renovate sees, what it skipped, and why.Renovate in the Actions tab — workflow_dispatch with logLevel: debug to debug Renovate itself.Renovate Merge in the Actions tab — shows which PRs were merged or skipped (with reason) when CI completed. workflow_dispatch for manual recovery.config:recommended is the base preset; any unspecified behavior comes from there.In your test files you can import utility functions.
import {
waitForLoading,
cleanupDropdowns,
assertAndDismissNoty,
cleanupNoty,
createFixtureGenerator,
} from '@leaflink/dom-testing-utils';
it('...', () => {
cleanupNoty();
});
Import @leaflink/dom-testing-utils/setup-env once (for instance in your tests setup file) and you're good to go:
Note:
@testing-library/jest-domis auto-imported from@leaflink/dom-testing-utilsso you don't have to.
// In your own setup-env.ts (or any other name)
import '@leaflink/dom-testing-utils/setup-env'
// DON'T import `@testing-library/jest-dom` is auto imported from dom-testing-utils
// In vite.config.ts add (if you haven't already)
setupFiles: ['tests/setup-env.js'],
// In jest.config.js add (if you haven't already)
setupFilesAfterEnv: ['<rootDir>/tests/setup-env.js']
This will be run once before each test file. See https://vitest.dev/config/#setupfiles.
Add the following import to your test config:
// In vite.config.ts add
globalSetup: ['node_modules/@leaflink/dom-testing-utils/dist/global-setup.js'],
// In jest.config.js add
globalSetup: ['<rootDir>/node_modules/@leaflink/dom-testing-utils/dist/global-setup.js']
This will run once before everything. See https://vitest.dev/config/#globalsetup.
cleanupNotyHelper method to remove all noty alerts from the DOM.
Parameters: None
Returns: void
waitForLoadingUtility that waits for all loading elements to be removed from the DOM. The data-test argument defaults to ll-loading or loading-spinner if testId is not specified.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| testId | string | ll-loading && loading-spinner | The data test ID to target. |
| timeout | number | 2000 | How long to wait for loading elements to be removed |
| failIfNull | boolean | false | Throws an error if no loading elements are found |
Returns: Promise<void>
Will resolve if the loaders get removed before the timeout. Otherwise, will throw an error if the loaders are still in the DOM by the end of the timeout.
Setting failIfNull to true will cause an error to be thrown if no loading spinners are initially found in the DOM.
cleanupDropdownsHelper method to remove all floating Stash Dropdown elements from the DOM.
Parameters: None
Returns: void
assertAndDismissNotyHelper to assert and manually dismiss a notification. This is useful in scenarios where cleanupNoty() does not work as expected, such as when validating error messages in test suites.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| text | string | Required | Expected notification text. |
Returns: void
getByDescriptionTermFinds the first HTML element with the role "definition" (DD) that matches the specified text for the description term.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| text | string | RegExp | Required | Expected description term text or regex |
Returns: HTMLElement | undefined - The first matching description detail element or undefined if no match is found.
getAllByDescriptionTermQueries and returns an array of HTML elements with the role "definition" (DD) that matches the specified text of a description term.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| textMatch | string | RegExp | Required | The text to match within the HTML elements. It can be a string or a regular expression. |
Returns: HTMLElement[] - An array of HTML description detail elements that match the given text.
getSelectedOptionFinds the first selected HTML element with the role "definition" (LI) "listitem" inside the specified select element.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| element | HTMLSelectElement | Required | Stash Select element to be checked. |
| selectedClass | string | 'is-selected' | Selected class added on selected items |
| options | ByRoleOptions | null | getAllByRole() options values using ByRoleOptions type |
Returns: HTMLElement | undefined - The first selected HTML listitem element or undefined if no match is found.
getSelectedOptionsFinds all the selected HTML elements with the role "definition" (LI) "listitem" inside the specified select element.
| Parameters | Type | Default | Summary |
|---|---|---|---|
| element | HTMLSelectElement | Required | Stash Select element to be checked. |
| selectedClass | string | 'is-selected' | Selected class added on selected items |
| options | ByRoleOptions | null | getAllByRole() options values using ByRoleOptions type |
Returns: HTMLElement[] - An array of selected HTML listitem elements.
createFixtureGeneratorHigher order function that takes a method whose responsibility is to create a single data fixture object and returns a new generator function that allows you to create 1 or more of those fixtures. Fixture generator function that's returned supports passing optional num and overrides params.
| Parameters | Type | Default | Summary |
|---|---|---|---|
fixtureFn | function | Required | Method that generates and returns a single data object. |
Returns
(num?, overrides?) => Array<{[key: string]: any}> | {[key: string]: any}
// OR
(overrides?) => {[key: string]: any}
A new generator function that accepts a number & overrides where:
num = The number of fake data objects to generate. Defaults to 1overrides = Specific attributes you want to override in each data fixture object.When calling the returned function, you'll get an array OR object of fixture data (It will be a ** single object** if num = 1).
Examples
Quick example:
const generateInvoice = (overrides) => ({
id: uuid(),
balance: 15799,
classification: 'Adult Use',
...overrides,
});
const generateInvoices = createFixtureGenerator(generateInvoice);
generateInvoices();
// => Single invoice object
generateInvoices(1);
// => Single invoice object
generateInvoices(1, { foo: 'bar' });
// => Single invoice object, override `foo` to equal `'bar'`
generateInvoices({ foo: 'bar' });
// => Single invoice object, override `foo` to equal `'bar'`
generateInvoices(10);
// => Array of 10 invoice objects
generateInvoices(10, { foo: 'bar' });
// => Array of 10 invoice objects, override `foo` to equal `'bar'` in each
Full example:
// tests/fixtures/products.ts
import { faker } from '@faker-js/faker';
import { createFixtureGenerator } from '@leaflink/dom-testing-utils';
export const generateProduct = (overrides = {}) => ({
sku: git.commitSha(),
name: faker.commerce.productName(),
quantity: faker.random.number(100),
cases: faker.random.number(10),
...overrides,
});
export default createFixtureGenerator(generateProduct);
// services/api/products.ts
import generateProducts from '@/tests/fixtures/products';
// ...
const mockProducts = generateProducts(10, { cases: 25 });
// ...
In order to mock API endpoints that your tests interact with, you can get a set of mocking functions from createMockApiUtils.
import { createMockApiUtils } from '@leaflink/dom-testing-utils';
import yourServer from './server.ts';
const {
mockGetData,
mockGetEndpoint,
mockPatchData,
// etc.
} = createMockApiUtils(yourServer);
There are two flavors of mocking utility functions:
To mock an endpoint with simple return data
mockGetData('/relative-url', myMockObj);
or you can customize the response
mockGetEndpoint('/relative-url', (req, res, ctx) => {
if (someConditional()) {
HttpResponse.json({ foo: 'bar' });
} else {
HttpResponse.json({ foo: 'baz' });
}
});
FAQs
Frontend DOM testing utilities
The npm package @leaflink/dom-testing-utils receives a total of 2,392 weekly downloads. As such, @leaflink/dom-testing-utils popularity was classified as popular.
We found that @leaflink/dom-testing-utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.