@keber/qa-framework 1.10.0 → 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.
Files changed (113) hide show
  1. package/CHANGELOG.md +63 -18
  2. package/README.md +2 -2
  3. package/docs/architecture.md +30 -26
  4. package/docs/decisions/lane-heartbeat-caller.md +86 -0
  5. package/docs/final-report.md +23 -23
  6. package/docs/folder-structure-guide.md +106 -80
  7. package/docs/installation.md +35 -7
  8. package/docs/skills-architecture.md +23 -23
  9. package/docs/spec-driven-philosophy.md +9 -9
  10. package/docs/usage-with-agent.md +83 -9
  11. package/docs/visuals/diag-01-ejecutivo-1.png +0 -0
  12. package/docs/visuals/diag-02-tactico-1.png +0 -0
  13. package/docs/visuals/diag-03-tecnico-1.png +0 -0
  14. package/docs/visuals/info-01-ejecutivo-1.png +0 -0
  15. package/docs/visuals/info-02-tactico-1.png +0 -0
  16. package/docs/visuals/info-03-tecnico-1.png +0 -0
  17. package/docs/visuals/pres-01-ejecutivo-1.png +0 -0
  18. package/docs/visuals/pres-02-tactico-1.png +0 -0
  19. package/docs/visuals/pres-03-tecnico-1.png +0 -0
  20. package/docs/visuals/qa-01-problema-1.png +0 -0
  21. package/docs/visuals/qa-02-que-es-1.png +0 -0
  22. package/docs/visuals/qa-03-pipeline-1.png +0 -0
  23. package/docs/visuals/qa-04-6-archivos-1.png +0 -0
  24. package/docs/visuals/qa-05-humano-1.png +0 -0
  25. package/docs/visuals/qa-06-agente-1.png +0 -0
  26. package/docs/visuals/qa-07-skills-arch-1.png +0 -0
  27. package/docs/visuals/qa-08-traceability-1.png +0 -0
  28. package/docs/visuals/qa-en-01-problem-1.png +0 -0
  29. package/docs/visuals/qa-en-02-what-is-1.png +0 -0
  30. package/docs/visuals/qa-en-03-pipeline-1.png +0 -0
  31. package/docs/visuals/qa-en-04-6files-1.png +0 -0
  32. package/docs/visuals/qa-en-05-human-1.png +0 -0
  33. package/docs/visuals/qa-en-06-agent-1.png +0 -0
  34. package/docs/visuals/qa-en-07-skills-arch-1.png +0 -0
  35. package/docs/visuals/qa-en-08-traceability-1.png +0 -0
  36. package/examples/module-example/suppliers/00-inventory.md +1 -1
  37. package/examples/module-example/suppliers/suppliers-create.spec.ts +8 -8
  38. package/integrations/ado-powershell/README.md +1 -1
  39. package/integrations/ado-powershell/pipelines/azure-pipeline-qa.yml +92 -25
  40. package/integrations/ado-powershell/scripts/sync-ado-titles.ps1 +2 -2
  41. package/integrations/playwright/README.md +128 -4
  42. package/package.json +9 -6
  43. package/qa-framework.config.json +24 -3
  44. package/scripts/check-forbidden-chars.js +187 -0
  45. package/scripts/check-spanish-accents.js +159 -0
  46. package/scripts/cli.js +1 -1
  47. package/scripts/generate.js +2 -2
  48. package/scripts/init.js +108 -45
  49. package/scripts/lib/claude-agents.js +78 -0
  50. package/scripts/lib/claude-commands.js +56 -0
  51. package/scripts/sync-version.js +36 -0
  52. package/scripts/upgrade.js +461 -30
  53. package/scripts/validate.js +17 -17
  54. package/skills/qa-ado-integration/SKILL.md +19 -19
  55. package/skills/qa-ado-integration/references/scripts-and-config.md +6 -6
  56. package/skills/qa-automation/SKILL.md +49 -27
  57. package/skills/qa-automation/references/config-checklist.md +4 -4
  58. package/skills/qa-automation/references/dom-inspection-template.js +7 -7
  59. package/skills/qa-automation/references/patterns.md +20 -20
  60. package/skills/qa-automation/references/pom-template.md +161 -0
  61. package/skills/qa-maintenance/SKILL.md +11 -11
  62. package/skills/qa-maintenance/references/update-rules.md +8 -8
  63. package/skills/qa-module-analysis/SKILL.md +20 -19
  64. package/skills/qa-module-analysis/references/exploration-checklist.md +4 -4
  65. package/skills/qa-module-analysis/references/output-verification.md +69 -0
  66. package/skills/qa-module-analysis/references/spec-file-formats.md +14 -14
  67. package/skills/qa-spec-generation/SKILL.md +13 -7
  68. package/skills/qa-test-cases/SKILL.md +11 -81
  69. package/skills/qa-test-cases/references/test-case-template.md +5 -5
  70. package/skills/qa-test-plan/SKILL.md +22 -86
  71. package/skills/qa-test-plan/references/plan-de-pruebas-template.md +24 -24
  72. package/skills/qa-test-stabilization/SKILL.md +59 -30
  73. package/skills/qa-test-stabilization/references/classification-protocol.md +35 -35
  74. package/templates/agent-next-steps.md +10 -10
  75. package/templates/agents/qa-analisis.md +142 -0
  76. package/templates/agents/qa-asesoria.md +48 -0
  77. package/templates/agents/qa-informe-resultados.md +234 -0
  78. package/templates/agents/qa-plan.md +131 -0
  79. package/templates/automation-scaffold/.env.example +52 -3
  80. package/templates/automation-scaffold/fixtures/auth.ts +27 -5
  81. package/templates/automation-scaffold/fixtures/base.ts +130 -0
  82. package/templates/automation-scaffold/fixtures/test-helpers.ts +1 -1
  83. package/templates/automation-scaffold/global-setup.ts +196 -37
  84. package/templates/automation-scaffold/playwright.config.ts +74 -10
  85. package/templates/automation-scaffold/scripts/global-setup-guards.js +184 -0
  86. package/templates/automation-scaffold/scripts/lane-config.js +269 -0
  87. package/templates/automation-scaffold/scripts/lane-lock.js +589 -0
  88. package/templates/defect-report.md +18 -4
  89. package/templates/execution-report.md +9 -9
  90. package/templates/integration-scaffold/.env.example +40 -0
  91. package/templates/integration-scaffold/README.md +77 -0
  92. package/templates/integration-scaffold/global-setup.ts +119 -0
  93. package/templates/integration-scaffold/package.json +15 -0
  94. package/templates/integration-scaffold/playwright.config.ts +92 -0
  95. package/templates/integration-scaffold/tests/example.spec.ts +43 -0
  96. package/templates/memory/ci-pipeline-findings.md +36 -0
  97. package/templates/memory/data-volatility-strategies.md +70 -0
  98. package/templates/memory/e2e-stabilization-patterns.md +54 -0
  99. package/templates/qa-framework.instructions.md +16 -7
  100. package/templates/qa-framework.rules.md +53 -0
  101. package/templates/qa-readme.md +26 -5
  102. package/templates/session-summary.md +2 -2
  103. package/templates/specification/00-inventory.md +3 -3
  104. package/templates/specification/01-business-rules.md +6 -6
  105. package/templates/specification/02-workflows.md +1 -1
  106. package/templates/specification/03-roles-permissions.md +1 -1
  107. package/templates/specification/04-test-data.md +8 -8
  108. package/templates/specification/05-test-scenarios.md +5 -5
  109. package/templates/test-case.md +3 -3
  110. package/templates/test-data-guidelines.md +7 -7
  111. package/templates/test-plan-sprint.md +13 -13
  112. package/templates/test-plan.md +3 -3
  113. package/skills/qa-test-plan/references/priority-and-feasibility.md +0 -159
@@ -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
- This framework is designed so that an IDE agent (such as GitHub Copilot in VS Code) can:
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 — Workspace instructions file (recommended)
49
+ ### Option A - Workspace instructions file (recommended)
48
50
 
49
- `init` generates `.github/copilot-instructions.md` automatically. It contains the full
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 — Reference skills directly in the agent conversation
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 — Individual task prompts
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 copilot-instructions.md)
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/copilot-instructions.md` rule 0). For additional context:
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
@@ -1,4 +1,4 @@
1
- # Suppliers — Inventory
1
+ # Suppliers - Inventory
2
2
 
3
3
  **Module**: Suppliers (SUP)
4
4
  **Submodule**: Create / Edit / List
@@ -15,16 +15,16 @@
15
15
 
16
16
  import { test, expect } from '@playwright/test';
17
17
 
18
- // EXEC_IDX: unique per minute-window — prevents data collisions between runs
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 — happy path @P0
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 — happy path @P0', async ({ page }) => {
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 — required fields validation @P0
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 — required fields validation @P0', async ({ page }) => {
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 — duplicate RUT rejected @P0
82
+ * [TC-SUP-CR-003] Create supplier - duplicate RUT rejected @P0
83
83
  *
84
- * NOTE: test.skip active — DEF-001: Duplicate RUT check not enforced server-side.
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 — duplicate RUT rejected @P0', async ({ page }) => {
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
  );
@@ -1,4 +1,4 @@
1
- # Integration — ADO PowerShell Scripts
1
+ # Integration - ADO PowerShell Scripts
2
2
 
3
3
  PowerShell scripts for Azure DevOps Test Plan management.
4
4
  All scripts are parameterized and project-agnostic.
@@ -1,5 +1,5 @@
1
1
  # -----------------------------------------------------------------------
2
- # keber/qa-framework — Azure Pipeline QA Template
2
+ # keber/qa-framework - Azure Pipeline QA Template
3
3
  # -----------------------------------------------------------------------
4
4
  # Parameterized CI pipeline for running Playwright E2E and API tests and
5
5
  # syncing results to Azure DevOps Test Plans via SYSTEM_ACCESSTOKEN.
@@ -8,28 +8,28 @@
8
8
  # Update 'resources.repositories.name' to match your org/project/repo.
9
9
  #
10
10
  # Required Pipeline Library Variable Group: "QA-Variables"
11
- # Create this group in ADO → Pipelines → Library and add the following:
11
+ # Create this group in ADO -> Pipelines -> Library and add the following:
12
12
  #
13
- # QA_BASE_URL — Target application URL (e.g. https://app-qa.example.com)
14
- # QA_USER_EMAIL — Test user email / RUT
15
- # QA_USER_PASSWORD — Test user password [secret]
16
- # ADO_ORG — ADO organization slug (e.g. garcesfruit-desarrollo)
17
- # ADO_PROJECT — ADO project name (e.g. gf-portalproveedores)
18
- # ADO_E2E_PLAN_ID — Test Plan ID for the E2E suite (e.g. 27044)
19
- # ADO_API_PLAN_ID — Test Plan ID for the API/integration suite (e.g. 27046)
13
+ # QA_BASE_URL - Target application URL (e.g. https://app-qa.example.com)
14
+ # QA_USER_EMAIL - Test user email / RUT
15
+ # QA_USER_PASSWORD - Test user password [secret]
16
+ # ADO_ORG - ADO organization slug (e.g. my-org)
17
+ # ADO_PROJECT - ADO project name (e.g. my-project)
18
+ # ADO_E2E_PLAN_ID - Test Plan ID for the E2E suite
19
+ # ADO_API_PLAN_ID - Test Plan ID for the API/integration suite
20
20
  #
21
- # NOTE on multiple Plan IDs: ADO variable groups store plain strings — there
21
+ # NOTE on multiple Plan IDs: ADO variable groups store plain strings - there
22
22
  # is no array type. Use one variable per suite (ADO_E2E_PLAN_ID / ADO_API_PLAN_ID).
23
23
  # If the project has only one suite, a single ADO_PLAN_ID variable is enough.
24
24
  # Results are reported to ADO via System.AccessToken (OAuth); no PAT required.
25
25
  # -----------------------------------------------------------------------
26
26
 
27
- # Cross-project repository — update <ADO_SOURCE_PROJECT>/<REPO_NAME> as needed
27
+ # Cross-project repository - update <ADO_SOURCE_PROJECT>/<REPO_NAME> as needed
28
28
  resources:
29
29
  repositories:
30
30
  - repository: source
31
31
  type: git
32
- name: <ADO_SOURCE_PROJECT>/<REPO_NAME> # e.g. gf-gestion-qa/gf-portalproveedores
32
+ name: <ADO_SOURCE_PROJECT>/<REPO_NAME> # e.g. my-qa-project/my-repo
33
33
  ref: refs/heads/main
34
34
 
35
35
  # Trigger: push and PR to main.
@@ -44,15 +44,34 @@ pr:
44
44
  include:
45
45
  - main
46
46
 
47
- # Optional scheduled run — uncomment and adjust if a nightly regression is needed:
47
+ # Optional scheduled run - uncomment and adjust if a nightly regression is needed:
48
48
  # schedules:
49
- # - cron: '0 6 * * 1-5' # 06:00 UTC Mon–Fri (03:00 CLT / 02:00 CLST)
49
+ # - cron: '0 6 * * 1-5' # 06:00 UTC Mon-Fri (03:00 CLT / 02:00 CLST)
50
50
  # displayName: Nightly regression
51
51
  # branches:
52
52
  # include:
53
53
  # - main
54
54
  # always: true
55
55
 
56
+ # -----------------------------------------------------------------------
57
+ # Pipeline parameters
58
+ # -----------------------------------------------------------------------
59
+ # suiteFilter controls which tests run when the pipeline is triggered manually
60
+ # or from a PR. Scheduled runs always override this to 'all' automatically
61
+ # (see the "Resolve suite filter" step in each test job).
62
+ #
63
+ # Default is 'all' so a plain manual run executes the full suite.
64
+ # Override at queue time to run a subset (e.g. 'smoke', '[P0]', 'login').
65
+ #
66
+ # The value is passed to Playwright via --grep. Special regex characters such
67
+ # as '[' and ']' are escaped by the bash array pattern in the run step, so
68
+ # values like '[P0]' are safe.
69
+ parameters:
70
+ - name: suiteFilter
71
+ displayName: 'Test suite filter (grep pattern, or "all" to skip filtering)'
72
+ type: string
73
+ default: 'all'
74
+
56
75
  pool:
57
76
  vmImage: 'ubuntu-latest'
58
77
 
@@ -111,6 +130,21 @@ stages:
111
130
  # -----------------------------------------------------------------------
112
131
  # Stage 2: E2E Tests (Playwright/Chromium)
113
132
  # -----------------------------------------------------------------------
133
+ # Single-job configuration. For parallel execution with two test users,
134
+ # split this into two jobs and set QA_AUTH_USER per job:
135
+ #
136
+ # - job: E2E_User1
137
+ # variables:
138
+ # QA_AUTH_USER: '1'
139
+ # ...
140
+ # - job: E2E_User2
141
+ # variables:
142
+ # QA_AUTH_USER: '2'
143
+ # ...
144
+ #
145
+ # Each job must use a distinct Playwright project (--project=chromium-user1 /
146
+ # --project=chromium-user2) to avoid session conflicts between parallel users.
147
+ # -----------------------------------------------------------------------
114
148
  - stage: E2E
115
149
  displayName: 'E2E Tests (Playwright/Chromium)'
116
150
  dependsOn: Setup
@@ -138,11 +172,11 @@ stages:
138
172
  HTTP_CODE=$(curl -o /dev/null -s -w "%{http_code}" --max-time 30 "$(QA_BASE_URL)")
139
173
  echo "HTTP response: $HTTP_CODE"
140
174
  if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 400 ]; then
141
- echo "##[error]Smoke test failed — $(QA_BASE_URL) returned HTTP $HTTP_CODE"
175
+ echo "##[error]Smoke test failed - $(QA_BASE_URL) returned HTTP $HTTP_CODE"
142
176
  exit 1
143
177
  fi
144
178
  echo "Smoke test passed (HTTP $HTTP_CODE)"
145
- displayName: 'Smoke test — verify QA_BASE_URL is reachable'
179
+ displayName: 'Smoke test - verify QA_BASE_URL is reachable'
146
180
 
147
181
  # ---------------------------------------------------------------
148
182
  # Playwright Workspaces (Azure App Testing)
@@ -159,9 +193,28 @@ stages:
159
193
  # inlineScript: echo "Authenticated via Entra ID"
160
194
 
161
195
  - script: |
196
+ # Scheduled runs always execute the full suite, regardless of the
197
+ # suiteFilter parameter. This prevents accidental partial runs in
198
+ # nightly regressions when the parameter was changed by a prior
199
+ # manual queue.
200
+ SUITE_FILTER="${{ parameters.suiteFilter }}"
201
+ if [ "$(Build.Reason)" = "Schedule" ]; then
202
+ SUITE_FILTER="all"
203
+ echo "Scheduled run detected - overriding suiteFilter to 'all'"
204
+ fi
205
+ echo "Effective suiteFilter: $SUITE_FILTER"
206
+
207
+ # Build --grep args as a bash array to safely handle patterns that
208
+ # contain special regex characters such as '[' and ']'. Inline
209
+ # string interpolation would break those patterns at the shell level.
210
+ GREP_ARGS=()
211
+ if [ "$SUITE_FILTER" != "all" ]; then
212
+ GREP_ARGS=(--grep "$SUITE_FILTER")
213
+ fi
214
+
162
215
  cd $(E2E_DIR)
163
216
  npx playwright test --project=chromium --ignore-snapshots \
164
- --grep-invert "_inspect|_diag"
217
+ --grep-invert "_inspect|_diag" "${GREP_ARGS[@]}"
165
218
  displayName: 'Run E2E tests'
166
219
  continueOnError: true
167
220
  env:
@@ -173,31 +226,40 @@ stages:
173
226
  ADO_PLAN_ID: $(ADO_E2E_PLAN_ID)
174
227
  SYSTEM_ACCESSTOKEN: $(System.AccessToken)
175
228
  CI: 'true'
176
- # PLAYWRIGHT_SERVICE_URL: $(PLAYWRIGHT_SERVICE_URL) # Playwright Workspaces endpoint — enable together with the login task
229
+ # QA_AUTH_USER: '1' # Set explicitly when running parallel jobs (user-1 / user-2 model)
230
+ # PLAYWRIGHT_SERVICE_URL: $(PLAYWRIGHT_SERVICE_URL) # Playwright Workspaces endpoint - enable together with the login task
177
231
 
232
+ # publishRunAttachments is false because screenshots and traces are
233
+ # already uploaded as pipeline artifacts below. Setting it to true
234
+ # would double-publish the same files.
178
235
  - task: PublishTestResults@2
179
236
  condition: always()
180
237
  inputs:
181
238
  testResultsFormat: 'JUnit'
182
- testResultsFiles: '**/test-results/**/*.xml'
239
+ testResultsFiles: 'test-results/junit-results.xml'
183
240
  searchFolder: $(E2E_DIR)
184
241
  mergeTestResults: true
242
+ publishRunAttachments: false
185
243
  failTaskOnFailedTests: true
186
244
  testRunTitle: '[Auto E2E] $(Build.BuildNumber)'
187
245
  displayName: 'Publish E2E test results (JUnit)'
188
246
 
247
+ # Artifact names include $(System.JobAttempt) to avoid name collisions
248
+ # when the job is retried. ADO rejects duplicate artifact names within
249
+ # the same pipeline run, so without the suffix a retry always fails at
250
+ # the publish step.
189
251
  - task: PublishPipelineArtifact@1
190
252
  condition: always()
191
253
  inputs:
192
254
  targetPath: '$(E2E_DIR)/playwright-report'
193
- artifact: 'e2e-playwright-report'
255
+ artifact: 'e2e-playwright-report-attempt-$(System.JobAttempt)'
194
256
  displayName: 'Publish E2E HTML report'
195
257
 
196
258
  - task: PublishPipelineArtifact@1
197
259
  condition: always()
198
260
  inputs:
199
261
  targetPath: '$(E2E_DIR)/test-results'
200
- artifact: 'e2e-test-results'
262
+ artifact: 'e2e-test-results-attempt-$(System.JobAttempt)'
201
263
  displayName: 'Publish E2E trace artifacts'
202
264
 
203
265
  # -----------------------------------------------------------------------
@@ -237,6 +299,7 @@ stages:
237
299
  # inlineScript: echo "Authenticated via Entra ID"
238
300
 
239
301
  - script: |
302
+ # API integration tests do not use suite tags - always run all.
240
303
  cd $(API_DIR)
241
304
  npx playwright test --grep-invert "_inspect|_diag"
242
305
  displayName: 'Run API integration tests'
@@ -250,29 +313,33 @@ stages:
250
313
  ADO_PLAN_ID: $(ADO_API_PLAN_ID)
251
314
  SYSTEM_ACCESSTOKEN: $(System.AccessToken)
252
315
  CI: 'true'
253
- # PLAYWRIGHT_SERVICE_URL: $(PLAYWRIGHT_SERVICE_URL) # Playwright Workspaces endpoint — enable together with the login task
316
+ # PLAYWRIGHT_SERVICE_URL: $(PLAYWRIGHT_SERVICE_URL) # Playwright Workspaces endpoint - enable together with the login task
254
317
 
318
+ # publishRunAttachments is false because trace artifacts are already
319
+ # uploaded as pipeline artifacts below.
255
320
  - task: PublishTestResults@2
256
321
  condition: always()
257
322
  inputs:
258
323
  testResultsFormat: 'JUnit'
259
- testResultsFiles: '**/test-results/**/*.xml'
324
+ testResultsFiles: 'test-results/junit-results.xml'
260
325
  searchFolder: $(API_DIR)
261
326
  mergeTestResults: true
327
+ publishRunAttachments: false
262
328
  failTaskOnFailedTests: true
263
329
  testRunTitle: '[Auto API] $(Build.BuildNumber)'
264
330
  displayName: 'Publish API test results (JUnit)'
265
331
 
332
+ # Artifact names include $(System.JobAttempt) to prevent collision on retries.
266
333
  - task: PublishPipelineArtifact@1
267
334
  condition: always()
268
335
  inputs:
269
336
  targetPath: '$(API_DIR)/playwright-report'
270
- artifact: 'api-playwright-report'
337
+ artifact: 'api-playwright-report-attempt-$(System.JobAttempt)'
271
338
  displayName: 'Publish API HTML report'
272
339
 
273
340
  - task: PublishPipelineArtifact@1
274
341
  condition: always()
275
342
  inputs:
276
343
  targetPath: '$(API_DIR)/test-results'
277
- artifact: 'api-test-results'
344
+ artifact: 'api-test-results-attempt-$(System.JobAttempt)'
278
345
  displayName: 'Publish API trace artifacts'
@@ -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) — skipping WI $adoId"
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 → $newTitle"
87
+ Write-Host " [SYNCED] WI #$adoId -> $newTitle"
88
88
  $synced++
89
89
  }
90
90
  }