@keber/qa-framework 1.13.1 → 1.13.2

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 CHANGED
@@ -7,6 +7,22 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.13.2] - 2026-11-06
11
+
12
+ ### Fixed
13
+
14
+ - **`upgrade` no longer moves user-owned content during the legacy layout migrations.**
15
+ Section 5c used to move every unknown folder under `07-automation/e2e/` into `tests/`
16
+ (including `lib/`, `node_modules/`, `.auth/` and `test-results/`). It now moves a folder
17
+ only when it contains `*.spec.*` files and is not a tooling folder (`node_modules`, `lib`,
18
+ `test-results`, `playwright-report`, `blob-report`) or a dot-folder. Section 6 used to move
19
+ every flat `.md` in `02-test-plans/` to `sprints/legacy/`; it now moves only pre-v1.7.0
20
+ plans: files named `*-test-plan.md` or carrying the old `**Plan ID**: TP-` header.
21
+ A path that cannot be read is reported as a warning and left alone: unproven means
22
+ untouched.
23
+
24
+ ---
25
+
10
26
  ## [1.13.1] - 2026-11-06
11
27
 
12
28
  ### Fixed
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # qa-framework
2
2
 
3
3
  > Reusable, installable, agent-oriented QA framework for spec-driven automated testing.
4
- > Current version: **v1.13.1**
4
+ > Current version: **v1.13.2**
5
5
 
6
6
  ---
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keber/qa-framework",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./qa-framework.config.schema.json",
3
- "frameworkVersion": "1.13.1",
3
+ "frameworkVersion": "1.13.2",
4
4
 
5
5
  "project": {
6
6
  "name": "{{PROJECT_NAME}}",
@@ -19,13 +19,14 @@
19
19
  * - qa/07-automation/.env.example -> qa/07-automation/e2e/
20
20
  * - qa/07-automation/fixtures/ -> qa/07-automation/e2e/fixtures/
21
21
  * - qa/07-automation/e2e/{module}/ -> qa/07-automation/e2e/tests/{module}/
22
+ * (only folders holding *.spec.* files; lib/, node_modules/, dot-folders stay)
22
23
  * - Creates integration/ and load/ placeholders * - Patches playwright.config.ts: testDir '.' -> './tests', adds testIgnore
23
24
  * - Creates tests/helpers/debug/ and tests/seeds/ if absent
24
25
  *
25
26
  * MIGRATES (v1.6.x -> v1.7.0, non-destructive):
26
27
  * - qa/02-test-plans/automated/ -> qa/02-test-plans/sprints/legacy-automated/
27
28
  * - qa/02-test-plans/manual/ -> qa/02-test-plans/sprints/legacy-manual/
28
- * - qa/02-test-plans/*.md -> qa/02-test-plans/sprints/legacy/
29
+ * - qa/02-test-plans/*-test-plan.md (or "**Plan ID**: TP-" header) -> sprints/legacy/
29
30
  * - Creates qa/02-test-plans/sprints/ if absent
30
31
  * - Creates qa/03-test-cases/README.md optional marker if absent
31
32
  *
@@ -316,12 +317,37 @@ if (fs.existsSync(oldFixturesDir) && !fs.existsSync(newFixturesDir)) {
316
317
  }
317
318
 
318
319
  // 5c. Spec stubs: e2e/{module}/*.spec.ts -> e2e/tests/{module}/
319
- // Moves any directory directly under e2e/ that is not a known non-module dir.
320
+ // Moves a directory directly under e2e/ only when it is provably a legacy module
321
+ // folder: not a known scaffold/tooling dir, not a dot-folder, and it contains at
322
+ // least one spec file. Anything else (lib/, node_modules/, docs/...) is user-owned.
320
323
  const testsDir = path.join(e2eDir, 'tests');
321
- const knownE2eDirs = new Set(['tests', 'page-objects', 'fixtures', 'diagnosis', 'scripts', 'seeds', 'helpers']);
324
+ const knownE2eDirs = new Set([
325
+ 'tests', 'page-objects', 'fixtures', 'diagnosis', 'scripts', 'seeds', 'helpers',
326
+ 'node_modules', 'lib', 'test-results', 'playwright-report', 'blob-report',
327
+ ]);
328
+ const SPEC_FILE_RE = /\.spec\.[cm]?[jt]s$/;
329
+
330
+ function containsSpecFile(dir) {
331
+ let entries;
332
+ try {
333
+ entries = fs.readdirSync(dir, { withFileTypes: true });
334
+ } catch (err) {
335
+ // Unreadable means unproven: leave the folder where it is.
336
+ warnings.push(`Cannot inspect ${path.relative(cwd, dir)} (${err.code}) - not migrated.`);
337
+ return false;
338
+ }
339
+ for (const entry of entries) {
340
+ if (entry.isFile() && SPEC_FILE_RE.test(entry.name)) return true;
341
+ if (entry.isDirectory() && entry.name !== 'node_modules' && !entry.name.startsWith('.') &&
342
+ containsSpecFile(path.join(dir, entry.name))) return true;
343
+ }
344
+ return false;
345
+ }
346
+
322
347
  if (fs.existsSync(e2eDir)) {
323
348
  for (const entry of fs.readdirSync(e2eDir, { withFileTypes: true })) {
324
- if (!entry.isDirectory() || knownE2eDirs.has(entry.name)) continue;
349
+ if (!entry.isDirectory() || knownE2eDirs.has(entry.name) || entry.name.startsWith('.')) continue;
350
+ if (!containsSpecFile(path.join(e2eDir, entry.name))) continue;
325
351
  const oldModDir = path.join(e2eDir, entry.name);
326
352
  const newModDir = path.join(testsDir, entry.name);
327
353
  if (!fs.existsSync(newModDir)) {
@@ -442,12 +468,28 @@ if (fs.existsSync(testPlansDir)) {
442
468
  }
443
469
  }
444
470
 
445
- // Move any .md files sitting flat in 02-test-plans/ (not in sprints/)
471
+ // Move flat .md files in 02-test-plans/ only when they are provably pre-v1.7.0 plans:
472
+ // named {module}-test-plan.md / {module}-{track}-test-plan.md (qa-test-plan <= v1.6.x),
473
+ // or generated from the old templates/test-plan.md (header "**Plan ID**: TP-...").
474
+ // Any other flat .md (README.md, plan-sprint-1.md, notes) is user-owned and stays.
475
+ const LEGACY_PLAN_NAME_RE = /-test-plan\.md$/i;
476
+ const LEGACY_PLAN_HEADER_RE = /^\*\*Plan ID\*\*:\s*TP-/m;
477
+ const isLegacyPlan = (file) => {
478
+ if (LEGACY_PLAN_NAME_RE.test(path.basename(file))) return true;
479
+ try {
480
+ return LEGACY_PLAN_HEADER_RE.test(fs.readFileSync(file, 'utf8'));
481
+ } catch (err) {
482
+ warnings.push(`Cannot read 02-test-plans/${path.basename(file)} (${err.code}) - not migrated.`);
483
+ return false;
484
+ }
485
+ };
486
+
446
487
  if (fs.existsSync(testPlansDir)) {
447
488
  const legacyDir = path.join(sprintsDir, 'legacy');
448
489
  for (const entry of fs.readdirSync(testPlansDir, { withFileTypes: true })) {
449
490
  if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
450
491
  const oldFile = path.join(testPlansDir, entry.name);
492
+ if (!isLegacyPlan(oldFile)) continue;
451
493
  const newFile = path.join(legacyDir, entry.name);
452
494
  if (!fs.existsSync(newFile)) {
453
495
  if (!dryRun) {