JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 12
  • Score
    100M100P100Q79379F
  • License MIT

Effortless, stable test IDs for Angular apps. Automatically injects data-qcauto attributes for QA and test automation teams without cluttering templates.

Package Exports

  • ng-qcauto
  • ng-qcauto/package.json

Readme

πŸ“˜ ng-qcauto

Effortless, stable test IDs for Angular apps, controlled by testers β€” not code.

npm version License: MIT


πŸ“– 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:

  • /dashboard route β†’ qc_dashboard_button_abc123
  • /users/profile route β†’ qc_users_profile_input_xyz789
  • Root / route β†’ qc_home_form_loginForm

Identifier Logic:

  1. If element has data-qc-key β†’ used directly (qc_dashboard_li_42)
  2. Else if element has id β†’ reused (qc_dashboard_form_loginForm)
  3. 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:

  1. Elements with QC IDs show a pointer cursor πŸ‘†
  2. Right-click any element to copy its QC ID
  3. A toast notification appears: βœ“ qc_dashboard_button_1k9d2
  4. 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 localStorage as qcAuto-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: MutationObserver handles only new nodes
  • Optimized:
    • Skips already tagged nodes
    • Filters by config before hashing
    • Uses data-qc-key for 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.



πŸ“œ License

MIT Β© 2025 – Kareem Mostafa


Made with ❀️ for QA Engineers and Test Automation Teams