@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.
- package/CHANGELOG.md +63 -18
- 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 +106 -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/pipelines/azure-pipeline-qa.yml +92 -25
- package/integrations/ado-powershell/scripts/sync-ado-titles.ps1 +2 -2
- package/integrations/playwright/README.md +128 -4
- package/package.json +9 -6
- package/qa-framework.config.json +24 -3
- 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 +108 -45
- 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 +461 -30
- 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 +52 -3
- package/templates/automation-scaffold/fixtures/auth.ts +27 -5
- package/templates/automation-scaffold/fixtures/base.ts +130 -0
- package/templates/automation-scaffold/fixtures/test-helpers.ts +1 -1
- package/templates/automation-scaffold/global-setup.ts +196 -37
- package/templates/automation-scaffold/playwright.config.ts +74 -10
- 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 +18 -4
- package/templates/execution-report.md +9 -9
- package/templates/integration-scaffold/.env.example +40 -0
- package/templates/integration-scaffold/README.md +77 -0
- package/templates/integration-scaffold/global-setup.ts +119 -0
- package/templates/integration-scaffold/package.json +15 -0
- package/templates/integration-scaffold/playwright.config.ts +92 -0
- package/templates/integration-scaffold/tests/example.spec.ts +43 -0
- package/templates/memory/ci-pipeline-findings.md +36 -0
- package/templates/memory/data-volatility-strategies.md +70 -0
- package/templates/memory/e2e-stabilization-patterns.md +54 -0
- package/templates/qa-framework.instructions.md +16 -7
- package/templates/qa-framework.rules.md +53 -0
- package/templates/qa-readme.md +26 -5
- 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/scripts/init.js
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
|
|
18
|
+
const { buildCommandContent, discoverSkillNames, commandFileName } = require('./lib/claude-commands');
|
|
19
|
+
const { AGENT_NAMES, isSprintCycleEnabled, buildAgentContent, agentFileName } = require('./lib/claude-agents');
|
|
20
|
+
|
|
18
21
|
// --- Parse args ---
|
|
19
22
|
const args = process.argv.slice(2);
|
|
20
23
|
const configFlag = args.indexOf('--config');
|
|
@@ -84,6 +87,7 @@ const topLevelFolders = [
|
|
|
84
87
|
'04-test-data',
|
|
85
88
|
'05-test-execution',
|
|
86
89
|
'06-defects/open',
|
|
90
|
+
'06-defects/disputed',
|
|
87
91
|
'06-defects/resolved',
|
|
88
92
|
'07-automation/e2e/tests',
|
|
89
93
|
'07-automation/e2e/tests/helpers/debug',
|
|
@@ -98,6 +102,18 @@ for (const folder of topLevelFolders) {
|
|
|
98
102
|
console.log(` [created] ${path.relative(cwd, fullPath)}/`);
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
// --- 06-defects/disputed/README.md ---
|
|
106
|
+
writeIfMissing(path.join(qaRoot, '06-defects', 'disputed', 'README.md'),
|
|
107
|
+
`# 06-defects/disputed/
|
|
108
|
+
|
|
109
|
+
Use this folder for defects where QA has reproducible evidence but the business or
|
|
110
|
+
development team disputes whether the observed behavior is actually a defect.
|
|
111
|
+
|
|
112
|
+
A defect stays here while the functional decision is pending. Once the team reaches
|
|
113
|
+
a formal conclusion, move the file to open/ (confirmed defect) or resolved/ (accepted
|
|
114
|
+
behavior or won't fix).
|
|
115
|
+
`);
|
|
116
|
+
|
|
101
117
|
// --- qa/README.md ---
|
|
102
118
|
const qaReadmeTemplate = path.resolve(__dirname, '..', 'templates', 'qa-readme.md');
|
|
103
119
|
if (fs.existsSync(qaReadmeTemplate)) {
|
|
@@ -113,20 +129,27 @@ if (fs.existsSync(qaReadmeTemplate)) {
|
|
|
113
129
|
// --- qa/memory/INDEX.md ---
|
|
114
130
|
const memoryDir = path.join(qaRoot, 'memory');
|
|
115
131
|
writeIfMissing(path.join(memoryDir, 'INDEX.md'),
|
|
116
|
-
`# Memory Index
|
|
132
|
+
`# Memory Index - ${config.project?.displayName ?? config.project?.name ?? 'Project'}
|
|
117
133
|
|
|
118
134
|
> The agent reads this file first before loading any memory file.
|
|
119
135
|
> Add a row here whenever you create or update a file in this directory.
|
|
120
136
|
|
|
121
137
|
| File | Topic | When to load |
|
|
122
138
|
|---|---|---|
|
|
123
|
-
|
|
124
|
-
|
|
139
|
+
| \`ci-pipeline-findings.md\` | CI pipeline issues, root causes, applied fixes, failure triage categories | Diagnosing CI failures; reviewing pipeline changes |
|
|
140
|
+
| \`e2e-stabilization-patterns.md\` | Flakiness patterns, crash-recovery techniques, strict-mode conflicts, reference timeouts | Investigating intermittent failures; refactoring fragile specs |
|
|
141
|
+
| \`data-volatility-strategies.md\` | Strategies for handling volatile test data (filter values, row counts, search inputs) | Fixing hardcoded-data failures; choosing an assertion resilience approach |
|
|
125
142
|
`);
|
|
126
143
|
|
|
144
|
+
// --- qa/memory template files ---
|
|
145
|
+
const memorySrc = path.resolve(__dirname, '..', 'templates', 'memory');
|
|
146
|
+
for (const file of ['ci-pipeline-findings.md', 'e2e-stabilization-patterns.md', 'data-volatility-strategies.md']) {
|
|
147
|
+
writeIfMissing(path.join(memoryDir, file), fs.readFileSync(path.join(memorySrc, file), 'utf8'));
|
|
148
|
+
}
|
|
149
|
+
|
|
127
150
|
// --- 03-test-cases README (optional directory marker) ---
|
|
128
151
|
writeIfMissing(path.join(qaRoot, '03-test-cases', 'README.md'),
|
|
129
|
-
`# 03-test-cases/
|
|
152
|
+
`# 03-test-cases/ - Optional Standalone Test Cases
|
|
130
153
|
|
|
131
154
|
> **v1.7.0+:** The primary location for test cases (with detailed steps) is now
|
|
132
155
|
> \`qa/02-test-plans/sprints/Sprint-{N}/Plan-de-Pruebas-{project}-Sprint-{N}-{module}.md\`.
|
|
@@ -178,7 +201,7 @@ for (const mod of modules) {
|
|
|
178
201
|
|
|
179
202
|
for (const sub of submodules) {
|
|
180
203
|
const subKey = sub.key ?? sub.name.toLowerCase().replace(/\s+/g, '-');
|
|
181
|
-
const subDir = path.join(qaRoot, moduleKey, subKey);
|
|
204
|
+
const subDir = path.join(qaRoot, '01-specifications', moduleKey, subKey);
|
|
182
205
|
fs.mkdirSync(subDir, { recursive: true });
|
|
183
206
|
|
|
184
207
|
for (const specFile of SPEC_FILES) {
|
|
@@ -225,9 +248,22 @@ for (const file of ['playwright.config.ts', 'global-setup.ts', '.env.example', '
|
|
|
225
248
|
console.log(` [created] ${path.relative(cwd, dest)}`);
|
|
226
249
|
}
|
|
227
250
|
}
|
|
251
|
+
// Lane scripts: the lane lock plus its config reader and global-setup guards. Plain
|
|
252
|
+
// CommonJS so they run as a CLI and stay testable with `node --test` without pulling
|
|
253
|
+
// in a TypeScript test runner.
|
|
254
|
+
const laneScriptsDir = path.join(e2eScaffoldDir, 'scripts');
|
|
255
|
+
fs.mkdirSync(laneScriptsDir, { recursive: true });
|
|
256
|
+
for (const file of ['lane-config.js', 'lane-lock.js', 'global-setup-guards.js']) {
|
|
257
|
+
const dest = path.join(laneScriptsDir, file);
|
|
258
|
+
if (!fs.existsSync(dest)) {
|
|
259
|
+
fs.copyFileSync(path.join(scaffoldSrc, 'scripts', file), dest);
|
|
260
|
+
console.log(` [created] ${path.relative(cwd, dest)}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
228
264
|
const fixturesDir = path.join(e2eScaffoldDir, 'fixtures');
|
|
229
265
|
fs.mkdirSync(fixturesDir, { recursive: true });
|
|
230
|
-
for (const file of ['auth.ts', 'test-helpers.ts']) {
|
|
266
|
+
for (const file of ['auth.ts', 'test-helpers.ts', 'base.ts']) {
|
|
231
267
|
const dest = path.join(fixturesDir, file);
|
|
232
268
|
if (!fs.existsSync(dest)) {
|
|
233
269
|
fs.copyFileSync(path.join(scaffoldSrc, 'fixtures', file), dest);
|
|
@@ -235,9 +271,26 @@ for (const file of ['auth.ts', 'test-helpers.ts']) {
|
|
|
235
271
|
}
|
|
236
272
|
}
|
|
237
273
|
|
|
238
|
-
// --- Integration
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
// --- Integration scaffold (lives in 07-automation/integration/) ---
|
|
275
|
+
const integrationDir = path.join(automationDir, 'integration');
|
|
276
|
+
const integrationScaffold = path.resolve(__dirname, '..', 'templates', 'integration-scaffold');
|
|
277
|
+
fs.mkdirSync(integrationDir, { recursive: true });
|
|
278
|
+
for (const file of ['package.json', 'playwright.config.ts', 'global-setup.ts', 'README.md', '.env.example']) {
|
|
279
|
+
const dest = path.join(integrationDir, file);
|
|
280
|
+
if (!fs.existsSync(dest)) {
|
|
281
|
+
fs.copyFileSync(path.join(integrationScaffold, file), dest);
|
|
282
|
+
console.log(` [created] ${path.relative(cwd, dest)}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const integrationTestsDir = path.join(integrationDir, 'tests');
|
|
286
|
+
fs.mkdirSync(integrationTestsDir, { recursive: true });
|
|
287
|
+
const exampleSpec = path.join(integrationTestsDir, 'example.spec.ts');
|
|
288
|
+
if (!fs.existsSync(exampleSpec)) {
|
|
289
|
+
fs.copyFileSync(path.join(integrationScaffold, 'tests', 'example.spec.ts'), exampleSpec);
|
|
290
|
+
console.log(` [created] ${path.relative(cwd, exampleSpec)}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// --- Load test placeholder ---
|
|
241
294
|
writeIfMissing(path.join(automationDir, 'load', 'README.md'),
|
|
242
295
|
`# Load Tests\n\n> Placeholder for load and performance tests.\n> Each tool gets its own subdirectory (e.g. k6/, jmeter/).\n`);
|
|
243
296
|
|
|
@@ -246,7 +299,7 @@ const adoDir = path.join(qaRoot, '08-azure-integration');
|
|
|
246
299
|
writeIfMissing(path.join(adoDir, 'README.md'), `# ADO Integration\n\nSee keber/qa-framework integrations/ado-powershell/ for setup instructions.\n`);
|
|
247
300
|
writeIfMissing(path.join(adoDir, 'module-registry.json'), JSON.stringify({ modules: [] }, null, 2));
|
|
248
301
|
|
|
249
|
-
// --- Skills
|
|
302
|
+
// --- Skills -> .github/skills/ ---
|
|
250
303
|
const skillsSrc = path.resolve(__dirname, '..', 'skills');
|
|
251
304
|
const skillsDest = path.join(cwd, '.github', 'skills');
|
|
252
305
|
fs.mkdirSync(skillsDest, { recursive: true });
|
|
@@ -261,7 +314,7 @@ if (fs.existsSync(skillsSrc)) {
|
|
|
261
314
|
}
|
|
262
315
|
}
|
|
263
316
|
|
|
264
|
-
// --- QA structure guide
|
|
317
|
+
// --- QA structure guide -> qa/QA-STRUCTURE-GUIDE.md ---
|
|
265
318
|
const structureGuideSrc = path.resolve(__dirname, '..', 'docs', 'folder-structure-guide.md');
|
|
266
319
|
const structureGuideDest = path.join(qaRoot, 'QA-STRUCTURE-GUIDE.md');
|
|
267
320
|
if (fs.existsSync(structureGuideSrc)) {
|
|
@@ -272,40 +325,38 @@ if (fs.existsSync(structureGuideSrc)) {
|
|
|
272
325
|
const githubDir = path.join(cwd, '.github');
|
|
273
326
|
const copilotInstrPath = path.join(githubDir, 'instructions', 'qa-framework.instructions.md');
|
|
274
327
|
|
|
275
|
-
// Detect whether the ado-qa skill is available (installed or already copied)
|
|
276
|
-
const adoQaInstalled = fs.existsSync(path.join(cwd, 'node_modules', '@keber', 'ado-qa'))
|
|
277
|
-
|| fs.existsSync(path.join(cwd, 'node_modules', 'ado-qa'));
|
|
278
|
-
const adoSkillReady = fs.existsSync(path.join(cwd, '.github', 'skills', 'ado-qa'));
|
|
279
|
-
const adoAvailable = adoQaInstalled || adoSkillReady;
|
|
280
|
-
|
|
281
|
-
const adoSection = adoAvailable
|
|
282
|
-
? `## Azure DevOps integration (ado-qa)
|
|
283
|
-
|
|
284
|
-
The \`ado-qa\` skill is installed. Load it in PowerShell before any ADO operation:
|
|
285
|
-
\`\`\`powershell
|
|
286
|
-
. .github/skills/ado-qa/load.ps1
|
|
287
|
-
\`\`\`
|
|
288
|
-
Refer to \`qa/00-guides/AGENT-INSTRUCTIONS-ADO-INTEGRATION.md\` for the full ADO workflow.
|
|
289
|
-
|
|
290
|
-
`
|
|
291
|
-
: `## Azure DevOps integration (not installed)
|
|
292
|
-
|
|
293
|
-
> ⚠️ **Agent note:** \`ado-qa\` is not installed in this project.
|
|
294
|
-
> To enable Azure DevOps integration (query Work Items, Test Plans, create bugs, etc.) run:
|
|
295
|
-
> \`\`\`bash
|
|
296
|
-
> npm install github:keber/ado-qa
|
|
297
|
-
> \`\`\`
|
|
298
|
-
> This will automatically copy the ADO skills to \`.github/skills/\` via postinstall.
|
|
299
|
-
|
|
300
|
-
`;
|
|
301
|
-
|
|
302
328
|
const instrTemplatePath = path.resolve(__dirname, '..', 'templates', 'qa-framework.instructions.md');
|
|
303
329
|
const copilotInstrContent = fs.readFileSync(instrTemplatePath, 'utf8')
|
|
304
|
-
.replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0')
|
|
305
|
-
.replace('{{ADO_SECTION}}', adoSection);
|
|
330
|
+
.replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
|
|
306
331
|
writeIfMissing(copilotInstrPath, copilotInstrContent);
|
|
307
332
|
|
|
308
|
-
// ---
|
|
333
|
+
// --- .claude/commands/qa-*.md - thin wrappers pointing at .github/skills/ ---
|
|
334
|
+
const claudeCommandsDest = path.join(cwd, '.claude', 'commands');
|
|
335
|
+
fs.mkdirSync(claudeCommandsDest, { recursive: true });
|
|
336
|
+
for (const skillName of discoverSkillNames(skillsSrc)) {
|
|
337
|
+
writeIfMissing(path.join(claudeCommandsDest, commandFileName(skillName)), buildCommandContent(skillName));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// --- .claude/rules/qa-framework.md - always-loaded rules (Claude Code equivalent of applyTo: '**') ---
|
|
341
|
+
const claudeRulesPath = path.join(cwd, '.claude', 'rules', 'qa-framework.md');
|
|
342
|
+
const rulesTemplatePath = path.resolve(__dirname, '..', 'templates', 'qa-framework.rules.md');
|
|
343
|
+
const claudeRulesContent = fs.readFileSync(rulesTemplatePath, 'utf8')
|
|
344
|
+
.replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
|
|
345
|
+
writeIfMissing(claudeRulesPath, claudeRulesContent);
|
|
346
|
+
|
|
347
|
+
// --- .claude/agents/qa-*.md - optional ANALISIS/PLAN sprint-cycle mode (Claude Code only) ---
|
|
348
|
+
// Gated by integrations.azureDevOps.sprintCycle.enabled. No .github/ equivalent: Claude
|
|
349
|
+
// Code subagents with per-agent tools/model frontmatter have no Copilot counterpart.
|
|
350
|
+
if (isSprintCycleEnabled(config)) {
|
|
351
|
+
const templatesDir = path.resolve(__dirname, '..', 'templates');
|
|
352
|
+
const claudeAgentsDest = path.join(cwd, '.claude', 'agents');
|
|
353
|
+
fs.mkdirSync(claudeAgentsDest, { recursive: true });
|
|
354
|
+
for (const agentName of AGENT_NAMES) {
|
|
355
|
+
writeIfMissing(path.join(claudeAgentsDest, agentFileName(agentName)), buildAgentContent(agentName, templatesDir, config));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// --- AGENT-NEXT-STEPS.md - readable by the agent after install ---
|
|
309
360
|
const nextStepsContent = `# ✅ @keber/qa-framework installed successfully
|
|
310
361
|
|
|
311
362
|
> This file was generated automatically by the postinstall script.
|
|
@@ -315,6 +366,7 @@ const nextStepsContent = `# ✅ @keber/qa-framework installed successfully
|
|
|
315
366
|
|
|
316
367
|
- \`qa/\` folder structure with spec templates and agent instructions
|
|
317
368
|
- \`.github/instructions/qa-framework.instructions.md\` with QA agent behavior rules (framework-owned, safe to upgrade)
|
|
369
|
+
- \`.claude/rules/qa-framework.md\` and \`.claude/commands/qa-*.md\` - equivalent Claude Code artifacts (framework-owned, safe to upgrade)
|
|
318
370
|
|
|
319
371
|
## Required next steps
|
|
320
372
|
|
|
@@ -328,15 +380,21 @@ Edit \`qa/qa-framework.config.json\` and add your project modules under the \`"m
|
|
|
328
380
|
npm install @keber/ado-qa
|
|
329
381
|
\`\`\`
|
|
330
382
|
|
|
331
|
-
**Playwright automation:**
|
|
383
|
+
**Playwright E2E automation:**
|
|
332
384
|
\`\`\`bash
|
|
333
385
|
npm install @playwright/test
|
|
334
386
|
cd qa/07-automation/e2e && npm install && npx playwright install chromium
|
|
335
387
|
\`\`\`
|
|
336
388
|
|
|
389
|
+
**Playwright API/integration automation:**
|
|
390
|
+
\`\`\`bash
|
|
391
|
+
cd qa/07-automation/integration && npm install
|
|
392
|
+
\`\`\`
|
|
393
|
+
|
|
337
394
|
### 3. Credentials
|
|
338
|
-
- Copy \`qa/07-automation/e2e/.env.example\`
|
|
339
|
-
-
|
|
395
|
+
- Copy \`qa/07-automation/e2e/.env.example\` -> \`qa/07-automation/e2e/.env\` and fill in credentials
|
|
396
|
+
- Copy \`qa/07-automation/integration/.env.example\` -> \`qa/07-automation/integration/.env\` and fill in credentials
|
|
397
|
+
- Add both \`.env\` files and both \`.auth/\` folders to \`.gitignore\`
|
|
340
398
|
`;
|
|
341
399
|
const nextStepsPath = path.join(qaRoot, 'AGENT-NEXT-STEPS.md');
|
|
342
400
|
writeIfMissing(nextStepsPath, nextStepsContent);
|
|
@@ -347,12 +405,17 @@ const adoQaInstalledFinal = fs.existsSync(path.join(cwd, 'node_modules', '@keber
|
|
|
347
405
|
const azureReporterInstalled = fs.existsSync(path.join(cwd, 'node_modules', '@alex_neo', 'playwright-azure-reporter'));
|
|
348
406
|
|
|
349
407
|
console.log('');
|
|
350
|
-
console.log(' @keber/qa-framework
|
|
408
|
+
console.log(' @keber/qa-framework - scaffold complete');
|
|
351
409
|
console.log(' ----------------------------------------');
|
|
352
410
|
console.log(' Installed:');
|
|
353
411
|
console.log(' qa/ QA directory structure + spec templates');
|
|
354
412
|
console.log(' .github/skills/ QA agent skills (8 stages)');
|
|
355
413
|
console.log(' .github/instructions/ qa-framework.instructions.md');
|
|
414
|
+
console.log(' .claude/commands/ qa-*.md (Claude Code slash commands)');
|
|
415
|
+
console.log(' .claude/rules/ qa-framework.md (Claude Code always-loaded rules)');
|
|
416
|
+
if (isSprintCycleEnabled(config)) {
|
|
417
|
+
console.log(' .claude/agents/ qa-analisis/qa-plan/qa-asesoria/qa-informe-resultados.md (ANALISIS/PLAN mode)');
|
|
418
|
+
}
|
|
356
419
|
console.log('');
|
|
357
420
|
console.log(' Optional integrations:');
|
|
358
421
|
if (playwrightInstalled) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/claude-agents.js - Generate .claude/agents/qa-*.md sprint-cycle subagents
|
|
5
|
+
*
|
|
6
|
+
* Optional ANALISIS/PLAN mode, gated by integrations.azureDevOps.sprintCycle.enabled in
|
|
7
|
+
* qa-framework.config.json. Claude Code only - subagents with per-agent tools/model
|
|
8
|
+
* frontmatter have no GitHub Copilot equivalent, so no .github/ counterpart is generated.
|
|
9
|
+
*
|
|
10
|
+
* Placeholders are resolved from config.integrations.azureDevOps.sprintCycle, falling back
|
|
11
|
+
* to neutral defaults when a field (or the whole config section) is absent.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const AGENT_NAMES = ['qa-analisis', 'qa-plan', 'qa-asesoria', 'qa-informe-resultados'];
|
|
18
|
+
|
|
19
|
+
const DEFAULT_SPRINT_CYCLE = {
|
|
20
|
+
sprintDurationDays: 8,
|
|
21
|
+
manualTestingTimeboxDays: 2,
|
|
22
|
+
dateFormat: 'dd-mm-aaaa',
|
|
23
|
+
timezone: 'America/Santiago',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const LOCALE_LANGUAGE_LABELS = {
|
|
27
|
+
es: 'espanol',
|
|
28
|
+
'es-cl': 'espanol de Chile',
|
|
29
|
+
'es-mx': 'espanol de Mexico',
|
|
30
|
+
'es-ar': 'espanol de Argentina',
|
|
31
|
+
en: 'English',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function isSprintCycleEnabled(config) {
|
|
35
|
+
return Boolean(config?.integrations?.azureDevOps?.sprintCycle?.enabled);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveLocaleLanguageLabel(locale) {
|
|
39
|
+
if (!locale) return 'espanol';
|
|
40
|
+
return LOCALE_LANGUAGE_LABELS[locale.toLowerCase()] ?? locale;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildPlaceholders(config) {
|
|
44
|
+
const sprintCycle = { ...DEFAULT_SPRINT_CYCLE, ...(config?.integrations?.azureDevOps?.sprintCycle ?? {}) };
|
|
45
|
+
const project = config?.project ?? {};
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
'{{PROJECT_NAME}}': project.name ?? '{{PROJECT_NAME}}',
|
|
49
|
+
'{{PROJECT_DISPLAY_NAME}}': project.displayName ?? project.name ?? '{{PROJECT_DISPLAY_NAME}}',
|
|
50
|
+
'{{SPRINT_DURATION_DAYS}}': String(sprintCycle.sprintDurationDays),
|
|
51
|
+
'{{MANUAL_TESTING_TIMEBOX_DAYS}}': String(sprintCycle.manualTestingTimeboxDays),
|
|
52
|
+
'{{DATE_FORMAT}}': sprintCycle.dateFormat,
|
|
53
|
+
'{{TIMEZONE}}': sprintCycle.timezone,
|
|
54
|
+
'{{LOCALE_LANGUAGE_LABEL}}': resolveLocaleLanguageLabel(config?.conventions?.locale ?? config?.conventions?.language),
|
|
55
|
+
'{{DEFECT_ID_PREFIX}}': config?.project?.name ? config.project.name.toUpperCase().replace(/[^A-Z0-9]/g, '-') : 'PROJ',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildAgentContent(agentName, templatesDir, config) {
|
|
60
|
+
const templatePath = path.join(templatesDir, 'agents', `${agentName}.md`);
|
|
61
|
+
let content = fs.readFileSync(templatePath, 'utf8');
|
|
62
|
+
const placeholders = buildPlaceholders(config);
|
|
63
|
+
for (const [placeholder, value] of Object.entries(placeholders)) {
|
|
64
|
+
content = content.split(placeholder).join(value);
|
|
65
|
+
}
|
|
66
|
+
return content;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function agentFileName(agentName) {
|
|
70
|
+
return `${agentName}.md`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
AGENT_NAMES,
|
|
75
|
+
isSprintCycleEnabled,
|
|
76
|
+
buildAgentContent,
|
|
77
|
+
agentFileName,
|
|
78
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/claude-commands.js - Generate .claude/commands/qa-{name}.md thin wrappers
|
|
5
|
+
*
|
|
6
|
+
* Each generated command points back at the corresponding SKILL.md as the single
|
|
7
|
+
* source of truth (no content duplication, no drift). Skills are discovered
|
|
8
|
+
* dynamically from the filesystem, never hardcoded, so a newly added skill
|
|
9
|
+
* under skills/ is picked up automatically by init.js and upgrade.js.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// Stage prerequisites mirrored from templates/qa-framework.instructions.md pipeline table.
|
|
16
|
+
// Skills not listed here (e.g. a future addition) still get a valid command with no
|
|
17
|
+
// prerequisite line, and this codebase does not need updating for the command to work.
|
|
18
|
+
const STAGE_PREREQUISITES = {
|
|
19
|
+
'qa-module-analysis': 'Prerequisite: None - this is the first stage of the QA pipeline.',
|
|
20
|
+
'qa-spec-generation': 'Prerequisite: `00-inventory.md` exists.',
|
|
21
|
+
'qa-test-plan': 'Prerequisite: `05-test-scenarios.md` exists.',
|
|
22
|
+
'qa-test-cases': 'Prerequisite: Test plan exists.',
|
|
23
|
+
'qa-automation': 'Prerequisite: Specs approved, no PENDING-CODE.',
|
|
24
|
+
'qa-test-stabilization': 'Prerequisite: Failing or flaky tests exist in `qa/07-automation/e2e/`.',
|
|
25
|
+
'qa-maintenance': 'Prerequisite: Application change delivered.',
|
|
26
|
+
'qa-ado-integration': 'Prerequisite: ADO enabled in `qa/qa-framework.config.json`.',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function buildCommandContent(skillName) {
|
|
30
|
+
const prerequisite = STAGE_PREREQUISITES[skillName];
|
|
31
|
+
const lines = [
|
|
32
|
+
`Read the full skill at \`.github/skills/${skillName}/SKILL.md\` FIRST, then follow its instructions exactly.`,
|
|
33
|
+
];
|
|
34
|
+
if (prerequisite) {
|
|
35
|
+
lines.push('', prerequisite);
|
|
36
|
+
}
|
|
37
|
+
return `${lines.join('\n')}\n`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function discoverSkillNames(skillsSrcDir) {
|
|
41
|
+
if (!fs.existsSync(skillsSrcDir)) return [];
|
|
42
|
+
return fs.readdirSync(skillsSrcDir, { withFileTypes: true })
|
|
43
|
+
.filter((entry) => entry.isDirectory())
|
|
44
|
+
.map((entry) => entry.name)
|
|
45
|
+
.sort();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function commandFileName(skillName) {
|
|
49
|
+
return `${skillName}.md`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
buildCommandContent,
|
|
54
|
+
discoverSkillNames,
|
|
55
|
+
commandFileName,
|
|
56
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
8
|
+
const pkg = require(path.join(rootDir, 'package.json'));
|
|
9
|
+
const version = pkg.version;
|
|
10
|
+
|
|
11
|
+
const targets = [
|
|
12
|
+
path.join(rootDir, 'README.md'),
|
|
13
|
+
path.join(rootDir, '.github', 'copilot-instructions.md'),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// templates/qa-framework.instructions.md and templates/qa-framework.rules.md contain a
|
|
17
|
+
// literal {{VERSION}} placeholder substituted by init.js/upgrade.js in the consumer
|
|
18
|
+
// project at install/upgrade time. That placeholder is NOT a hardcoded version string
|
|
19
|
+
// and must never be touched here.
|
|
20
|
+
|
|
21
|
+
const versionPattern = /v\d+\.\d+\.\d+/;
|
|
22
|
+
|
|
23
|
+
for (const filePath of targets) {
|
|
24
|
+
const original = fs.readFileSync(filePath, 'utf8');
|
|
25
|
+
|
|
26
|
+
if (!versionPattern.test(original)) {
|
|
27
|
+
throw new Error(`sync-version: no version string found in ${filePath}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const updated = original.replace(versionPattern, `v${version}`);
|
|
31
|
+
|
|
32
|
+
if (updated !== original) {
|
|
33
|
+
fs.writeFileSync(filePath, updated, 'utf8');
|
|
34
|
+
console.log(`sync-version: updated ${path.relative(rootDir, filePath)} -> v${version}`);
|
|
35
|
+
}
|
|
36
|
+
}
|