@sun-asterisk/sungen 3.2.28 → 3.2.30
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/dist/capabilities/discover.d.ts +29 -0
- package/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +144 -10
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/capabilities/registry.d.ts +18 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +25 -0
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +9 -0
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.js +1 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +2 -1
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +6 -2
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/inspect.js +1 -1
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts.map +1 -1
- package/dist/cli/commands/results-merge.js +9 -2
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/results-sanitize.d.ts +24 -0
- package/dist/cli/commands/results-sanitize.d.ts.map +1 -0
- package/dist/cli/commands/results-sanitize.js +126 -0
- package/dist/cli/commands/results-sanitize.js.map +1 -0
- package/dist/cli/commands/script-check.d.ts.map +1 -1
- package/dist/cli/commands/script-check.js +3 -1
- package/dist/cli/commands/script-check.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +3 -0
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/dashboard/templates/index.html +1 -1
- package/dist/exporters/api-testcase-formatter.d.ts +3 -6
- package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
- package/dist/exporters/api-testcase-formatter.js +7 -10
- package/dist/exporters/api-testcase-formatter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +1 -1
- package/dist/exporters/feature-parser.js +1 -1
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +18 -3
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/item-labels.d.ts +35 -0
- package/dist/exporters/matrix/item-labels.d.ts.map +1 -0
- package/dist/exporters/matrix/item-labels.js +90 -0
- package/dist/exporters/matrix/item-labels.js.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts +14 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.js +162 -0
- package/dist/exporters/matrix/render-coverage-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +3 -1
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-overview-sheet.d.ts +20 -0
- package/dist/exporters/matrix/render-overview-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-overview-sheet.js +32 -0
- package/dist/exporters/matrix/render-overview-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts +30 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.js +156 -0
- package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +41 -31
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +72 -425
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/result-totals.d.ts +23 -0
- package/dist/exporters/matrix/result-totals.d.ts.map +1 -0
- package/dist/exporters/matrix/result-totals.js +21 -0
- package/dist/exporters/matrix/result-totals.js.map +1 -0
- package/dist/exporters/matrix/testcase-summary-band.d.ts +30 -0
- package/dist/exporters/matrix/testcase-summary-band.d.ts.map +1 -0
- package/dist/exporters/matrix/testcase-summary-band.js +71 -0
- package/dist/exporters/matrix/testcase-summary-band.js.map +1 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +13 -0
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.d.ts +38 -0
- package/dist/exporters/matrix/xlsx-cells.d.ts.map +1 -0
- package/dist/exporters/matrix/xlsx-cells.js +52 -0
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -0
- package/dist/exporters/qa-env-secret-denylist.js +2 -2
- package/dist/exporters/qa-env-secret-denylist.js.map +1 -1
- package/dist/exporters/report-path-sanitizer.d.ts +38 -0
- package/dist/exporters/report-path-sanitizer.d.ts.map +1 -0
- package/dist/exporters/report-path-sanitizer.js +137 -0
- package/dist/exporters/report-path-sanitizer.js.map +1 -0
- package/dist/exporters/result-variants.d.ts.map +1 -1
- package/dist/exporters/result-variants.js +2 -1
- package/dist/exporters/result-variants.js.map +1 -1
- package/dist/exporters/sensitive-key.d.ts +31 -0
- package/dist/exporters/sensitive-key.d.ts.map +1 -0
- package/dist/exporters/sensitive-key.js +50 -0
- package/dist/exporters/sensitive-key.js.map +1 -0
- package/dist/exporters/test-data-resolver.d.ts +4 -1
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +12 -2
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/xlsx-report-builder.d.ts +12 -1
- package/dist/exporters/xlsx-report-builder.d.ts.map +1 -1
- package/dist/exporters/xlsx-report-builder.js +10 -2
- package/dist/exporters/xlsx-report-builder.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +10 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +20 -12
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/gherkin-parser/parent-scope-clause.d.ts +54 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.d.ts.map +1 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.js +109 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.js.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +23 -18
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +4 -19
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts +53 -0
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js +200 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js.map +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +62 -23
- package/dist/generators/test-generator/adapters/index.d.ts +1 -1
- package/dist/generators/test-generator/adapters/index.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +4 -19
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
- package/dist/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
- package/dist/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +21 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +20 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
- package/dist/generators/test-generator/code-generator.d.ts +37 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +165 -40
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +116 -6
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +115 -5
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +57 -11
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +163 -21
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +8 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +18 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js +40 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts +103 -10
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +266 -45
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +4 -19
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +17 -12
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +7 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +19 -2
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts +41 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.js +61 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.js.map +1 -0
- package/dist/generators/test-generator/utils/escape-js-string.d.ts +17 -0
- package/dist/generators/test-generator/utils/escape-js-string.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/escape-js-string.js +29 -0
- package/dist/generators/test-generator/utils/escape-js-string.js.map +1 -0
- package/dist/generators/test-generator/utils/nearest-match.d.ts +24 -0
- package/dist/generators/test-generator/utils/nearest-match.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/nearest-match.js +56 -0
- package/dist/generators/test-generator/utils/nearest-match.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts +58 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js +119 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.d.ts +25 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.js +54 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.d.ts +59 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.js +91 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +53 -2
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +273 -27
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-type-validation.d.ts +51 -0
- package/dist/generators/test-generator/utils/selector-type-validation.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-type-validation.js +77 -0
- package/dist/generators/test-generator/utils/selector-type-validation.js.map +1 -0
- package/dist/harness/audit.d.ts +3 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +51 -28
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability-plan.d.ts +4 -0
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +32 -2
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +145 -0
- package/dist/harness/data-driven-lint.d.ts.map +1 -1
- package/dist/harness/data-driven-lint.js +217 -33
- package/dist/harness/data-driven-lint.js.map +1 -1
- package/dist/harness/parse.d.ts +6 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +1 -1
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/repair.d.ts.map +1 -1
- package/dist/harness/repair.js +8 -1
- package/dist/harness/repair.js.map +1 -1
- package/dist/harness/script-check.d.ts +2 -2
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +186 -25
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts +48 -0
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +26 -1
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts +10 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +23 -2
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +6 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +16 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/report-sanitize-reporter-install.d.ts +24 -0
- package/dist/orchestrator/report-sanitize-reporter-install.d.ts.map +1 -0
- package/dist/orchestrator/report-sanitize-reporter-install.js +161 -0
- package/dist/orchestrator/report-sanitize-reporter-install.js.map +1 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +9 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +62 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +126 -11
- package/dist/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +12 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +19 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +62 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/dist/orchestrator/templates/playwright.config.ts +7 -0
- package/dist/orchestrator/templates/readme.md +1 -1
- package/dist/orchestrator/templates/specs-checked-state.d.ts +83 -0
- package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-checked-state.js +261 -0
- package/dist/orchestrator/templates/specs-checked-state.js.map +1 -0
- package/dist/orchestrator/templates/specs-checked-state.ts +324 -0
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts +36 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js +155 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js.map +1 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
- package/dist/orchestrator/templates/specs-url-assert.d.ts +50 -25
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +65 -24
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +101 -28
- package/dist/orchestrator/templates/wdio.conf.ts +105 -1
- package/dist/utils/selector-types.d.ts +43 -1
- package/dist/utils/selector-types.d.ts.map +1 -1
- package/dist/utils/selector-types.js +51 -7
- package/dist/utils/selector-types.js.map +1 -1
- package/package.json +3 -3
- package/src/capabilities/discover.ts +146 -10
- package/src/capabilities/registry.ts +30 -0
- package/src/cli/commands/audit.ts +9 -0
- package/src/cli/commands/capability.ts +1 -1
- package/src/cli/commands/delivery.ts +2 -1
- package/src/cli/commands/generate.ts +5 -2
- package/src/cli/commands/inspect.ts +1 -1
- package/src/cli/commands/results-merge.ts +9 -2
- package/src/cli/commands/results-sanitize.ts +98 -0
- package/src/cli/commands/script-check.ts +3 -1
- package/src/cli/commands/update.ts +3 -0
- package/src/dashboard/templates/index.html +1 -1
- package/src/exporters/api-testcase-formatter.ts +5 -7
- package/src/exporters/feature-parser.ts +1 -1
- package/src/exporters/matrix/build.ts +17 -3
- package/src/exporters/matrix/item-labels.ts +78 -0
- package/src/exporters/matrix/render-coverage-sheet.ts +170 -0
- package/src/exporters/matrix/render-csv.ts +3 -1
- package/src/exporters/matrix/render-overview-sheet.ts +51 -0
- package/src/exporters/matrix/render-testcase-sheet.ts +178 -0
- package/src/exporters/matrix/render-xlsx.ts +67 -448
- package/src/exporters/matrix/result-totals.ts +37 -0
- package/src/exporters/matrix/testcase-summary-band.ts +74 -0
- package/src/exporters/matrix/wording.ts +13 -0
- package/src/exporters/matrix/xlsx-cells.ts +56 -0
- package/src/exporters/qa-env-secret-denylist.ts +1 -1
- package/src/exporters/report-path-sanitizer.ts +144 -0
- package/src/exporters/result-variants.ts +2 -1
- package/src/exporters/sensitive-key.ts +48 -0
- package/src/exporters/test-data-resolver.ts +12 -2
- package/src/exporters/xlsx-report-builder.ts +16 -2
- package/src/generators/gherkin-parser/index.ts +22 -11
- package/src/generators/gherkin-parser/parent-scope-clause.ts +130 -0
- package/src/generators/test-generator/adapters/adapter-interface.ts +25 -3
- package/src/generators/test-generator/adapters/appium/appium-adapter.ts +4 -4
- package/src/generators/test-generator/adapters/appium/os-support.ts +176 -0
- package/src/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +62 -23
- package/src/generators/test-generator/adapters/index.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
- package/src/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
- package/src/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +21 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +20 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
- package/src/generators/test-generator/code-generator.ts +181 -46
- package/src/generators/test-generator/diagnostics.ts +117 -6
- package/src/generators/test-generator/patterns/index.ts +175 -24
- package/src/generators/test-generator/patterns/types.ts +8 -0
- package/src/generators/test-generator/patterns/with-parent-scope.ts +41 -0
- package/src/generators/test-generator/step-mapper.ts +277 -43
- package/src/generators/test-generator/template-engine.ts +21 -21
- package/src/generators/test-generator/utils/data-resolver.ts +23 -2
- package/src/generators/test-generator/utils/dialog-scope-identity.ts +71 -0
- package/src/generators/test-generator/utils/escape-js-string.ts +24 -0
- package/src/generators/test-generator/utils/nearest-match.ts +55 -0
- package/src/generators/test-generator/utils/selector-entry-fields.ts +131 -0
- package/src/generators/test-generator/utils/selector-entry-walk.ts +58 -0
- package/src/generators/test-generator/utils/selector-nth-validation.ts +99 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +333 -28
- package/src/generators/test-generator/utils/selector-type-validation.ts +87 -0
- package/src/harness/audit.ts +55 -30
- package/src/harness/capability-plan.ts +33 -1
- package/src/harness/catalog/drivers.yaml +1 -1
- package/src/harness/catalog/universal-viewpoints.yaml +145 -0
- package/src/harness/data-driven-lint.ts +192 -32
- package/src/harness/parse.ts +7 -1
- package/src/harness/repair.ts +8 -1
- package/src/harness/script-check.ts +186 -21
- package/src/harness/sensors.ts +75 -1
- package/src/harness/viewpoint-ledger.ts +26 -5
- package/src/index.ts +7 -0
- package/src/orchestrator/project-initializer.ts +17 -0
- package/src/orchestrator/report-sanitize-reporter-install.ts +109 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/src/orchestrator/templates/ai-src/commands/run-test.md +9 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +62 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +126 -11
- package/src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +12 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +19 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +62 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/src/orchestrator/templates/playwright.config.ts +7 -0
- package/src/orchestrator/templates/readme.md +1 -1
- package/src/orchestrator/templates/specs-checked-state.ts +324 -0
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
- package/src/orchestrator/templates/specs-url-assert.ts +101 -28
- package/src/orchestrator/templates/wdio.conf.ts +105 -1
- package/src/utils/selector-types.ts +54 -5
|
@@ -150,6 +150,19 @@ export function renderExpected(raw: string): string {
|
|
|
150
150
|
|
|
151
151
|
/** Precondition wording: a state, not an action ("The user is signed out."). */
|
|
152
152
|
export function renderPrecondition(raw: string): string {
|
|
153
|
+
// A capture step is an INSTRUCTION the tester performs, not a state — the same
|
|
154
|
+
// reasoning renderSetupInstruction applies to manual `Setup:` lines, and "The
|
|
155
|
+
// user remember X text as …" was broken grammar besides.
|
|
156
|
+
// It also carries the ONLY thing that resolves the `as {{v}}` token: on the
|
|
157
|
+
// trigger path build.ts renders `renderAction(sub(s))`, so substituteDisplayVars
|
|
158
|
+
// leaves that token for renderAction to quote. The precondition path renders
|
|
159
|
+
// FIRST and substitutes second, so renderAction was never reached, sub then
|
|
160
|
+
// declined to touch the token by design, and the raw brace shipped into the
|
|
161
|
+
// deliverable — Gate D rejected the whole unit over it (#684).
|
|
162
|
+
// Guarded on the token form exactly as renderExpected does, so a Given that
|
|
163
|
+
// merely says "remember" in prose keeps its state wording.
|
|
164
|
+
if (RUNTIME_CAPTURE.test(raw) && /\bas\s+\{\{/.test(raw)) return renderAction(raw);
|
|
165
|
+
|
|
153
166
|
const s = raw.trim().replace(/^(User|The user)\s+/i, '');
|
|
154
167
|
const m = s.match(/^is on \[([^\]]+)\] page(.*)$/i);
|
|
155
168
|
if (m) return sentence(`The user is on the ${m[1]} page${m[2] ?? ''}`);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cell primitives for the matrix workbook renderers.
|
|
3
|
+
*
|
|
4
|
+
* One place for the palette, the header/data cell styling, and the DRAFT
|
|
5
|
+
* banner — the testcase, coverage and overview renderers all draw from here so
|
|
6
|
+
* the four sheets stay visually one document.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as ExcelJS from 'exceljs';
|
|
10
|
+
import { allBordersBlack, ARIAL_FONT, BLACK, LAVENDER } from '../xlsx-report-builder';
|
|
11
|
+
import { MatrixModel } from './types';
|
|
12
|
+
|
|
13
|
+
export const SOFT_BLUE = { argb: 'FFDCE6F1' };
|
|
14
|
+
export const DRAFT_RED = { argb: 'FFC00000' };
|
|
15
|
+
export const GAP_FILL = { argb: 'FFFCE4EC' };
|
|
16
|
+
/** The five words a variant Result cell may hold — the vocabulary COUNTIF counts. */
|
|
17
|
+
export const RESULT_STATES = 'Passed,Failed,Blocked,Pending,N/A';
|
|
18
|
+
|
|
19
|
+
export interface DataCellOpts {
|
|
20
|
+
bold?: boolean;
|
|
21
|
+
fill?: { argb: string };
|
|
22
|
+
center?: boolean;
|
|
23
|
+
color?: { argb: string };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function headerCell(c: ExcelJS.Cell, label: string): void {
|
|
27
|
+
c.value = label;
|
|
28
|
+
c.font = { bold: true, size: 10, color: BLACK, name: ARIAL_FONT };
|
|
29
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: LAVENDER };
|
|
30
|
+
c.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
|
31
|
+
c.border = allBordersBlack;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function dataCell(c: ExcelJS.Cell, value: ExcelJS.CellValue, opts?: DataCellOpts): void {
|
|
35
|
+
// An empty value stays a genuinely blank cell — never an empty shared string,
|
|
36
|
+
// which some renderers display as its string-table index (review M-03).
|
|
37
|
+
if (value !== '' && value !== undefined && value !== null) c.value = value;
|
|
38
|
+
c.font = { bold: opts?.bold ?? false, size: 10, name: ARIAL_FONT, ...(opts?.color ? { color: opts.color } : {}) };
|
|
39
|
+
c.alignment = { horizontal: opts?.center ? 'center' : 'left', vertical: 'top', wrapText: true };
|
|
40
|
+
c.border = allBordersBlack;
|
|
41
|
+
if (opts?.fill) c.fill = { type: 'pattern', pattern: 'solid', fgColor: opts.fill };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Red DRAFT banner on row 5 when the matrix is not approved for official delivery.
|
|
46
|
+
* Spans A..H — the same width as the masthead above it, on every sheet.
|
|
47
|
+
*/
|
|
48
|
+
export function draftBanner(ws: ExcelJS.Worksheet, model: MatrixModel): void {
|
|
49
|
+
if (model.manifest.approvalState !== 'draft') return;
|
|
50
|
+
ws.mergeCells('A5:H5');
|
|
51
|
+
const c = ws.getCell('A5');
|
|
52
|
+
c.value = 'DRAFT — NOT APPROVED FOR OFFICIAL DELIVERY (review-required findings or proposed groups remain)';
|
|
53
|
+
c.font = { bold: true, size: 11, name: ARIAL_FONT, color: { argb: 'FFFFFFFF' } };
|
|
54
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: DRAFT_RED };
|
|
55
|
+
c.alignment = { horizontal: 'center', vertical: 'middle' };
|
|
56
|
+
}
|
|
@@ -26,7 +26,7 @@ import * as fs from 'fs';
|
|
|
26
26
|
import * as path from 'path';
|
|
27
27
|
// Sensitive-name signal shared with the API exporter's key/body-field classifier —
|
|
28
28
|
// here it is applied to the ENV VAR NAME (QA_ACCESS_TOKEN), not the yaml key.
|
|
29
|
-
import { SENSITIVE_NAME } from './
|
|
29
|
+
import { SENSITIVE_NAME } from './sensitive-key';
|
|
30
30
|
|
|
31
31
|
/** `${QA_*}` env reference — same shape the runtime resolver allowlists (uppercase, QA_ prefix). */
|
|
32
32
|
const QA_ENV_REF = /\$\{(QA_[A-Z0-9_]+)\}/g;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report path sanitizer — rewrites the operator's machine paths out of a result/report JSON.
|
|
3
|
+
*
|
|
4
|
+
* Playwright's JSON reporter (and sungen's own script-check report) record ABSOLUTE paths:
|
|
5
|
+
* `config.configFile`, `rootDir`, `projects[].testDir/outputDir`, every `error.location.file`,
|
|
6
|
+
* attachment paths, and the paths quoted inside `stack`/`message`/`snippet` text. Teams commit these
|
|
7
|
+
* files next to the spec (the delivery reads them), so a username and a machine layout travel into
|
|
8
|
+
* the repository and the deliverable. Nothing downstream needs them absolute: delivery, dashboard and
|
|
9
|
+
* merge read titles, statuses and attachment names.
|
|
10
|
+
*
|
|
11
|
+
* Rules (pure — no I/O, no mutation of the input):
|
|
12
|
+
* - A string that IS an absolute path (POSIX, `C:\…`, `file:///…`) under the report's project root
|
|
13
|
+
* becomes the POSIX path relative to that root (the root itself → `.`); outside the root a path that
|
|
14
|
+
* names a machine (a home directory, a drive) keeps only its basename; any other absolute string —
|
|
15
|
+
* a URL route such as `/products` — is left alone.
|
|
16
|
+
* - Inside longer text (stack traces, messages, snippets) every occurrence of the root becomes a
|
|
17
|
+
* relative path, and a home directory (`/Users/<name>/`, `/home/<name>/`, `C:\Users\<name>\`) becomes
|
|
18
|
+
* `~/` — the text is rewritten, never dropped, so the trace stays readable.
|
|
19
|
+
* - URLs are untouched: a `/home/...` inside `https://host/home/...` is not a filesystem path.
|
|
20
|
+
* - Idempotent: sanitizing an already-sanitized report rewrites nothing.
|
|
21
|
+
*
|
|
22
|
+
* The root is the report's OWN `dirname(config.configFile)` when it carries one (self-describing —
|
|
23
|
+
* a report produced on another machine relativizes against ITS root, not the reader's cwd); callers
|
|
24
|
+
* fall back to their cwd otherwise.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export interface SanitizeOptions {
|
|
28
|
+
/** Project root the report's paths are made relative to (absolute; POSIX or Windows). */
|
|
29
|
+
root: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SanitizeOutcome<T> {
|
|
33
|
+
doc: T;
|
|
34
|
+
/** Number of string values that changed. */
|
|
35
|
+
rewritten: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ABSOLUTE = /^(?:file:\/\/\/?|[A-Za-z]:[\\/]|\/)/;
|
|
39
|
+
const WINDOWS_DRIVE = /^[A-Za-z]:/;
|
|
40
|
+
/**
|
|
41
|
+
* A home directory quoted inside text, not embedded in a URL or a longer token: `/Users/<name>/`,
|
|
42
|
+
* `/home/<name>/`, `C:\Users\<name>\`, each optionally behind a `file://` scheme (Node ESM errors
|
|
43
|
+
* quote modules as `file:///Users/<name>/…`).
|
|
44
|
+
*/
|
|
45
|
+
const HOME_DIR = /(?<![\w.:/\\-])(?:file:\/\/\/?)?(?:\/(?:Users|home)\/[^/\\\s"'`)\]]+[/\\]|[A-Za-z]:[\\/]Users[\\/][^/\\\s"'`)\]]+[\\/])/g;
|
|
46
|
+
|
|
47
|
+
/** The project root a report describes itself against, if it says. */
|
|
48
|
+
export function detectReportRoot(doc: unknown): string | undefined {
|
|
49
|
+
const configFile = (doc as { config?: { configFile?: unknown } } | null)?.config?.configFile;
|
|
50
|
+
if (typeof configFile !== 'string' || !ABSOLUTE.test(configFile)) return undefined;
|
|
51
|
+
const p = toPosix(stripFileScheme(configFile));
|
|
52
|
+
const i = p.lastIndexOf('/');
|
|
53
|
+
return i > 0 ? p.slice(0, i) : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function sanitizeReportPaths<T>(doc: T, opts: SanitizeOptions): SanitizeOutcome<T> {
|
|
57
|
+
const root = normalizeRoot(opts.root);
|
|
58
|
+
// A root of `/` (or empty) would make every `/` a "root prefix" and shred the document.
|
|
59
|
+
if (root.posix.length < 2) return { doc, rewritten: 0 };
|
|
60
|
+
let rewritten = 0;
|
|
61
|
+
const rewrite = (value: string): string => {
|
|
62
|
+
const next = sanitizeString(value, root);
|
|
63
|
+
if (next !== value) rewritten++;
|
|
64
|
+
return next;
|
|
65
|
+
};
|
|
66
|
+
const walk = (v: unknown): unknown => {
|
|
67
|
+
if (typeof v === 'string') return rewrite(v);
|
|
68
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
69
|
+
if (v && typeof v === 'object') {
|
|
70
|
+
const out: Record<string, unknown> = {};
|
|
71
|
+
for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = walk(x);
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
return v;
|
|
75
|
+
};
|
|
76
|
+
return { doc: walk(doc) as T, rewritten };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface Root { posix: string; caseInsensitive: boolean; pattern: RegExp }
|
|
80
|
+
|
|
81
|
+
function normalizeRoot(raw: string): Root {
|
|
82
|
+
const posix = toPosix(stripFileScheme(raw)).replace(/\/+$/, '');
|
|
83
|
+
const caseInsensitive = WINDOWS_DRIVE.test(posix);
|
|
84
|
+
// The root as it may appear inside text: either separator style, optional file:// scheme, and the
|
|
85
|
+
// separator that follows it (consumed, so what remains starts at the relative path).
|
|
86
|
+
const body = posix.split('/').map(escapeRegExp).join('[\\\\/]');
|
|
87
|
+
return { posix, caseInsensitive, pattern: new RegExp(`(?:file:\\/\\/\\/?)?${body}(?:[\\\\/](?![\\\\/])|(?=$|[\\s"'\`):,]))`, caseInsensitive ? 'gi' : 'g') };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function sanitizeString(value: string, root: Root): string {
|
|
91
|
+
const trimmed = value.trim();
|
|
92
|
+
if (ABSOLUTE.test(trimmed) && !/\s/.test(trimmed)) {
|
|
93
|
+
// The whole value is one path.
|
|
94
|
+
const p = toPosix(stripFileScheme(trimmed)).replace(/\/+$/, '');
|
|
95
|
+
const rel = relativeToRoot(p, root);
|
|
96
|
+
if (rel !== undefined) return rel;
|
|
97
|
+
// Outside the root: only a path that names a MACHINE (a home directory, a drive) is cut to its
|
|
98
|
+
// basename. Any other absolute string is left alone — `/products` in an audit report is a route.
|
|
99
|
+
if (isMachinePath(p)) {
|
|
100
|
+
// Another machine's checkout of THIS project (`…/work/<project>/specs/x.ts`, root `/home/me/<project>`):
|
|
101
|
+
// the part after the project directory is the honest relative path; only then fall back to the basename.
|
|
102
|
+
const marker = '/' + root.posix.slice(root.posix.lastIndexOf('/') + 1) + '/';
|
|
103
|
+
const i = marker.length > 2 ? (root.caseInsensitive ? p.toLowerCase().indexOf(marker.toLowerCase()) : p.indexOf(marker)) : -1;
|
|
104
|
+
if (i >= 0) return p.slice(i + marker.length);
|
|
105
|
+
return p.slice(p.lastIndexOf('/') + 1) || p;
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
// Paths quoted inside text: root → relative (the root alone → `.`), home → ~/.
|
|
110
|
+
const out = value.replace(root.pattern, (m: string) => (/[\\/]$/.test(m) ? '' : '.'));
|
|
111
|
+
return out.replace(HOME_DIR, '~/');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A path that identifies the operator's machine: a drive-letter path, or a home directory with
|
|
116
|
+
* something UNDER the user's folder (`/Users/<name>/<…>`). Case-sensitive and two segments deep on
|
|
117
|
+
* purpose — `/users/42/orders` and `/home/dashboard` are routes an audit report lists, not homes.
|
|
118
|
+
*/
|
|
119
|
+
function isMachinePath(posixPath: string): boolean {
|
|
120
|
+
return WINDOWS_DRIVE.test(posixPath) || /\/(?:Users|home)\/[^/]+\/./.test(posixPath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function relativeToRoot(p: string, root: Root): string | undefined {
|
|
124
|
+
const a = root.caseInsensitive ? p.toLowerCase() : p;
|
|
125
|
+
const b = root.caseInsensitive ? root.posix.toLowerCase() : root.posix;
|
|
126
|
+
if (a === b) return '.';
|
|
127
|
+
if (a.startsWith(b + '/')) return p.slice(root.posix.length + 1);
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function stripFileScheme(p: string): string {
|
|
132
|
+
if (!p.startsWith('file://')) return p;
|
|
133
|
+
const rest = p.slice('file://'.length);
|
|
134
|
+
// file:///C:/x → C:/x ; file:///home/x → /home/x
|
|
135
|
+
return /^\/[A-Za-z]:/.test(rest) ? rest.slice(1) : rest;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function toPosix(p: string): string {
|
|
139
|
+
return p.replace(/\\/g, '/');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function escapeRegExp(s: string): string {
|
|
143
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
144
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { MergedScenario } from './scenario-merger';
|
|
7
7
|
import { PlaywrightResult } from './types';
|
|
8
|
+
import { maskCredentialValue } from './sensitive-key';
|
|
8
9
|
|
|
9
10
|
export interface ResultVariant {
|
|
10
11
|
/** Appended to the scenario display name: `''` for a plain scenario, ` — <label>` per
|
|
@@ -102,6 +103,6 @@ export function formatVariantRow(variant: ResultVariant, separator: string = '\n
|
|
|
102
103
|
const useBullet = separator.includes('\n');
|
|
103
104
|
return Object.entries(variant.row)
|
|
104
105
|
.filter(([k]) => !k.startsWith('__'))
|
|
105
|
-
.map(([k, v]) => `${useBullet ? '• ' : ''}${k}: ${v}`)
|
|
106
|
+
.map(([k, v]) => `${useBullet ? '• ' : ''}${k}: ${maskCredentialValue(k, String(v))}`)
|
|
106
107
|
.join(separator);
|
|
107
108
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential-name matching shared by the delivery exporters (key-based masking, value-based
|
|
3
|
+
* redaction, the qa-env secret denylist). Kept in its own module so the test-data loader can
|
|
4
|
+
* mask a cell without importing the API formatter.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Credential name matcher (substring, not anchored) for body-field keys and test-data keys —
|
|
9
|
+
* catches variants like confirmPassword / new_password / password_confirmation / access_token /
|
|
10
|
+
* client_secret / otp / passcode.
|
|
11
|
+
*/
|
|
12
|
+
export const SENSITIVE_NAME = /pass(word|code)?|passwd|pwd|secret|token|otp|api[-_]?key|apikey|credential|authorization|signature/i;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The segment of a flattened test-data key that NAMES the value: the last dot-segment, skipping
|
|
16
|
+
* array indexes. `accounts.0.password` → `password`; `passwords.0` → `passwords`; `tokens.0.label`
|
|
17
|
+
* → `label`. Matching the whole key would treat every field under a list called `tokens` as a
|
|
18
|
+
* secret — `tokens.0.label: "New"` is a label, not a token.
|
|
19
|
+
*/
|
|
20
|
+
export function sensitivityKeyName(key: string): string {
|
|
21
|
+
const segments = key.split('.').filter((seg) => !/^\d+$/.test(seg));
|
|
22
|
+
return segments[segments.length - 1] ?? key;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Key path that walks into an array (`list.0.field`, `tags.0`). */
|
|
26
|
+
const HAS_INDEX_SEGMENT = /(^|\.)\d+(\.|$)/;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Is this test-data key a credential? A non-indexed key is judged as a whole, exactly as before
|
|
30
|
+
* arrays were flattened per index — `api_key.value` and `token.value` stay credentials. An indexed
|
|
31
|
+
* key is judged by the segment that names the value, so `tokens.0.label` is not one.
|
|
32
|
+
*/
|
|
33
|
+
export function isSensitiveKey(key: string): boolean {
|
|
34
|
+
if (SENSITIVE_NAME.test(sensitivityKeyName(key))) return true;
|
|
35
|
+
return !HAS_INDEX_SEGMENT.test(key) && SENSITIVE_NAME.test(key);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A value that still carries a raw `${QA_*}` env placeholder (nothing substituted at export time). */
|
|
39
|
+
const UNRESOLVED_QA_ENV_REF = /\$\{QA_[A-Z0-9_]+\}/;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What a Test Data cell may show for `key`: `***` for a credential-named key, else the value. An
|
|
43
|
+
* unresolved `${QA_*}` placeholder is not a value but the name of the variable the tester must
|
|
44
|
+
* set — it stays literal (also inside a composed value like `Bearer ${QA_TOKEN}`) so they can find it.
|
|
45
|
+
*/
|
|
46
|
+
export function maskCredentialValue(key: string, value: string): string {
|
|
47
|
+
return isSensitiveKey(key) && !UNRESOLVED_QA_ENV_REF.test(value) ? '***' : value;
|
|
48
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { parse as parseYaml } from 'yaml';
|
|
8
|
+
import { maskCredentialValue } from './sensitive-key';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Load test-data.yaml into a flat key→value map.
|
|
@@ -12,7 +13,10 @@ import { parse as parseYaml } from 'yaml';
|
|
|
12
13
|
* kudos:
|
|
13
14
|
* recipient: "An"
|
|
14
15
|
* become { "kudos.recipient": "An" }, matching {{kudos.recipient}} refs.
|
|
15
|
-
*
|
|
16
|
+
* An array is kept whole as a JSON string under its own key (the `@cases` dataset
|
|
17
|
+
* reader parses it back) AND flattened per index, so `{{list.0.field}}` /
|
|
18
|
+
* `{{tags.0}}` / `{{m.0.1}}` refs resolve exactly as the compile-time resolver
|
|
19
|
+
* resolves them. Returns empty map if both files are missing.
|
|
16
20
|
*
|
|
17
21
|
* When `SUNGEN_ENV` is set, also merges the matching overlay file alongside
|
|
18
22
|
* the base (e.g. `login.yaml` + `login.vi.yaml`). Shallow root-level merge —
|
|
@@ -89,6 +93,10 @@ function flatten(obj: Record<string, unknown>, prefix: string, out: Record<strin
|
|
|
89
93
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
90
94
|
if (Array.isArray(value)) {
|
|
91
95
|
out[path] = JSON.stringify(value);
|
|
96
|
+
// Per-index leaves (`list.0.field`, `tags.0`, nested `m.0.1`) — dot form only, matching
|
|
97
|
+
// the compile-time resolver; a shorter overlay list replaces the whole array, so no
|
|
98
|
+
// stale `.N` leaves survive from the base.
|
|
99
|
+
flatten(Object.fromEntries(value.map((el, i) => [String(i), el])), path, out);
|
|
92
100
|
} else if (typeof value === 'object') {
|
|
93
101
|
flatten(value as Record<string, unknown>, path, out);
|
|
94
102
|
} else {
|
|
@@ -122,7 +130,9 @@ export function formatTestData(
|
|
|
122
130
|
for (const key of referencedVars) {
|
|
123
131
|
const value = testData[key];
|
|
124
132
|
if (value === undefined) continue;
|
|
125
|
-
|
|
133
|
+
// A credential-named key shows `***` in this cell, whatever the unit kind (the same rule the
|
|
134
|
+
// matrix data column and @cases rows apply); unresolved `${QA_*}` placeholders stay literal.
|
|
135
|
+
const line = `${key}: ${truncate(maskCredentialValue(key, value), maxLen)}`;
|
|
126
136
|
pairs.push(useBullet ? `• ${line}` : line);
|
|
127
137
|
}
|
|
128
138
|
return pairs.join(separator);
|
|
@@ -239,11 +239,20 @@ export const OVERVIEW_HEADERS = ['#', 'Sheet name', 'Passed', 'Failed', 'Pending
|
|
|
239
239
|
*
|
|
240
240
|
* `Remain = Total − Passed − N/A` (i.e. Failed + Pending) so the number
|
|
241
241
|
* reconciles with the "Remaining" cell in each detail sheet's summary band.
|
|
242
|
+
*
|
|
243
|
+
* Only `sheetName` and `summary` are read, so a caller that has no data rows to
|
|
244
|
+
* hand over (the matrix exporter renders its own detail sheets) passes just
|
|
245
|
+
* those two fields rather than inventing an empty `BuilderSheet`.
|
|
246
|
+
*
|
|
247
|
+
* `opts.titleLabel` / `opts.formNo` override the masthead; omitted, the title
|
|
248
|
+
* falls back to `<first sheet's screen> TESTCASE` and the form number to the
|
|
249
|
+
* builder default — which is what the legacy path has always produced.
|
|
242
250
|
*/
|
|
243
251
|
export function addOverviewSheet(
|
|
244
252
|
wb: ExcelJS.Workbook,
|
|
245
|
-
sheets: BuilderSheet
|
|
253
|
+
sheets: Array<Pick<BuilderSheet, 'sheetName' | 'summary'>>,
|
|
246
254
|
sungenVersion?: string,
|
|
255
|
+
opts?: { titleLabel?: string; formNo?: string },
|
|
247
256
|
): void {
|
|
248
257
|
const ws = wb.addWorksheet('Overview');
|
|
249
258
|
|
|
@@ -262,7 +271,12 @@ export function addOverviewSheet(
|
|
|
262
271
|
];
|
|
263
272
|
|
|
264
273
|
const titleScreen = sheets[0]?.summary.screen ?? '';
|
|
265
|
-
renderReportHeaderBand(
|
|
274
|
+
renderReportHeaderBand(
|
|
275
|
+
wb, ws,
|
|
276
|
+
opts?.titleLabel ?? `${titleScreen.toUpperCase()} TESTCASE`,
|
|
277
|
+
sungenVersion,
|
|
278
|
+
opts?.formNo,
|
|
279
|
+
);
|
|
266
280
|
|
|
267
281
|
// Roll-up table: header on row 7, first column B (index 2).
|
|
268
282
|
const HEADER_ROW = 7;
|
|
@@ -6,6 +6,7 @@ import path from 'path';
|
|
|
6
6
|
import { SelectorResolver } from '../test-generator/utils/selector-resolver';
|
|
7
7
|
import { maskRefsForMatching } from '../test-generator/utils/mask-refs-for-matching';
|
|
8
8
|
import type { StateClause } from '../test-generator/patterns/types';
|
|
9
|
+
import { extractParentScopeClause } from './parent-scope-clause';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Canonical state clauses, longest-first. Order is load-bearing twice over: `sorted ascending` must
|
|
@@ -13,7 +14,7 @@ import type { StateClause } from '../test-generator/patterns/types';
|
|
|
13
14
|
* substring never misclassifies — the same hazard, and the same fix, as `qualifiedState()` in
|
|
14
15
|
* driver-ui's assertion-patterns.
|
|
15
16
|
*/
|
|
16
|
-
const STATE_CLAUSES: ReadonlyArray<StateClause> = [
|
|
17
|
+
export const STATE_CLAUSES: ReadonlyArray<StateClause> = [
|
|
17
18
|
'sorted ascending', 'sorted descending',
|
|
18
19
|
'unchecked', 'checked',
|
|
19
20
|
'hidden', 'visible', 'disabled', 'enabled', 'focused', 'empty', 'loading', 'selected',
|
|
@@ -71,7 +72,10 @@ export interface ParsedStep {
|
|
|
71
72
|
nth?: number; // Positional index from "[Element] field 3" → 3 (0 = not specified)
|
|
72
73
|
featurePath?: string; // NEW: Feature path for page type (e.g., "/", "/dashboard")
|
|
73
74
|
parentRef?: string; // Parent scope reference: "in [Parent Name] parentType" → parentRef = "Parent Name"
|
|
74
|
-
parentType?: string; // Parent scope type
|
|
75
|
+
parentType?: string; // Parent scope container type — see PARENT_CONTAINER_TYPES
|
|
76
|
+
parentValueRef?: string; // Parent scoped BY CONTENT: "in [Job Card] card with {{job_title}}" → "job_title"
|
|
77
|
+
parentValueText?: string; // Same, written as a quoted literal: `… card with "Senior Dev"` → "Senior Dev"
|
|
78
|
+
unsupportedScopeType?: string; // An `in [X] <word>` clause whose <word> is no container — dropped, reported as SG-W028
|
|
75
79
|
dataTable?: ParsedDataTable; // Inline data table (Cucumber DataTable syntax)
|
|
76
80
|
state?: StateClause; // Trailing canonical state clause, e.g. "hidden" from "… is hidden" (drives SG-E021)
|
|
77
81
|
}
|
|
@@ -231,17 +235,21 @@ export class GherkinParser {
|
|
|
231
235
|
private parseStep(step: Messages.Step): ParsedStep {
|
|
232
236
|
let text = step.text;
|
|
233
237
|
|
|
234
|
-
// === Extract parent scoping: "in [Parent Name]
|
|
235
|
-
// Must be extracted first so the parent [brackets] don't interfere with target selector
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
+
// === Extract parent scoping: "in [Parent Name] containerType [with <value>]" ===
|
|
239
|
+
// Must be extracted first so the parent [brackets] don't interfere with target selector
|
|
240
|
+
// extraction. See parent-scope-clause.ts for the container vocabulary and why an unrecognized
|
|
241
|
+
// clause is reported (SG-W028) rather than left to be absorbed into the target's tokens.
|
|
242
|
+
const { clause: parentScope, unsupportedType } = extractParentScopeClause(text);
|
|
238
243
|
let parentRef: string | undefined;
|
|
239
244
|
let parentType: string | undefined;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
let parentValueRef: string | undefined;
|
|
246
|
+
let parentValueText: string | undefined;
|
|
247
|
+
if (parentScope) {
|
|
248
|
+
parentRef = parentScope.ref;
|
|
249
|
+
parentType = parentScope.type;
|
|
250
|
+
parentValueRef = parentScope.valueRef;
|
|
251
|
+
parentValueText = parentScope.valueText;
|
|
252
|
+
text = parentScope.remainingText;
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
// Extract selector reference: [Email Address] or [login:email_field] (legacy)
|
|
@@ -321,6 +329,9 @@ export class GherkinParser {
|
|
|
321
329
|
nth,
|
|
322
330
|
parentRef,
|
|
323
331
|
parentType,
|
|
332
|
+
parentValueRef,
|
|
333
|
+
parentValueText,
|
|
334
|
+
unsupportedScopeType: unsupportedType,
|
|
324
335
|
dataTable,
|
|
325
336
|
state: extractStateClause(step.text),
|
|
326
337
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The trailing scope clause — `… in [Parent] <containerType>` and its optional `with <value>` tail.
|
|
3
|
+
*
|
|
4
|
+
* Lifted out of `parseStep` because the clause failed SILENTLY (#667): the container whitelist held
|
|
5
|
+
* five types, anything else fell through, and the unmatched words stayed inline in the sentence
|
|
6
|
+
* where the first-match extractors below simply ignored them. The step compiled clean
|
|
7
|
+
* and the assertion ran against the WHOLE page instead of the named container — a test that passes
|
|
8
|
+
* for the wrong reason, findable only by diffing the generated spec against the Gherkin.
|
|
9
|
+
*
|
|
10
|
+
* So this module answers two questions, not one:
|
|
11
|
+
* - what the clause MEANS when it is recognized (`parentRef` / `parentType` / the value tail), and
|
|
12
|
+
* - what was DISCARDED when it is not (`unsupportedType`), so the compiler can say so (SG-W028)
|
|
13
|
+
* instead of quietly truncating the sentence.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Container types a step may scope into. Every one of these is something that HOLDS other elements
|
|
18
|
+
* on a page; `button`, `field`, `badge` and friends are deliberately absent — a leaf cannot be a
|
|
19
|
+
* parent, and admitting one would turn a typo into a silently wrong locator rather than a report.
|
|
20
|
+
*
|
|
21
|
+
* Ordered longest-first where one type is a prefix of another (`list-item` before `list`), because
|
|
22
|
+
* the alternation is matched left-to-right: `in [X] list-item` must not resolve as `list` with a
|
|
23
|
+
* stray `-item` left behind.
|
|
24
|
+
*
|
|
25
|
+
* `header` and `footer` are NOT here, though both hold elements. The auto-inferred fallback role
|
|
26
|
+
* for them would be the page landmarks `banner`/`contentinfo`, and a `<header>` nested inside an
|
|
27
|
+
* `<article>`/`<section>` — which is exactly how a repeating card is marked up — carries neither.
|
|
28
|
+
* That fallback would match nothing and fail for a reason unreadable from the Gherkin, so the
|
|
29
|
+
* clause is refused (SG-W028) and the author is pointed at a container type that resolves.
|
|
30
|
+
*/
|
|
31
|
+
export const PARENT_CONTAINER_TYPES: readonly string[] = [
|
|
32
|
+
'list-item', 'listitem', 'list',
|
|
33
|
+
'table', 'section', 'dialog', 'modal', 'drawer', 'overlay', 'form',
|
|
34
|
+
'card', 'item', 'row', 'group', 'panel', 'region', 'container',
|
|
35
|
+
'tab', 'menu',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const TYPES_ALT = PARENT_CONTAINER_TYPES.join('|');
|
|
39
|
+
|
|
40
|
+
/** `in [Parent] <containerType>` — the clause proper, without any value tail. */
|
|
41
|
+
const SCOPE_CLAUSE_BASE = new RegExp(`\\s+in\\s+\\[([^\\]]+)\\]\\s+(${TYPES_ALT})\\b`, 'i');
|
|
42
|
+
|
|
43
|
+
/** The same, plus the optional `with {{v}}` / `with "text"` tail. */
|
|
44
|
+
const SCOPE_CLAUSE = new RegExp(
|
|
45
|
+
`${SCOPE_CLAUSE_BASE.source}(?:\\s+with\\s+(\\{\\{[a-z0-9\\-._]+\\}\\}|"[^"]*"))?`,
|
|
46
|
+
'i',
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/** The same shape with ANY word where a container type belongs — used only to report what was lost. */
|
|
50
|
+
const SCOPE_CLAUSE_ANY_TYPE = /\s+in\s+\[([^\]]+)\]\s+([\w-]+)/i;
|
|
51
|
+
|
|
52
|
+
export interface ParentScopeClause {
|
|
53
|
+
/** The `[Parent]` reference — a selectors.yaml key, or `{{var}}` naming the container's text. */
|
|
54
|
+
ref: string;
|
|
55
|
+
/** The container type, lowercased. */
|
|
56
|
+
type: string;
|
|
57
|
+
/** `{{var}}` name when the value tail scopes the parent BY CONTENT. */
|
|
58
|
+
valueRef?: string;
|
|
59
|
+
/** Quoted literal when the value tail scopes the parent by content without a variable. */
|
|
60
|
+
valueText?: string;
|
|
61
|
+
/** The sentence with the clause removed, so the target extractors never see the parent's tokens. */
|
|
62
|
+
remainingText: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Is the `[…]` at `clauseAt` a CONTAINER, or is it the step's own target?
|
|
67
|
+
*
|
|
68
|
+
* A scope clause narrows a target, so the target's bracket must already have appeared. Without this
|
|
69
|
+
* test, `User select {{country}} in [Country Picker] dropdown` reads its one and only target as a
|
|
70
|
+
* parent — the bracket is what the step acts ON, and the `in` is English, not grammar.
|
|
71
|
+
*/
|
|
72
|
+
function scopesATarget(text: string, clauseAt: number): boolean {
|
|
73
|
+
return /\[[^\]]*\]/.test(text.slice(0, clauseAt));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Does the segment before the scope clause already carry its own `with`?
|
|
78
|
+
*
|
|
79
|
+
* This is what keeps the value tail backward compatible. The documented grammar
|
|
80
|
+
* (`see [first] row in [apply list] table with {{job_name}}`) spends its single `with` on the
|
|
81
|
+
* TARGET, and re-reading that as a parent filter would silently invert the meaning of every such
|
|
82
|
+
* step already written. A SECOND `with` — `see [Tag] header with {{label}} in [Job Card] card with
|
|
83
|
+
* {{title}}` — cannot belong to the target, which already has one; only then does the tail bind to
|
|
84
|
+
* the parent. `[Ref]` contents are blanked first so a bracket like `[Filter With Tags]` is not
|
|
85
|
+
* mistaken for the keyword.
|
|
86
|
+
*/
|
|
87
|
+
function targetAlreadyHasValue(head: string): boolean {
|
|
88
|
+
return /\s+with\s+/i.test(head.replace(/\[[^\]]*\]/g, ''));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extract the scope clause from a step sentence.
|
|
93
|
+
*
|
|
94
|
+
* Returns `{ clause }` when a container type was recognized, `{ unsupportedType }` when the
|
|
95
|
+
* sentence carries an `in [X] <word>` clause whose `<word>` is not a container (it is dropped, and
|
|
96
|
+
* the caller reports it), or an empty object when there is no scope clause at all.
|
|
97
|
+
*/
|
|
98
|
+
export function extractParentScopeClause(text: string): {
|
|
99
|
+
clause?: ParentScopeClause;
|
|
100
|
+
unsupportedType?: string;
|
|
101
|
+
} {
|
|
102
|
+
const match = text.match(SCOPE_CLAUSE);
|
|
103
|
+
if (!match) {
|
|
104
|
+
const loose = text.match(SCOPE_CLAUSE_ANY_TYPE);
|
|
105
|
+
return loose && scopesATarget(text, loose.index!)
|
|
106
|
+
? { unsupportedType: loose[2].toLowerCase() }
|
|
107
|
+
: {};
|
|
108
|
+
}
|
|
109
|
+
if (!scopesATarget(text, match.index!)) return {};
|
|
110
|
+
|
|
111
|
+
const head = text.slice(0, match.index);
|
|
112
|
+
const tail = match[3];
|
|
113
|
+
const bindsToParent = Boolean(tail) && targetAlreadyHasValue(head);
|
|
114
|
+
// A value tail the target has already claimed stays in the sentence, exactly where it was, for the
|
|
115
|
+
// target extractors to pick up — what gets consumed is then only `in [Parent] <type>`.
|
|
116
|
+
const consumedLength = bindsToParent
|
|
117
|
+
? match[0].length
|
|
118
|
+
: match[0].match(SCOPE_CLAUSE_BASE)![0].length;
|
|
119
|
+
|
|
120
|
+
const clause: ParentScopeClause = {
|
|
121
|
+
ref: match[1],
|
|
122
|
+
type: match[2].toLowerCase(),
|
|
123
|
+
remainingText: head + text.slice(match.index! + consumedLength),
|
|
124
|
+
};
|
|
125
|
+
if (bindsToParent) {
|
|
126
|
+
if (tail!.startsWith('{{')) clause.valueRef = tail!.slice(2, -2);
|
|
127
|
+
else clause.valueText = tail!.slice(1, -1);
|
|
128
|
+
}
|
|
129
|
+
return { clause };
|
|
130
|
+
}
|
|
@@ -8,6 +8,27 @@ export interface AuthGroup {
|
|
|
8
8
|
scenarios: string[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* One statement of a hook block (`@beforeAll` / `@afterEach` / `@afterAll`). `wrap` → the template
|
|
13
|
+
* guards the statement with try/catch and records a failure under `label` (a JSON string literal,
|
|
14
|
+
* safe to paste into code); `stopOnFailure` → a failure also ends the hook right there (action
|
|
15
|
+
* steps: later steps would run on state the failed action never produced). A plain step runs
|
|
16
|
+
* unwrapped and throws as before.
|
|
17
|
+
*/
|
|
18
|
+
export interface HookStep {
|
|
19
|
+
comment?: string;
|
|
20
|
+
code: string;
|
|
21
|
+
wrap?: boolean;
|
|
22
|
+
stopOnFailure?: boolean;
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Hook block render input. `collect` → declare the failure list and end with the fail-once guard. */
|
|
27
|
+
export interface HookRenderData {
|
|
28
|
+
steps: HookStep[];
|
|
29
|
+
collect?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
export interface TestFileData {
|
|
12
33
|
imports: string;
|
|
13
34
|
featureName: string;
|
|
@@ -79,6 +100,7 @@ export interface RenderImportsOptions {
|
|
|
79
100
|
needsMock?: boolean;
|
|
80
101
|
urlAssertImports?: string;
|
|
81
102
|
regexEscapeImport?: boolean;
|
|
103
|
+
checkedStateImports?: string;
|
|
82
104
|
}
|
|
83
105
|
|
|
84
106
|
export interface TestGeneratorAdapter {
|
|
@@ -91,9 +113,9 @@ export interface TestGeneratorAdapter {
|
|
|
91
113
|
renderScenario(data: ScenarioData): string;
|
|
92
114
|
renderImports(options?: RenderImportsOptions): string;
|
|
93
115
|
renderBeforeEach(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
94
|
-
renderBeforeAll(data:
|
|
95
|
-
renderAfterEach(data:
|
|
96
|
-
renderAfterAll(data:
|
|
116
|
+
renderBeforeAll(data: HookRenderData): string;
|
|
117
|
+
renderAfterEach(data: HookRenderData): string;
|
|
118
|
+
renderAfterAll(data: HookRenderData): string;
|
|
97
119
|
|
|
98
120
|
// Step rendering
|
|
99
121
|
renderStep(templateName: string, data: any): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { TestGeneratorAdapter, TestFileData, ScenarioData, RenderImportsOptions } from '../adapter-interface';
|
|
2
|
+
import { TestGeneratorAdapter, TestFileData, ScenarioData, RenderImportsOptions, HookRenderData } from '../adapter-interface';
|
|
3
3
|
import { TemplateEngine } from '../../template-engine';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -39,15 +39,15 @@ export class AppiumAdapter implements TestGeneratorAdapter {
|
|
|
39
39
|
return this.templateEngine.renderBeforeEach(data);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
renderBeforeAll(data:
|
|
42
|
+
renderBeforeAll(data: HookRenderData): string {
|
|
43
43
|
return this.templateEngine.renderBeforeAll(data);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
renderAfterEach(data:
|
|
46
|
+
renderAfterEach(data: HookRenderData): string {
|
|
47
47
|
return this.templateEngine.renderAfterEach(data);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
renderAfterAll(data:
|
|
50
|
+
renderAfterAll(data: HookRenderData): string {
|
|
51
51
|
return this.templateEngine.renderAfterAll(data);
|
|
52
52
|
}
|
|
53
53
|
|