https://github.com/KareemMostafa77/qc-auto-package
https://github.com/KareemMostafa77/qc-auto-package
Last synced: 7 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/KareemMostafa77/qc-auto-package
- Owner: KareemMostafa77
- Created: 2025-09-27T18:28:24.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2025-10-29T09:56:41.000Z (about 1 month ago)
- Last Synced: 2025-10-29T11:42:16.353Z (about 1 month ago)
- Language: TypeScript
- Size: 280 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-angular - qc-auto-package - Effortless, reliable test IDs for Angular β managed by testers, independent of code. (Testing / Helpers)
- fucking-awesome-angular - qc-auto-package - Effortless, reliable test IDs for Angular β managed by testers, independent of code. (Testing / Helpers)
README
# π `ng-qcauto` v2.0
**Effortless, stable test IDs for Angular apps, controlled by testers β not code.**
---
## π How IDs Are Generated (v2.0)
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. 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.
- π§βπ€βπ§ **Tester-friendly** β configuration lives in `localStorage`, manageable via modal.
- π¦ **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.
---
## π 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
Save
Submit
...
-
{{ user.name }}
```
### 3οΈβ£ After Render
```html
Save
Submit
...
```
### 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
### Reset Config
```js
localStorage.setItem('qcAuto-tags', JSON.stringify([]));
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.
---
## π License
MIT Β© 2025 β Kareem Mostafa