@keber/qa-framework 1.11.3 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/docs/architecture.md +30 -26
- package/docs/decisions/lane-heartbeat-caller.md +86 -0
- package/docs/final-report.md +23 -23
- package/docs/folder-structure-guide.md +100 -80
- package/docs/installation.md +35 -7
- package/docs/skills-architecture.md +23 -23
- package/docs/spec-driven-philosophy.md +9 -9
- package/docs/usage-with-agent.md +83 -9
- package/docs/visuals/diag-01-ejecutivo-1.png +0 -0
- package/docs/visuals/diag-02-tactico-1.png +0 -0
- package/docs/visuals/diag-03-tecnico-1.png +0 -0
- package/docs/visuals/info-01-ejecutivo-1.png +0 -0
- package/docs/visuals/info-02-tactico-1.png +0 -0
- package/docs/visuals/info-03-tecnico-1.png +0 -0
- package/docs/visuals/pres-01-ejecutivo-1.png +0 -0
- package/docs/visuals/pres-02-tactico-1.png +0 -0
- package/docs/visuals/pres-03-tecnico-1.png +0 -0
- package/docs/visuals/qa-01-problema-1.png +0 -0
- package/docs/visuals/qa-02-que-es-1.png +0 -0
- package/docs/visuals/qa-03-pipeline-1.png +0 -0
- package/docs/visuals/qa-04-6-archivos-1.png +0 -0
- package/docs/visuals/qa-05-humano-1.png +0 -0
- package/docs/visuals/qa-06-agente-1.png +0 -0
- package/docs/visuals/qa-07-skills-arch-1.png +0 -0
- package/docs/visuals/qa-08-traceability-1.png +0 -0
- package/docs/visuals/qa-en-01-problem-1.png +0 -0
- package/docs/visuals/qa-en-02-what-is-1.png +0 -0
- package/docs/visuals/qa-en-03-pipeline-1.png +0 -0
- package/docs/visuals/qa-en-04-6files-1.png +0 -0
- package/docs/visuals/qa-en-05-human-1.png +0 -0
- package/docs/visuals/qa-en-06-agent-1.png +0 -0
- package/docs/visuals/qa-en-07-skills-arch-1.png +0 -0
- package/docs/visuals/qa-en-08-traceability-1.png +0 -0
- package/examples/module-example/suppliers/00-inventory.md +1 -1
- package/examples/module-example/suppliers/suppliers-create.spec.ts +8 -8
- package/integrations/ado-powershell/README.md +1 -1
- package/integrations/ado-powershell/scripts/sync-ado-titles.ps1 +2 -2
- package/integrations/playwright/README.md +4 -4
- package/package.json +7 -3
- package/qa-framework.config.json +23 -2
- package/scripts/check-forbidden-chars.js +187 -0
- package/scripts/check-spanish-accents.js +159 -0
- package/scripts/cli.js +1 -1
- package/scripts/generate.js +2 -2
- package/scripts/init.js +55 -7
- package/scripts/lib/claude-agents.js +78 -0
- package/scripts/lib/claude-commands.js +56 -0
- package/scripts/sync-version.js +36 -0
- package/scripts/upgrade.js +356 -20
- package/scripts/validate.js +17 -17
- package/skills/qa-ado-integration/SKILL.md +19 -19
- package/skills/qa-ado-integration/references/scripts-and-config.md +6 -6
- package/skills/qa-automation/SKILL.md +49 -27
- package/skills/qa-automation/references/config-checklist.md +4 -4
- package/skills/qa-automation/references/dom-inspection-template.js +7 -7
- package/skills/qa-automation/references/patterns.md +20 -20
- package/skills/qa-automation/references/pom-template.md +161 -0
- package/skills/qa-maintenance/SKILL.md +11 -11
- package/skills/qa-maintenance/references/update-rules.md +8 -8
- package/skills/qa-module-analysis/SKILL.md +20 -19
- package/skills/qa-module-analysis/references/exploration-checklist.md +4 -4
- package/skills/qa-module-analysis/references/output-verification.md +69 -0
- package/skills/qa-module-analysis/references/spec-file-formats.md +14 -14
- package/skills/qa-spec-generation/SKILL.md +13 -7
- package/skills/qa-test-cases/SKILL.md +11 -81
- package/skills/qa-test-cases/references/test-case-template.md +5 -5
- package/skills/qa-test-plan/SKILL.md +22 -86
- package/skills/qa-test-plan/references/plan-de-pruebas-template.md +24 -24
- package/skills/qa-test-stabilization/SKILL.md +59 -30
- package/skills/qa-test-stabilization/references/classification-protocol.md +35 -35
- package/templates/agent-next-steps.md +10 -10
- package/templates/agents/qa-analisis.md +142 -0
- package/templates/agents/qa-asesoria.md +48 -0
- package/templates/agents/qa-informe-resultados.md +234 -0
- package/templates/agents/qa-plan.md +131 -0
- package/templates/automation-scaffold/.env.example +36 -6
- package/templates/automation-scaffold/fixtures/auth.ts +27 -5
- package/templates/automation-scaffold/fixtures/base.ts +23 -1
- package/templates/automation-scaffold/fixtures/test-helpers.ts +1 -1
- package/templates/automation-scaffold/global-setup.ts +117 -48
- package/templates/automation-scaffold/playwright.config.ts +72 -32
- package/templates/automation-scaffold/scripts/global-setup-guards.js +184 -0
- package/templates/automation-scaffold/scripts/lane-config.js +269 -0
- package/templates/automation-scaffold/scripts/lane-lock.js +589 -0
- package/templates/defect-report.md +17 -3
- package/templates/execution-report.md +9 -9
- package/templates/integration-scaffold/.env.example +1 -1
- package/templates/integration-scaffold/global-setup.ts +21 -1
- package/templates/integration-scaffold/playwright.config.ts +15 -1
- package/templates/qa-framework.instructions.md +8 -7
- package/templates/qa-framework.rules.md +53 -0
- package/templates/qa-readme.md +8 -8
- package/templates/session-summary.md +2 -2
- package/templates/specification/00-inventory.md +3 -3
- package/templates/specification/01-business-rules.md +6 -6
- package/templates/specification/02-workflows.md +1 -1
- package/templates/specification/03-roles-permissions.md +1 -1
- package/templates/specification/04-test-data.md +8 -8
- package/templates/specification/05-test-scenarios.md +5 -5
- package/templates/test-case.md +3 -3
- package/templates/test-data-guidelines.md +7 -7
- package/templates/test-plan-sprint.md +13 -13
- package/templates/test-plan.md +3 -3
- package/skills/qa-test-plan/references/priority-and-feasibility.md +0 -159
package/docs/usage-with-agent.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# docs/usage-with-agent.md
|
|
2
2
|
|
|
3
|
-
## Using `qa-framework` with an IDE Agent (GitHub Copilot)
|
|
3
|
+
## Using `qa-framework` with an IDE Agent (GitHub Copilot or Claude Code)
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
`init` and `upgrade` always generate artifacts for both GitHub Copilot and Claude Code
|
|
10
|
+
in parallel - there is no agent detection. This framework is designed so that either
|
|
11
|
+
IDE agent can:
|
|
10
12
|
|
|
11
13
|
1. Understand the QA structure by reading the agent instruction files
|
|
12
14
|
2. Navigate the `qa/` directory predictably
|
|
@@ -42,11 +44,11 @@ This framework is designed so that an IDE agent (such as GitHub Copilot in VS Co
|
|
|
42
44
|
|
|
43
45
|
---
|
|
44
46
|
|
|
45
|
-
## Setting Up Agent Instructions in VS Code
|
|
47
|
+
## Setting Up Agent Instructions in VS Code (GitHub Copilot)
|
|
46
48
|
|
|
47
|
-
### Option A
|
|
49
|
+
### Option A - Workspace instructions file (recommended)
|
|
48
50
|
|
|
49
|
-
`init` generates `.github/
|
|
51
|
+
`init` generates `.github/instructions/qa-framework.instructions.md` automatically. It contains the full
|
|
50
52
|
pipeline sequencer with 11 agent behavior rules, the QA pipeline table, and ADO detection.
|
|
51
53
|
No manual setup needed.
|
|
52
54
|
|
|
@@ -56,7 +58,7 @@ To regenerate after an upgrade:
|
|
|
56
58
|
npx qa-framework upgrade
|
|
57
59
|
```
|
|
58
60
|
|
|
59
|
-
### Option B
|
|
61
|
+
### Option B - Reference skills directly in the agent conversation
|
|
60
62
|
|
|
61
63
|
Paste a skill path into the Copilot chat:
|
|
62
64
|
|
|
@@ -64,7 +66,7 @@ Paste a skill path into the Copilot chat:
|
|
|
64
66
|
Read .github/skills/qa-module-analysis/SKILL.md and analyze the module at {URL}
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
### Option C
|
|
69
|
+
### Option C - Individual task prompts
|
|
68
70
|
|
|
69
71
|
For each major QA task, ask the agent to load the relevant skill:
|
|
70
72
|
|
|
@@ -75,6 +77,77 @@ tests for the submodule {name}
|
|
|
75
77
|
|
|
76
78
|
---
|
|
77
79
|
|
|
80
|
+
## Setting Up Agent Instructions in Claude Code
|
|
81
|
+
|
|
82
|
+
`init` and `upgrade` generate two Claude-Code-native artifacts automatically, no manual
|
|
83
|
+
setup needed:
|
|
84
|
+
|
|
85
|
+
- `.claude/rules/qa-framework.md` - the Claude Code equivalent of the Copilot
|
|
86
|
+
`.instructions.md` file. It has no frontmatter, which means it loads unconditionally in
|
|
87
|
+
every session (the same effect as `applyTo: '**'`). It contains the same 11 agent
|
|
88
|
+
behavior rules and pipeline table as the Copilot instructions, with every skill
|
|
89
|
+
reference expressed as a `/qa-{name}` slash command instead of "load this file".
|
|
90
|
+
- `.claude/commands/qa-{name}.md` - one thin wrapper per skill, generated dynamically
|
|
91
|
+
from whatever folders exist under `.github/skills/` at install/upgrade time. Each
|
|
92
|
+
wrapper is a single instruction: `Read the full skill at .github/skills/qa-{name}/SKILL.md
|
|
93
|
+
FIRST, then follow its instructions exactly`, plus the stage prerequisite. There is zero
|
|
94
|
+
duplication of skill content and therefore zero drift - if a skill is updated, every
|
|
95
|
+
command that points to it picks up the change with no regeneration needed.
|
|
96
|
+
|
|
97
|
+
### Using a command
|
|
98
|
+
|
|
99
|
+
In a Claude Code session, invoke the pipeline stage directly:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
/qa-module-analysis
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Claude reads `.github/skills/qa-module-analysis/SKILL.md` and follows it exactly, the
|
|
106
|
+
same way the Copilot pipeline table routes to that file.
|
|
107
|
+
|
|
108
|
+
### Regenerating after an upgrade
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx qa-framework upgrade
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This refreshes both `.claude/commands/qa-*.md` and `.claude/rules/qa-framework.md` the
|
|
115
|
+
same safe way it refreshes the Copilot artifacts (framework-owned, always overwritten
|
|
116
|
+
with the current version; nothing else under `.claude/` is touched).
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Optional: ANALISIS/PLAN Mode (Claude Code only, ADO-gated)
|
|
121
|
+
|
|
122
|
+
Some teams run a sprint-centric manual testing workflow in parallel with the 6-stage
|
|
123
|
+
pipeline: an analysis document, a test plan trace-linked to Azure DevOps work items,
|
|
124
|
+
ad-hoc QA advisory chat, and a sprint closing results report. This mode is:
|
|
125
|
+
|
|
126
|
+
- **Optional** - off by default.
|
|
127
|
+
- **Gated** by `integrations.azureDevOps.sprintCycle.enabled` in
|
|
128
|
+
`qa/qa-framework.config.json` (it requires Azure DevOps enabled, since three of the
|
|
129
|
+
four agents depend on ADO work items or an ADO-generated execution report).
|
|
130
|
+
- **Claude Code only, by design, not by omission.** Each agent is a Claude Code subagent
|
|
131
|
+
with its own `tools`/`model` frontmatter (e.g. `qa-asesoria` intentionally has no
|
|
132
|
+
`Write`/`Bash`). GitHub Copilot has no equivalent primitive, so no `.github/` artifact
|
|
133
|
+
is generated for this mode.
|
|
134
|
+
|
|
135
|
+
When enabled, `init`/`upgrade` generate four subagents under `.claude/agents/`:
|
|
136
|
+
|
|
137
|
+
| Agent | Purpose |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `qa-analisis.md` | Analysis document: test universe, automation feasibility, P0-P3 prioritization, excluded universe, traceability matrix |
|
|
140
|
+
| `qa-plan.md` | Default mode: executive summary + a trace-linked test case table, executable within the configured manual-testing timebox |
|
|
141
|
+
| `qa-asesoria.md` | Chat-only QA advisory (no `Write`/`Bash`) - answers point questions, redirects to `qa-plan`/`qa-analisis` when a full document is actually needed |
|
|
142
|
+
| `qa-informe-resultados.md` | Narrative sprint closing report built from an execution report already produced by the `qa-ado-integration` skill; updates incrementally, never overwrites prior sections |
|
|
143
|
+
|
|
144
|
+
Sprint duration, manual-testing timebox, date format, and timezone are parameterized
|
|
145
|
+
from `integrations.azureDevOps.sprintCycle` (with neutral defaults - 8-day sprint,
|
|
146
|
+
2-day timebox, `dd-mm-aaaa`, `America/Santiago` - when a field is omitted). See
|
|
147
|
+
[docs/installation.md](installation.md) for the config shape.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
78
151
|
## Typical Agent Session Workflow
|
|
79
152
|
|
|
80
153
|
### Session 1: Initial Module Analysis
|
|
@@ -124,7 +197,7 @@ tests for the submodule {name}
|
|
|
124
197
|
|
|
125
198
|
### DO
|
|
126
199
|
|
|
127
|
-
- Read `qa/AGENT-NEXT-STEPS.md` at the start of each conversation (auto-enforced by
|
|
200
|
+
- Read `qa/AGENT-NEXT-STEPS.md` at the start of each conversation (auto-enforced by `.github/instructions/qa-framework.instructions.md` for Copilot, or `.claude/rules/qa-framework.md` for Claude Code)
|
|
128
201
|
- Load the relevant skill from `.github/skills/` before starting any QA task
|
|
129
202
|
- Save all artifacts in the exact path specified by `qa/QA-STRUCTURE-GUIDE.md`
|
|
130
203
|
- Use TC-ID, RN-ID, FL-ID naming consistently
|
|
@@ -181,7 +254,8 @@ This performs:
|
|
|
181
254
|
## Continuing from a Previous Session
|
|
182
255
|
|
|
183
256
|
The agent reads `qa/AGENT-NEXT-STEPS.md` automatically at the start of every conversation
|
|
184
|
-
(enforced by `.github/
|
|
257
|
+
(enforced by rule 0 in `.github/instructions/qa-framework.instructions.md` for Copilot,
|
|
258
|
+
or `.claude/rules/qa-framework.md` for Claude Code). For additional context:
|
|
185
259
|
|
|
186
260
|
```
|
|
187
261
|
"Read qa/README.md and qa/memory/INDEX.md, then tell me where we left off."
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
import { test, expect } from '@playwright/test';
|
|
17
17
|
|
|
18
|
-
// EXEC_IDX: unique per minute-window
|
|
18
|
+
// EXEC_IDX: unique per minute-window - prevents data collisions between runs
|
|
19
19
|
const EXEC_IDX = Math.floor(Date.now() / 60_000) % 100_000;
|
|
20
20
|
|
|
21
21
|
test.describe('Suppliers > Create @P0', () => {
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* [TC-SUP-CR-001] Create supplier
|
|
24
|
+
* [TC-SUP-CR-001] Create supplier - happy path @P0
|
|
25
25
|
* Verifies the complete create flow for a valid supplier.
|
|
26
26
|
*/
|
|
27
|
-
test('[TC-SUP-CR-001] Create supplier
|
|
27
|
+
test('[TC-SUP-CR-001] Create supplier - happy path @P0', async ({ page }) => {
|
|
28
28
|
const supplierName = `QA-Supplier-${EXEC_IDX}`;
|
|
29
29
|
const supplierRut = `${EXEC_IDX}-K`;
|
|
30
30
|
const supplierEmail = `qa-supplier-${EXEC_IDX}@example.com`;
|
|
@@ -57,10 +57,10 @@ test.describe('Suppliers > Create @P0', () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* [TC-SUP-CR-002] Create supplier
|
|
60
|
+
* [TC-SUP-CR-002] Create supplier - required fields validation @P0
|
|
61
61
|
* Verifies that the form prevents submission when required fields are empty.
|
|
62
62
|
*/
|
|
63
|
-
test('[TC-SUP-CR-002] Create supplier
|
|
63
|
+
test('[TC-SUP-CR-002] Create supplier - required fields validation @P0', async ({ page }) => {
|
|
64
64
|
await page.goto('/suppliers');
|
|
65
65
|
await page.locator('button:has-text("New Supplier")').click();
|
|
66
66
|
await expect(page.locator('.supplier-modal')).toBeVisible();
|
|
@@ -79,12 +79,12 @@ test.describe('Suppliers > Create @P0', () => {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
* [TC-SUP-CR-003] Create supplier
|
|
82
|
+
* [TC-SUP-CR-003] Create supplier - duplicate RUT rejected @P0
|
|
83
83
|
*
|
|
84
|
-
* NOTE: test.skip active
|
|
84
|
+
* NOTE: test.skip active - DEF-001: Duplicate RUT check not enforced server-side.
|
|
85
85
|
* Reactivate when ADO #99001 is resolved.
|
|
86
86
|
*/
|
|
87
|
-
test('[TC-SUP-CR-003] Create supplier
|
|
87
|
+
test('[TC-SUP-CR-003] Create supplier - duplicate RUT rejected @P0', async ({ page }) => {
|
|
88
88
|
test.skip(true,
|
|
89
89
|
'DEF-001: Duplicate RUT validation not enforced. Reactivate when ADO #99001 is resolved.'
|
|
90
90
|
);
|
|
@@ -61,7 +61,7 @@ foreach ($entry in $mapping) {
|
|
|
61
61
|
Select-Object -First 1
|
|
62
62
|
|
|
63
63
|
if (-not $specFile) {
|
|
64
|
-
Write-Warning " [NOT FOUND] $($entry.specFile)
|
|
64
|
+
Write-Warning " [NOT FOUND] $($entry.specFile) - skipping WI $adoId"
|
|
65
65
|
$skipped++
|
|
66
66
|
continue
|
|
67
67
|
}
|
|
@@ -84,7 +84,7 @@ foreach ($entry in $mapping) {
|
|
|
84
84
|
|
|
85
85
|
if ($PSCmdlet.ShouldProcess("WI #$adoId", "Update title to: $newTitle")) {
|
|
86
86
|
Invoke-RestMethod -Method Patch -Uri $patchUrl -Headers $headers -Body $body | Out-Null
|
|
87
|
-
Write-Host " [SYNCED] WI #$adoId
|
|
87
|
+
Write-Host " [SYNCED] WI #$adoId -> $newTitle"
|
|
88
88
|
$synced++
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Integration
|
|
1
|
+
# Integration - Playwright
|
|
2
2
|
|
|
3
3
|
This directory documents how `@playwright/test` is configured within the
|
|
4
4
|
`keber/qa-framework` opinionated setup.
|
|
@@ -40,9 +40,9 @@ suite execution.
|
|
|
40
40
|
|
|
41
41
|
## Debugging checklist
|
|
42
42
|
|
|
43
|
-
1. `PWDEBUG=1 npx playwright test`
|
|
44
|
-
2. `--headed`
|
|
45
|
-
3. `--trace on`
|
|
43
|
+
1. `PWDEBUG=1 npx playwright test` - step through in inspector
|
|
44
|
+
2. `--headed` - watch the browser
|
|
45
|
+
3. `--trace on` - record full trace; open with `npx playwright show-trace`
|
|
46
46
|
4. Increase `actionTimeout` if the app has slow server-side rendering
|
|
47
47
|
5. Add `await page.waitForLoadState('networkidle')` before assertions on
|
|
48
48
|
dynamically loaded content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keber/qa-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Reusable spec-driven QA framework for IDE-agent-assisted automated testing. Installable as an npm package. Provides structure, templates, agent instructions, and optional integrations for Playwright and Azure DevOps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"qa",
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
"postinstall": "node scripts/init.js --skip-if-exists",
|
|
22
22
|
"init": "node scripts/cli.js init",
|
|
23
23
|
"generate": "node scripts/cli.js generate",
|
|
24
|
-
"validate": "node scripts/cli.js validate"
|
|
24
|
+
"validate": "node scripts/cli.js validate",
|
|
25
|
+
"check-accents": "node scripts/check-spanish-accents.js",
|
|
26
|
+
"check-chars": "node scripts/check-forbidden-chars.js",
|
|
27
|
+
"test": "node --test test/*.test.js",
|
|
28
|
+
"version": "node scripts/sync-version.js && git add README.md .github/copilot-instructions.md"
|
|
25
29
|
},
|
|
26
30
|
"files": [
|
|
27
31
|
"agent-instructions/",
|
|
@@ -36,7 +40,7 @@
|
|
|
36
40
|
"qa-framework.config.json"
|
|
37
41
|
],
|
|
38
42
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
43
|
+
"node": ">=20"
|
|
40
44
|
},
|
|
41
45
|
"peerDependencies": {
|
|
42
46
|
"@keber/ado-qa": ">=1.0.0",
|
package/qa-framework.config.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"testCaseIdPattern": "TC-{MODULE}-{SUBMODULE}-{NUM}",
|
|
28
28
|
"businessRuleIdPattern": "RN-{MODULE}-{NUM}",
|
|
29
29
|
"workflowIdPattern": "FL-{MODULE}-{NUM}",
|
|
30
|
-
"defectIdPattern": "DEF-{NUM}",
|
|
30
|
+
"defectIdPattern": "DEF-{PREFIX}-{NUM}",
|
|
31
31
|
"testNamingPattern": "[{ID}] {title} @{priority}",
|
|
32
32
|
"timestampFormat": "YYYY-MM-DD_HH-MM-SS",
|
|
33
33
|
"tcPriorityLevels": ["P0", "P1", "P2", "P3"]
|
|
@@ -42,6 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
],
|
|
44
44
|
|
|
45
|
+
"parallelLanes": {
|
|
46
|
+
"enabled": false,
|
|
47
|
+
"lanes": [
|
|
48
|
+
{
|
|
49
|
+
"id": "1",
|
|
50
|
+
"account": "QA_USER",
|
|
51
|
+
"storageState": ".auth/user-default.json",
|
|
52
|
+
"mcpNamespace": null,
|
|
53
|
+
"runnerProject": "chromium",
|
|
54
|
+
"capabilities": []
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
|
|
45
59
|
"integrations": {
|
|
46
60
|
"playwright": {
|
|
47
61
|
"enabled": false,
|
|
@@ -69,7 +83,14 @@
|
|
|
69
83
|
"variableGroup": "{{ADO_VARIABLE_GROUP}}",
|
|
70
84
|
"pipelineFile": "qa/08-azure-integration/pipelines/azure-pipeline-qa.yml",
|
|
71
85
|
"moduleRegistry": "qa/08-azure-integration/module-registry.json",
|
|
72
|
-
"reporterPackage": "@alex_neo/playwright-azure-reporter"
|
|
86
|
+
"reporterPackage": "@alex_neo/playwright-azure-reporter",
|
|
87
|
+
"sprintCycle": {
|
|
88
|
+
"enabled": false,
|
|
89
|
+
"sprintDurationDays": 8,
|
|
90
|
+
"manualTestingTimeboxDays": 2,
|
|
91
|
+
"dateFormat": "dd-mm-aaaa",
|
|
92
|
+
"timezone": "America/Santiago"
|
|
93
|
+
}
|
|
73
94
|
}
|
|
74
95
|
},
|
|
75
96
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-forbidden-chars.js
|
|
4
|
+
*
|
|
5
|
+
* Enforces the BLOCKING character-safety rule declared in
|
|
6
|
+
* .github/copilot-instructions.md: generated content must never carry typographic
|
|
7
|
+
* characters that survive a copy-paste but break on the way to a terminal, a CSV,
|
|
8
|
+
* a PowerShell script or an Azure DevOps field.
|
|
9
|
+
*
|
|
10
|
+
* Forbidden: em-dash (U+2014), en-dash (U+2013), horizontal ellipsis (U+2026),
|
|
11
|
+
* smart quotes (U+201C, U+201D, U+2018, U+2019) and arrows (U+2190-U+21FF).
|
|
12
|
+
* Each has an ASCII equivalent that renders identically everywhere.
|
|
13
|
+
*
|
|
14
|
+
* Latin Extended (U+00C0-U+024F) is explicitly NOT flagged: Spanish accents,
|
|
15
|
+
* n with tilde and u/o with umlaut are required orthography, not decoration.
|
|
16
|
+
* Emoji are not flagged either - they are outside the forbidden set.
|
|
17
|
+
*
|
|
18
|
+
* Unlike check-spanish-accents.js this scans .js and .ts as well as .md, because
|
|
19
|
+
* the generators under scripts/ embed these characters in template literals and
|
|
20
|
+
* console output that propagate into every consuming project.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* node scripts/check-forbidden-chars.js <file|dir> [...] check paths
|
|
24
|
+
* node scripts/check-forbidden-chars.js --staged check staged files
|
|
25
|
+
* node scripts/check-forbidden-chars.js --report <paths> print findings, never fail
|
|
26
|
+
*
|
|
27
|
+
* Exit codes: 0 = clean, 1 = at least one forbidden character, 2 = usage error.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
'use strict';
|
|
31
|
+
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const path = require('path');
|
|
34
|
+
const { execFileSync } = require('child_process');
|
|
35
|
+
|
|
36
|
+
// '.example' covers .env.example, which ships to consuming projects like any other
|
|
37
|
+
// template. A BOM or an em-dash there is not cosmetic: some .env parsers read the
|
|
38
|
+
// first key as QA_BASE_URL and silently fail to find it.
|
|
39
|
+
const EXTENSIONS = ['.md', '.js', '.ts', '.ps1', '.example'];
|
|
40
|
+
|
|
41
|
+
// Code point -> { name, replacement }. Arrows are handled by range below.
|
|
42
|
+
const FORBIDDEN = {
|
|
43
|
+
0x2014: { name: 'em dash', replacement: ' - ' },
|
|
44
|
+
0x2013: { name: 'en dash', replacement: ' - ' },
|
|
45
|
+
0x2026: { name: 'horizontal ellipsis', replacement: '...' },
|
|
46
|
+
0x201c: { name: 'left double quotation mark', replacement: '"' },
|
|
47
|
+
0x201d: { name: 'right double quotation mark', replacement: '"' },
|
|
48
|
+
0x2018: { name: 'left single quotation mark', replacement: "'" },
|
|
49
|
+
0x2019: { name: 'right single quotation mark', replacement: "'" },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ARROW_START = 0x2190;
|
|
53
|
+
const ARROW_END = 0x21ff;
|
|
54
|
+
|
|
55
|
+
// The two arrows with an obvious ASCII spelling; every other arrow in the block
|
|
56
|
+
// has no single equivalent, so the report asks for a rewrite instead of guessing.
|
|
57
|
+
const ARROW_REPLACEMENTS = {
|
|
58
|
+
0x2190: '<-',
|
|
59
|
+
0x2192: '->',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function describe(codePoint) {
|
|
63
|
+
if (FORBIDDEN[codePoint]) return FORBIDDEN[codePoint];
|
|
64
|
+
if (codePoint >= ARROW_START && codePoint <= ARROW_END) {
|
|
65
|
+
return {
|
|
66
|
+
name: 'arrow',
|
|
67
|
+
replacement: ARROW_REPLACEMENTS[codePoint] || 'an ASCII arrow such as -> or <-',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function hex(codePoint) {
|
|
74
|
+
return 'U+' + codePoint.toString(16).toUpperCase().padStart(4, '0');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Scan one file's text. Returns an array of findings; column is 1-based and
|
|
79
|
+
* counted in code points, so an astral character earlier on the line does not
|
|
80
|
+
* shift the reported position.
|
|
81
|
+
*/
|
|
82
|
+
function scanText(text, file) {
|
|
83
|
+
const findings = [];
|
|
84
|
+
const lines = text.split(/\r?\n/);
|
|
85
|
+
|
|
86
|
+
lines.forEach((line, lineIndex) => {
|
|
87
|
+
let column = 0;
|
|
88
|
+
for (const char of line) {
|
|
89
|
+
column += 1;
|
|
90
|
+
const codePoint = char.codePointAt(0);
|
|
91
|
+
const match = describe(codePoint);
|
|
92
|
+
if (!match) continue;
|
|
93
|
+
findings.push({
|
|
94
|
+
file,
|
|
95
|
+
line: lineIndex + 1,
|
|
96
|
+
column,
|
|
97
|
+
char,
|
|
98
|
+
codePoint,
|
|
99
|
+
name: match.name,
|
|
100
|
+
replacement: match.replacement,
|
|
101
|
+
text: line.trim(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return findings;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function scanFile(file) {
|
|
110
|
+
return scanText(fs.readFileSync(file, 'utf8'), file);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Archived copies of content this framework shipped in an earlier release. Their bytes
|
|
114
|
+
// are the point: test/upgrade.test.js matches them against SHIPPED_SCAFFOLD_HASHES to
|
|
115
|
+
// prove a real legacy installation is recognised as pristine. Rewriting a character in
|
|
116
|
+
// one of them changes its hash and silently voids that proof, so they are read-only
|
|
117
|
+
// history rather than authored content and the rule does not apply to them.
|
|
118
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'legacy-v1.11.3']);
|
|
119
|
+
|
|
120
|
+
function collect(target) {
|
|
121
|
+
const stat = fs.statSync(target);
|
|
122
|
+
if (stat.isFile()) return EXTENSIONS.includes(path.extname(target)) ? [target] : [];
|
|
123
|
+
return fs.readdirSync(target).flatMap((entry) => {
|
|
124
|
+
if (SKIP_DIRS.has(entry)) return [];
|
|
125
|
+
return collect(path.join(target, entry));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function stagedFiles() {
|
|
130
|
+
const out = execFileSync(
|
|
131
|
+
'git',
|
|
132
|
+
['diff', '--cached', '--name-only', '--diff-filter=ACM'],
|
|
133
|
+
{ encoding: 'utf8' }
|
|
134
|
+
);
|
|
135
|
+
return out
|
|
136
|
+
.split('\n')
|
|
137
|
+
.map((s) => s.trim())
|
|
138
|
+
.filter((s) => s && EXTENSIONS.includes(path.extname(s)) && fs.existsSync(s));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function main(argv) {
|
|
142
|
+
const reportOnly = argv.includes('--report');
|
|
143
|
+
const args = argv.filter((a) => a !== '--report');
|
|
144
|
+
|
|
145
|
+
let files;
|
|
146
|
+
if (args.includes('--staged')) {
|
|
147
|
+
files = stagedFiles();
|
|
148
|
+
} else if (args.length) {
|
|
149
|
+
files = args.flatMap(collect);
|
|
150
|
+
} else {
|
|
151
|
+
console.error('usage: check-forbidden-chars.js <file|dir>... | --staged [--report]');
|
|
152
|
+
return 2;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const findings = files.flatMap(scanFile);
|
|
156
|
+
|
|
157
|
+
if (findings.length) {
|
|
158
|
+
const stream = reportOnly ? console.log : console.error;
|
|
159
|
+
stream(`\nForbidden characters (${findings.length} occurrence(s)):\n`);
|
|
160
|
+
for (const f of findings) {
|
|
161
|
+
stream(` ${f.file}:${f.line}:${f.column} "${f.char}" ${hex(f.codePoint)} ${f.name}`);
|
|
162
|
+
stream(` use ${f.replacement}`);
|
|
163
|
+
stream(` ${f.text}`);
|
|
164
|
+
}
|
|
165
|
+
if (!reportOnly) {
|
|
166
|
+
stream(
|
|
167
|
+
'\nThese characters break on the way to a terminal, a CSV export or an Azure' +
|
|
168
|
+
'\nDevOps field. Replace them with their ASCII equivalents rather than' +
|
|
169
|
+
'\nbypassing this check. Spanish accents are permitted and are never flagged.\n'
|
|
170
|
+
);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (reportOnly) {
|
|
177
|
+
console.log(`check-forbidden-chars: ${files.length} file(s) checked, no findings.`);
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(`check-forbidden-chars: ${files.length} file(s) checked, no violations.`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (require.main === module) process.exit(main(process.argv.slice(2)));
|
|
186
|
+
|
|
187
|
+
module.exports = { scanText, scanFile, main, EXTENSIONS, FORBIDDEN, ARROW_START, ARROW_END };
|