Package Exports
- ng-qcauto
- ng-qcauto/package.json
Readme
π ng-qcauto
Effortless, stable test IDs for Angular apps, controlled by testers β not code.
π Overview
ng-qcauto is an Angular utility library that automatically injects stable data-qcauto attributes into DOM elements.
It empowers QA and test automation teams by providing deterministic, human-friendly selectors without requiring developers to clutter templates with data-testid.
β¨ Key Features
- π Automatic injection β works globally, no directives or template edits
- π― Configurable β track elements by tag, class, or ID
- π Route-based stable IDs β IDs include route path for better organization
- β¨οΈ Ctrl+Q Modal β easy configuration interface without DevTools
- π±οΈ Right-click to Copy β quickly copy QC IDs during testing (optional)
- π§βπ€βπ§ Tester-friendly β configuration lives in
localStorage, manageable via modal - π Version-based migration β automatic cleanup when upgrading package versions
- π¦ Test-only mode β enable in dev/staging, disable in prod
- β‘ Lightweight β observer-based, minimal performance impact
- π Angular v14 and below + v15+ support β works in both module-based and standalone bootstraps
π How IDs Are Generated
IDs follow this pattern: qc_{route}_{tag}_{identifier}
Examples:
/dashboardroute βqc_dashboard_button_abc123/users/profileroute βqc_users_profile_input_xyz789- Root
/route βqc_home_form_loginForm
Identifier Logic:
- If element has
data-qc-keyβ used directly (qc_dashboard_li_42) - Else if element has
idβ reused (qc_dashboard_form_loginForm) - Else β deterministic hash (
qc_dashboard_button_1k9d2)
IDs remain stable across reloads as long as route and structure don't change.
π Angular Version Support
| Angular Version | Supported | Setup Type |
|---|---|---|
| v15+ | β Yes | Standalone bootstrap (bootstrapApplication) |
| v14 and below | β Yes | Module bootstrap (bootstrapModule(AppModule)) |
π¦ Installation
npm install ng-qcautoπ Usage
πΉ Angular v14 and Below (Modules)
For module-bootstrapped apps:
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { initQcAutoGlobal } from 'ng-qcauto';
platformBrowserDynamic()
.bootstrapModule(AppModule)
.then(() => initQcAutoGlobal()) // init after Angular bootstraps
.catch(err => console.error(err));πΉ Angular v15+ (Standalone)
For standalone-bootstrapped apps:
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { initQcAutoGlobal } from 'ng-qcauto';
bootstrapApplication(AppComponent).then(() => {
initQcAutoGlobal(); // init after bootstrap
});π§βπ» Tester Workflow
ng-qcauto reads its configuration from localStorage.
1οΈβ£ Open Configuration Modal
Press Ctrl+Q (or Cmd+Q on Mac) anywhere in the app to open the configuration modal:
βββββββββββββββββββββββββββββββββββββββ
β QC Auto Configuration β β
βββββββββββββββββββββββββββββββββββββββ€
β Tags: button, input, a β
β Classes: btn-primary β
β IDs: saveBtn β
β β Enable Click-to-Copy QC IDs β
β β
β [Save & Reload] [Cancel] β
βββββββββββββββββββββββββββββββββββββββOR use DevTools Console:
localStorage.setItem('qcAuto-tags', JSON.stringify(['button','input','a']));
localStorage.setItem('qcAuto-classes', JSON.stringify(['btn-primary']));
localStorage.setItem('qcAuto-ids', JSON.stringify(['saveBtn']));
localStorage.setItem('qcAuto-clickToCopy', 'true');
location.reload();2οΈβ£ Example Template
<!-- On /dashboard route -->
<button>Save</button>
<button class="btn-primary">Submit</button>
<form id="loginForm"> ... </form>
<ul>
<li *ngFor="let user of users" [attr.data-qc-key]="user.id">
{{ user.name }}
</li>
</ul>3οΈβ£ After Render
<!-- On /dashboard route -->
<button data-qcauto="qc_dashboard_button_1k9d2">Save</button>
<button class="btn-primary" data-qcauto="qc_dashboard_button_btn-primary">Submit</button>
<form id="loginForm" data-qcauto="qc_dashboard_form_loginForm"> ... </form>
<li data-qc-key="42" data-qcauto="qc_dashboard_li_42">John Doe</li>4οΈβ£ Copy QC IDs (NEW!)
When Click-to-Copy is enabled:
- Elements with QC IDs show a pointer cursor π
- Right-click any element to copy its QC ID
- A toast notification appears:
β qc_dashboard_button_1k9d2 - Paste anywhere:
Ctrl+V
π How IDs Are Generated
- If element has
data-qc-keyβ used directly (qc_li_42). - Else if element has
idβ reused (qc_form_loginForm). - Else β deterministic hash (
qc_button_1k9d2).
IDs remain stable across reloads as long as structure doesnβt change.
βοΈ Configuration Reference
LocalStorage Keys
qcAuto-tagsβ Array of tag names (e.g.['button','input'])qcAuto-classesβ Array of class names (e.g.['btn-primary'])qcAuto-idsβ Array of element IDs (e.g.['loginForm'])qcAuto-clickToCopyβ Boolean string ('true'or'false') for right-click copy mode
β¨οΈ Keyboard Shortcut
- Ctrl+Q (Windows/Linux) or Cmd+Q (Mac) β Opens configuration modal
- Press again to close modal
π Version-Based Configuration
ng-qcauto automatically manages configuration updates when you upgrade the package:
- Automatic cleanup: When the package version changes, all old
qcAuto-*localStorage items are cleared - Fresh defaults: After cleanup, new default configuration is applied
- No manual migration: Users don't need to manually clear old settings
- Version tracking: Current version is stored in
localStorageasqcAuto-version
This ensures a clean state after updates and prevents conflicts from old configuration formats.
π Reset Config
To manually reset configuration to defaults:
// Clear all qcAuto settings (will trigger fresh defaults on reload)
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i);
if (key && key.startsWith('qcAuto-')) {
localStorage.removeItem(key);
}
}
location.reload();Or reset to specific values:
localStorage.setItem('qcAuto-tags', JSON.stringify(['button', 'input', 'a']));
localStorage.setItem('qcAuto-classes', JSON.stringify([]));
localStorage.setItem('qcAuto-ids', JSON.stringify([]));
localStorage.setItem('qcAuto-clickToCopy', 'false');
location.reload();π§ͺ Testing Examples
Cypress
// Full ID
cy.get('[data-qcauto="qc_dashboard_form_loginForm"]').should('be.visible');
// Pattern matching (all buttons on dashboard)
cy.get('[data-qcauto^="qc_dashboard_button"]').click();
// By route prefix
cy.get('[data-qcauto^="qc_users_profile"]').should('exist');Custom command:
Cypress.Commands.add('qc', selector =>
cy.get(`[data-qcauto="${selector}"]`)
);
// Usage
cy.qc('qc_dashboard_form_loginForm').submit();
cy.qc('qc_users_profile_button_save').click();Playwright
// Direct selector
await page.locator('[data-qcauto="qc_dashboard_li_42"]').click();
// Route-based pattern
await page.locator('[data-qcauto^="qc_checkout"]').count();Selenium
// Java
WebElement element = driver.findElement(
By.cssSelector("[data-qcauto='qc_dashboard_button_submit']"));
element.click();π‘ Test-Only Mode
To disable in production, guard init with environment flags:
import { environment } from './environments/environment';
import { initQcAutoGlobal } from 'ng-qcauto';
bootstrapApplication(AppComponent).then(() => {
if (!environment.production) {
initQcAutoGlobal();
}
});β‘ Performance Notes
- Startup: one-time DOM scan (few ms even for large apps)
- Runtime:
MutationObserverhandles only new nodes - Optimized:
- Skips already tagged nodes
- Filters by config before hashing
- Uses
data-qc-keyfor list stability
Overhead is negligible compared to Angular rendering.
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
π Changelog
See CHANGELOG.md for version history.
π Links
- GitHub Repository: qc-auto-package
- npm Package: ng-qcauto
- Demo: GitHub Pages
- Issues: Report a bug
π License
MIT Β© 2025 β Kareem Mostafa
Made with β€οΈ for QA Engineers and Test Automation Teams