ngx-auto-test-id
v0.1.0
Published
Automatically generate stable, deterministic data-test-id attributes for Angular components
Maintainers
Readme
ngx-auto-test-id
Automatically generate stable, deterministic data-test-id attributes on interactive DOM elements in Angular applications. Your e2e test team can use page.getByTestId() in Playwright or Cypress without developers manually adding test IDs.
Installation
npm install ngx-auto-test-idQuick Start
1. Add the provider to your app config
import { provideAutoTestIds } from 'ngx-auto-test-id';
export const appConfig: ApplicationConfig = {
providers: [
provideAutoTestIds(),
],
};2. Import the directive in each component
import { AutoTestIdDirective } from 'ngx-auto-test-id';
@Component({
selector: 'app-my-component',
imports: [AutoTestIdDirective],
template: `
<button>Save</button>
<input name="email">
<a href="/docs">Documentation</a>
`,
})
export class MyComponent {}The directive automatically matches: button, a, input, select, textarea, and elements with a role attribute.
3. Generated IDs
The template above produces:
<button data-test-id="app-my-component__button--save">Save</button>
<input data-test-id="app-my-component__input--email" name="email">
<a data-test-id="app-my-component__a--documentation" href="/docs">Documentation</a>ID Format
[prefix]__[tag]--[differentiator][--siblingIndex]| Part | Source |
|------|--------|
| prefix | Nearest custom element ancestor tag name (e.g. app-my-component), or root if none found |
| tag | Element tag name (e.g. button, input, a) |
| differentiator | First non-empty value from: textContent, aria-label, formcontrolname, name attribute, or sibling index |
| siblingIndex | Appended when multiple siblings of the same tag exist in the same parent |
Configuration
provideAutoTestIds({
attribute: 'data-qa', // default: 'data-test-id'
selectors: ['button', 'a', 'input', '[role]'], // default: button, a, input, select, textarea, [role]
});Disambiguation with testIdPrefix
When using the same component multiple times, use [testIdPrefix] to produce unique IDs:
import { AutoTestIdDirective, TestIdPrefixDirective } from 'ngx-auto-test-id';
@Component({
imports: [AutoTestIdDirective, TestIdPrefixDirective],
template: `
<div testIdPrefix="shipping">
<app-address-form />
</div>
<div testIdPrefix="billing">
<app-address-form />
</div>
`,
})
export class CheckoutComponent {}Elements inside the shipping div get prefix shipping__..., billing gets billing__....
Manual Override
Elements with an existing data-test-id attribute are never overwritten:
<button data-test-id="custom-checkout-btn">Checkout</button>
<!-- Stays as "custom-checkout-btn" -->Playwright / Cypress Configuration
Since this library uses data-test-id (not Playwright's default data-testid), configure your test framework:
Playwright (playwright.config.ts):
export default defineConfig({
use: {
testIdAttribute: 'data-test-id',
},
});Cypress (cypress/support/commands.ts):
Cypress.SelectorPlayground.defaults({
selectorPriority: ['data-test-id'],
});Then in your tests:
// Playwright
await page.getByTestId('app-login__button--submit').click();
// Cypress
cy.get('[data-test-id="app-login__button--submit"]').click();Limitations
- Read-once: IDs are generated once after the first render via
afterNextRender(). Dynamically added elements after initial render won't get IDs automatically. - i18n: Different locales produce different IDs if button text changes between languages.
- No
@forcontext: The directive cannot detect@forloop$index(Angular has no public API for this). It differentiates by text content and appends sibling index. - Prefix propagation:
[testIdPrefix]propagates into child components via Angular's DI element injector. Place it on a wrapper element to control scope.
