UNPKG

ng-qcauto

Version:

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

317 lines (234 loc) β€’ 9.98 kB
# πŸ“˜ ng-qcauto **Effortless, stable test IDs for Angular apps, controlled by testers β€” not code.** [![npm version](https://img.shields.io/npm/v/ng-qcauto.svg)](https://www.npmjs.com/package/ng-qcauto) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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 ```bash npm install ng-qcauto ``` --- ## πŸš€ Usage ### πŸ”Ή Angular v14 and Below (Modules) For module-bootstrapped apps: ```ts // 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: ```ts // 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: ```js 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 ```html <!-- 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 ```html <!-- 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: ```js // 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: ```js 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 ```js // 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: ```js 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 ```ts // 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 // 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: ```ts 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](https://github.com/KareemMostafa77/qc-auto-package/blob/master/projects/ng-qcauto/CHANGELOG.md) for version history. --- ## πŸ”— Links - **GitHub Repository**: [qc-auto-package](https://github.com/KareemMostafa77/qc-auto-package) - **npm Package**: [ng-qcauto](https://www.npmjs.com/package/ng-qcauto) - **Demo**: [GitHub Pages](https://kareemmostafa77.github.io/qc-auto-package/) - **Issues**: [Report a bug](https://github.com/KareemMostafa77/qc-auto-package/issues) --- ## πŸ“œ License MIT Β© 2025 – Kareem Mostafa --- **Made with ❀️ for QA Engineers and Test Automation Teams**