@keber/qa-framework 1.8.0 → 1.9.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 CHANGED
@@ -33,6 +33,37 @@ has been removed or parameterized. See `docs/comparison-matrix.md` and `docs/gen
33
33
 
34
34
  ---
35
35
 
36
+ ## [1.9.0] - 2026-05-15
37
+
38
+ ### Changed
39
+
40
+ - **`create-testplan-from-mapping.ps1` refactored** — removed all ADO-specific logic (auth headers,
41
+ `Invoke-RestMethod` calls, XML step construction). The script now delegates to `New-AdoTestCase`
42
+ and `Add-AdoTestCaseToSuite` from `@keber/ado-powershell`, keeping `qa-framework` free of
43
+ ADO implementation details. Requires `@keber/ado-powershell` ≥ 1.3.0.
44
+ - **Script parameters simplified** — `-OrgUrl`, `-ProjectName`, and `-Token` removed from
45
+ `create-testplan-from-mapping.ps1`. Caller is responsible for loading the ADO session via
46
+ `. .github/skills/ado-powershell/load.ps1` before invoking the script.
47
+ - **`tc-mapping.json` schema extended** — added optional `steps` (string array) and
48
+ `expectedResult` (string) fields per test case. When present, steps are sent to ADO as
49
+ `Microsoft.VSTS.TCM.Steps`; all steps except the last are `ActionStep`, the last is
50
+ `ValidateStep` with `expectedResult`.
51
+ - **`qa-ado-integration` SKILL.md updated** — Step 3 now specifies exactly how to parse the
52
+ Tabla de Pruebas columns (`Steps` split on `<br>`, strip numbering prefix) to produce
53
+ `tc-mapping.json` with steps.
54
+
55
+ ### Added
56
+
57
+ - **Guard in `create-testplan-from-mapping.ps1`** — fails fast with a clear message if
58
+ `New-AdoTestCase` is not available (ADO session not loaded).
59
+
60
+ ### Requires
61
+
62
+ - `@keber/ado-powershell` ≥ **1.3.0** — adds `$ExpectedResult` parameter to `New-AdoTestCase`
63
+ and `ValidateStep` support in the generated TCM steps XML.
64
+
65
+ ---
66
+
36
67
  ## [1.8.0] - 2026-05-07
37
68
 
38
69
  ### Changed
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.8.0**
4
+ > Current version: **v1.9.0**
5
5
 
6
6
  ---
7
7
 
@@ -15,19 +15,20 @@ All scripts are parameterized and project-agnostic.
15
15
  ## Prerequisites
16
16
 
17
17
  - PowerShell 7+ (recommended) or Windows PowerShell 5.1
18
- - Azure DevOps Personal Access Token (PAT) with `Work Items: Read & Write` scope
19
- - PAT stored as `$env:AZURE_TOKEN` or passed via `-Token` parameter
18
+ - `@keber/ado-powershell` skill installed and loaded (provides `New-AdoTestCase`, `Add-AdoTestCaseToSuite`, etc.)
19
+ - Azure DevOps Personal Access Token (PAT) with `Work Items: Read & Write` and `Test Management: Read & Write` scopes
20
+ - PAT and org/project set as env vars: `ADO_PAT`, `ADO_ORG`, `ADO_PROJECT`
20
21
 
21
22
  ## Quick start
22
23
 
23
24
  ```powershell
24
- # 1. Generate ado-ids.json mapping (from your TC mapping file)
25
+ # 0. Load the ADO skill (initialises session from env vars)
26
+ . .github/skills/ado-powershell/load.ps1
27
+
28
+ # 1. Create Test Cases in ADO from tc-mapping.json
25
29
  .\scripts\create-testplan-from-mapping.ps1 `
26
- -OrgUrl "https://dev.azure.com/your-org" `
27
- -ProjectName "YourProject" `
28
30
  -PlanId 22304 `
29
- -MappingFile "qa/08-azure-integration/tc-mapping.json" `
30
- -Token $env:AZURE_TOKEN
31
+ -MappingFile "qa/08-azure-integration/tc-mapping.json"
31
32
 
32
33
  # 2. Inject ADO IDs into spec files (idempotent)
33
34
  .\scripts\inject-ado-ids.ps1 `
@@ -59,12 +60,36 @@ npx playwright test
59
60
  "planId": 22304,
60
61
  "suiteId": 22305,
61
62
  "testCases": [
62
- { "localId": "TC-SUP-CR-001", "title": "Create supplier @P0", "specFile": "suppliers/create.spec.ts" },
63
- { "localId": "TC-SUP-CR-002", "title": "Create supplier - required fields @P0", "specFile": "suppliers/create.spec.ts" }
63
+ {
64
+ "localId": "TC-SUP-CR-001",
65
+ "title": "Create supplier @P0",
66
+ "specFile": "suppliers/create.spec.ts",
67
+ "steps": [
68
+ "Navigate to /suppliers/new",
69
+ "Fill in required fields: Name, RUT, Email",
70
+ "Click 'Save'"
71
+ ],
72
+ "expectedResult": "Supplier is created and appears in the list with status Active"
73
+ },
74
+ {
75
+ "localId": "TC-SUP-CR-002",
76
+ "title": "Create supplier - required fields @P0",
77
+ "specFile": "suppliers/create.spec.ts",
78
+ "steps": [
79
+ "Navigate to /suppliers/new",
80
+ "Leave required fields empty",
81
+ "Click 'Save'"
82
+ ],
83
+ "expectedResult": "Validation errors appear for each required field; record is not saved"
84
+ }
64
85
  ]
65
86
  }
66
87
  ```
67
88
 
89
+ > **`steps`** (optional): Array of step strings. Each entry becomes one ADO step. All steps except the last are `ActionStep`; the last is `ValidateStep` and receives `expectedResult`.
90
+ > **`expectedResult`** (optional): Overall expected result, applied to the last step. If omitted, the last step has an empty expected result.
91
+ > If `steps` is absent or empty, the Test Case is created with title only (no steps).
92
+
68
93
  ## File: ado-ids.json (output after create-testplan)
69
94
 
70
95
  ```json
@@ -7,17 +7,16 @@
7
7
  corresponding Test Case Work Items in a specified ADO Test Plan/Suite.
8
8
  Outputs ado-ids.json with the resulting WI IDs for use by inject-ado-ids.ps1.
9
9
 
10
- .PARAMETER OrgUrl
11
- Azure DevOps organization URL (e.g. https://dev.azure.com/your-org).
12
-
13
- .PARAMETER ProjectName
14
- ADO project name.
10
+ Requires an active ADO session. Call:
11
+ . .github/skills/ado-powershell/load.ps1
12
+ before running this script. The session is initialised automatically by
13
+ load.ps1 when ADO_ORG, ADO_PROJECT and ADO_PAT env vars are set.
15
14
 
16
15
  .PARAMETER PlanId
17
16
  ADO Test Plan ID.
18
17
 
19
18
  .PARAMETER SuiteId
20
- ADO Test Suite ID. If omitted, uses the plan's root suite.
19
+ ADO Test Suite ID. If omitted, uses the suiteId from tc-mapping.json.
21
20
 
22
21
  .PARAMETER MappingFile
23
22
  Path to tc-mapping.json input file.
@@ -25,43 +24,33 @@
25
24
  .PARAMETER OutputFile
26
25
  Path to write ado-ids.json output. Default: ado-ids.json in same dir as MappingFile.
27
26
 
28
- .PARAMETER Token
29
- ADO Personal Access Token. Prefer passing via $env:AZURE_TOKEN.
30
-
31
27
  .EXAMPLE
28
+ . .github/skills/ado-powershell/load.ps1
32
29
  .\create-testplan-from-mapping.ps1 `
33
- -OrgUrl "https://dev.azure.com/your-org" `
34
- -ProjectName "YourProject" `
35
30
  -PlanId 22304 `
36
- -MappingFile "qa/08-azure-integration/tc-mapping.json" `
37
- -Token $env:AZURE_TOKEN
31
+ -MappingFile "qa/08-azure-integration/tc-mapping.json"
38
32
  #>
39
33
  [CmdletBinding(SupportsShouldProcess)]
40
34
  param(
41
- [Parameter(Mandatory)] [string] $OrgUrl,
42
- [Parameter(Mandatory)] [string] $ProjectName,
43
35
  [Parameter(Mandatory)] [int] $PlanId,
44
36
  [int] $SuiteId = 0,
45
37
  [Parameter(Mandatory)] [string] $MappingFile,
46
- [string] $OutputFile = '',
47
- [Parameter(Mandatory)] [string] $Token
38
+ [string] $OutputFile = ''
48
39
  )
49
40
 
50
41
  Set-StrictMode -Version Latest
51
42
  $ErrorActionPreference = 'Stop'
52
43
 
53
- # Build auth header
54
- $b64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$Token"))
55
- $headers = @{
56
- 'Authorization' = "Basic $b64"
57
- 'Content-Type' = 'application/json'
44
+ # Verify ADO session is available
45
+ if (-not (Get-Command 'New-AdoTestCase' -ErrorAction SilentlyContinue)) {
46
+ Write-Error "ADO session not loaded. Run: . .github/skills/ado-powershell/load.ps1"
47
+ exit 1
58
48
  }
59
49
 
60
50
  # Load mapping
61
51
  if (-not (Test-Path $MappingFile)) { Write-Error "Not found: $MappingFile"; exit 1 }
62
52
  $mapping = Get-Content $MappingFile -Raw | ConvertFrom-Json
63
53
 
64
- $apiBase = "$OrgUrl/$ProjectName/_apis"
65
54
  $suiteId = if ($SuiteId -gt 0) { $SuiteId } else { $mapping.suiteId }
66
55
 
67
56
  Write-Host "[create-testplan] Creating $($mapping.testCases.Count) test cases in Plan $PlanId / Suite $suiteId"
@@ -69,29 +58,25 @@ Write-Host "[create-testplan] Creating $($mapping.testCases.Count) test cases in
69
58
  $results = @()
70
59
 
71
60
  foreach ($tc in $mapping.testCases) {
72
- $body = @{
73
- op = 'add'
74
- path = '/fields/System.Title'
75
- value = "[$($tc.localId)] $($tc.title)"
76
- } | ConvertTo-Json -AsArray
77
-
78
- $createUrl = "$apiBase/wit/workitems/`$Test Case?api-version=7.1"
61
+ $steps = if ($tc.PSObject.Properties['steps']) { $tc.steps } else { @() }
62
+ $expectedResult = if ($tc.PSObject.Properties['expectedResult']) { $tc.expectedResult } else { '' }
79
63
 
80
64
  if ($PSCmdlet.ShouldProcess($tc.localId, "Create ADO Test Case")) {
81
65
  try {
82
- $wi = Invoke-RestMethod `
83
- -Method Patch `
84
- -Uri $createUrl `
85
- -Headers ($headers + @{ 'Content-Type' = 'application/json-patch+json' }) `
86
- -Body $body
66
+ $splatArgs = @{
67
+ Title = "[$($tc.localId)] $($tc.title)"
68
+ Confirm = $false
69
+ }
70
+ if ($steps.Count -gt 0) {
71
+ $splatArgs.Steps = $steps
72
+ $splatArgs.ExpectedResult = $expectedResult
73
+ }
87
74
 
75
+ $wi = New-AdoTestCase @splatArgs
88
76
  $wiId = $wi.id
89
77
  Write-Host " [CREATED] $($tc.localId) → WI #$wiId"
90
78
 
91
- # Add to suite
92
- $suiteUrl = "$apiBase/testplan/Plans/$PlanId/Suites/$suiteId/TestCase?api-version=7.1"
93
- $suiteBody = @(@{ workItem = @{ id = $wiId } }) | ConvertTo-Json
94
- Invoke-RestMethod -Method Post -Uri $suiteUrl -Headers $headers -Body $suiteBody | Out-Null
79
+ Add-AdoTestCaseToSuite -PlanId $PlanId -SuiteId $suiteId -TestCaseIds @($wiId) -Confirm:$false
95
80
 
96
81
  $results += [PSCustomObject]@{
97
82
  localId = $tc.localId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keber/qa-framework",
3
- "version": "1.8.0",
3
+ "version": "1.9.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",
package/scripts/init.js CHANGED
@@ -341,6 +341,40 @@ cd qa/07-automation/e2e && npm install && npx playwright install chromium
341
341
  const nextStepsPath = path.join(qaRoot, 'AGENT-NEXT-STEPS.md');
342
342
  writeIfMissing(nextStepsPath, nextStepsContent);
343
343
 
344
+ // --- Print post-install summary ---
345
+ const playwrightInstalled = fs.existsSync(path.join(cwd, 'node_modules', '@playwright', 'test'));
346
+ const adoQaInstalledFinal = fs.existsSync(path.join(cwd, 'node_modules', '@keber', 'ado-qa'));
347
+ const azureReporterInstalled = fs.existsSync(path.join(cwd, 'node_modules', '@alex_neo', 'playwright-azure-reporter'));
348
+
349
+ console.log('');
350
+ console.log(' @keber/qa-framework — scaffold complete');
351
+ console.log(' ----------------------------------------');
352
+ console.log(' Installed:');
353
+ console.log(' qa/ QA directory structure + spec templates');
354
+ console.log(' .github/skills/ QA agent skills (8 stages)');
355
+ console.log(' .github/instructions/ qa-framework.instructions.md');
356
+ console.log('');
357
+ console.log(' Optional integrations:');
358
+ if (playwrightInstalled) {
359
+ console.log(' [ok] Playwright @playwright/test');
360
+ } else {
361
+ console.log(' [ ] Playwright npm install @playwright/test');
362
+ console.log(' cd qa/07-automation/e2e && npm install && npx playwright install chromium');
363
+ }
364
+ if (adoQaInstalledFinal) {
365
+ console.log(' [ok] ADO integration @keber/ado-qa');
366
+ } else {
367
+ console.log(' [ ] ADO integration npm install @keber/ado-qa');
368
+ }
369
+ if (azureReporterInstalled) {
370
+ console.log(' [ok] ADO Playwright reporter (installed)');
371
+ } else {
372
+ console.log(' [ ] ADO Playwright reporter npm install @alex_neo/playwright-azure-reporter');
373
+ }
374
+ console.log('');
375
+ console.log(' Next: edit qa/qa-framework.config.json, then read qa/AGENT-NEXT-STEPS.md');
376
+ console.log('');
377
+
344
378
  // -----------------------------------------------------------------------
345
379
  // Helpers
346
380
  // -----------------------------------------------------------------------
@@ -19,6 +19,6 @@
19
19
  "dotenv": "^16.0.0"
20
20
  },
21
21
  "optionalDependencies": {
22
- "@alex_neo/playwright-azure-reporter": "^2.0.0"
22
+ "@alex_neo/playwright-azure-reporter": "^1.20.0"
23
23
  }
24
24
  }
@@ -0,0 +1,74 @@
1
+ function New-AdoTestCase {
2
+ <#
3
+ .SYNOPSIS
4
+ Creates a Test Case work item with optional steps.
5
+ Wraps New-AdoWorkItem -Type 'Test Case' and handles TCM-specific fields.
6
+
7
+ .PARAMETER Title Title of the Test Case (mandatory).
8
+ .PARAMETER Steps Ordered array of plain-text action strings.
9
+ Encoded as the XML format required by Microsoft.VSTS.TCM.Steps.
10
+ .PARAMETER Priority 1 (Critical) | 2 (High) | 3 (Medium) | 4 (Low).
11
+ .PARAMETER ExtraFields Hashtable of additional TCM or custom fields.
12
+
13
+ .EXAMPLE
14
+ New-AdoTestCase -Title 'Verify login with valid credentials' `
15
+ -Steps @('Navigate to /login', 'Enter username and password', 'Click Sign In') `
16
+ -Priority '2' -AssignedTo 'qa@contoso.com' -Tags 'login; smoke'
17
+ #>
18
+ [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
19
+ param(
20
+ [Parameter(Mandatory)][string]$Title,
21
+ [string]$Description,
22
+ [string]$AssignedTo,
23
+ [string]$AreaPath,
24
+ [string]$IterationPath,
25
+ [string]$Tags,
26
+ [ValidateSet('Design','Ready','Closed')]
27
+ [string]$State,
28
+ [string[]]$Steps,
29
+ [ValidateSet('1','2','3','4')]
30
+ [string]$Priority,
31
+ [hashtable]$ExtraFields = @{},
32
+ [string]$Org = $script:AdoSession.Org,
33
+ [string]$Project = $script:AdoSession.Project,
34
+ [string]$ApiV = $script:AdoSession.ApiV,
35
+ [hashtable]$Headers = $script:AdoSession.Headers
36
+ )
37
+
38
+ $extra = $ExtraFields.Clone()
39
+
40
+ if ($Priority) {
41
+ $extra['Microsoft.VSTS.Common.Priority'] = $Priority
42
+ }
43
+
44
+ if ($Steps -and $Steps.Count -gt 0) {
45
+ $stepsXml = '<steps id="0" last="{0}">' -f $Steps.Count
46
+ $stepIndex = 1
47
+ foreach ($action in $Steps) {
48
+ $escaped = [System.Security.SecurityElement]::Escape($action)
49
+ $stepsXml += '<step id="{0}" type="ActionStep"><parameterizedString isformatted="true">{1}</parameterizedString><parameterizedString isformatted="true"/></step>' -f $stepIndex, $escaped
50
+ $stepIndex++
51
+ }
52
+ $stepsXml += '</steps>'
53
+ $extra['Microsoft.VSTS.TCM.Steps'] = $stepsXml
54
+ }
55
+
56
+ $splatArgs = @{
57
+ Type = 'Test Case'
58
+ Title = $Title
59
+ ExtraFields = $extra
60
+ Org = $Org
61
+ Project = $Project
62
+ ApiV = $ApiV
63
+ Headers = $Headers
64
+ }
65
+ if ($Description) { $splatArgs.Description = $Description }
66
+ if ($AssignedTo) { $splatArgs.AssignedTo = $AssignedTo }
67
+ if ($AreaPath) { $splatArgs.AreaPath = $AreaPath }
68
+ if ($IterationPath) { $splatArgs.IterationPath = $IterationPath }
69
+ if ($Tags) { $splatArgs.Tags = $Tags }
70
+ if ($State) { $splatArgs.State = $State }
71
+
72
+ # WhatIf / Confirm propagate automatically via $WhatIfPreference / $ConfirmPreference
73
+ return New-AdoWorkItem @splatArgs
74
+ }