@sun-asterisk/sungen 3.2.32 → 3.2.34
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/cli/commands/audit.d.ts +5 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +26 -4
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +35 -17
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/changed.d.ts.map +1 -1
- package/dist/cli/commands/changed.js +2 -3
- package/dist/cli/commands/changed.js.map +1 -1
- package/dist/cli/commands/dashboard.d.ts.map +1 -1
- package/dist/cli/commands/dashboard.js +2 -28
- package/dist/cli/commands/dashboard.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +79 -53
- 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 +14 -7
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts +9 -0
- package/dist/cli/commands/results-merge.d.ts.map +1 -1
- package/dist/cli/commands/results-merge.js +9 -12
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/results-sanitize.d.ts.map +1 -1
- package/dist/cli/commands/results-sanitize.js +2 -13
- package/dist/cli/commands/results-sanitize.js.map +1 -1
- package/dist/cli/commands/select.d.ts +12 -0
- package/dist/cli/commands/select.d.ts.map +1 -0
- package/dist/cli/commands/select.js +122 -0
- package/dist/cli/commands/select.js.map +1 -0
- 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/cli/index.js +7 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
- package/dist/dashboard/snapshot-builder.js +6 -10
- package/dist/dashboard/snapshot-builder.js.map +1 -1
- package/dist/dashboard/templates/index.html +1 -1
- package/dist/dashboard/types.d.ts +1 -0
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +11 -10
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +21 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +32 -1
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/json-exporter.d.ts.map +1 -1
- package/dist/exporters/json-exporter.js +7 -0
- package/dist/exporters/json-exporter.js.map +1 -1
- package/dist/exporters/locale-variants.d.ts +7 -1
- package/dist/exporters/locale-variants.d.ts.map +1 -1
- package/dist/exporters/locale-variants.js +23 -16
- package/dist/exporters/locale-variants.js.map +1 -1
- package/dist/exporters/manual-hybrid-oracle.d.ts +54 -0
- package/dist/exporters/manual-hybrid-oracle.d.ts.map +1 -0
- package/dist/exporters/manual-hybrid-oracle.js +114 -0
- package/dist/exporters/manual-hybrid-oracle.js.map +1 -0
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +19 -9
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +70 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -0
- package/dist/exporters/matrix/carry-manual-results.js +232 -0
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -0
- package/dist/exporters/matrix/export.d.ts +5 -0
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +8 -1
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.js +6 -0
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -1
- package/dist/exporters/matrix/render-testcase-sheet.js +10 -4
- package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +8 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/wording.d.ts +8 -1
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +37 -2
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/run-environment.d.ts +32 -0
- package/dist/exporters/run-environment.d.ts.map +1 -0
- package/dist/exporters/run-environment.js +125 -0
- package/dist/exporters/run-environment.js.map +1 -0
- package/dist/exporters/scenario-merger.d.ts +18 -0
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +47 -3
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/test-data-resolver.d.ts +0 -4
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +0 -7
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +2 -0
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +7 -2
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +11 -4
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +49 -19
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +4 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/dist/generators/test-generator/code-generator.d.ts +2 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +90 -5
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +36 -5
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +35 -4
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +12 -2
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +15 -9
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +15 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +5 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.js +6 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +46 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +84 -9
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +25 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +39 -8
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.js +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +37 -4
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +212 -79
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts +3 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +29 -12
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability.d.ts +7 -1
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js +15 -12
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +5 -5
- package/dist/harness/config-file.d.ts +17 -0
- package/dist/harness/config-file.d.ts.map +1 -0
- package/dist/harness/config-file.js +100 -0
- package/dist/harness/config-file.js.map +1 -0
- package/dist/harness/data-driven-lint.d.ts.map +1 -1
- package/dist/harness/data-driven-lint.js +69 -27
- package/dist/harness/data-driven-lint.js.map +1 -1
- package/dist/harness/depth-lint.d.ts.map +1 -1
- package/dist/harness/depth-lint.js +2 -1
- package/dist/harness/depth-lint.js.map +1 -1
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +6 -2
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/keyword-match.d.ts +59 -0
- package/dist/harness/keyword-match.d.ts.map +1 -0
- package/dist/harness/keyword-match.js +86 -0
- package/dist/harness/keyword-match.js.map +1 -0
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +1 -0
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +24 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +117 -13
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/perf.d.ts +7 -1
- package/dist/harness/perf.d.ts.map +1 -1
- package/dist/harness/perf.js +13 -10
- package/dist/harness/perf.js.map +1 -1
- package/dist/harness/quality-gates.d.ts +9 -2
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +22 -8
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/query-catalog.js +0 -0
- package/dist/harness/query-catalog.js.map +1 -1
- package/dist/harness/read-text.d.ts +21 -0
- package/dist/harness/read-text.d.ts.map +1 -1
- package/dist/harness/read-text.js +32 -1
- package/dist/harness/read-text.js.map +1 -1
- package/dist/harness/secret-scan.d.ts +27 -1
- package/dist/harness/secret-scan.d.ts.map +1 -1
- package/dist/harness/secret-scan.js +57 -4
- package/dist/harness/secret-scan.js.map +1 -1
- package/dist/harness/select-by-ids.d.ts +31 -0
- package/dist/harness/select-by-ids.d.ts.map +1 -0
- package/dist/harness/select-by-ids.js +106 -0
- package/dist/harness/select-by-ids.js.map +1 -0
- package/dist/harness/sensors.d.ts +20 -1
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +102 -27
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/spec-branches.d.ts +0 -18
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +24 -3
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +14 -4
- package/dist/harness/spec-coverage.js.map +1 -1
- package/dist/harness/ui-inventory.d.ts +40 -0
- package/dist/harness/ui-inventory.d.ts.map +1 -0
- package/dist/harness/ui-inventory.js +191 -0
- package/dist/harness/ui-inventory.js.map +1 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +66 -10
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/harness/write-atomic.d.ts +2 -0
- package/dist/harness/write-atomic.d.ts.map +1 -0
- package/dist/harness/write-atomic.js +62 -0
- package/dist/harness/write-atomic.js.map +1 -0
- package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/writer.js +15 -7
- package/dist/orchestrator/ai-skills/writer.js.map +1 -1
- package/dist/orchestrator/assets-drift.d.ts.map +1 -1
- package/dist/orchestrator/assets-drift.js +11 -6
- package/dist/orchestrator/assets-drift.js.map +1 -1
- package/dist/orchestrator/project-deps-sync.d.ts.map +1 -1
- package/dist/orchestrator/project-deps-sync.js +22 -1
- package/dist/orchestrator/project-deps-sync.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +8 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +49 -7
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +3 -11
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/security-overrides-catalog.d.ts +39 -0
- package/dist/orchestrator/security-overrides-catalog.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides-catalog.js +63 -0
- package/dist/orchestrator/security-overrides-catalog.js.map +1 -0
- package/dist/orchestrator/security-overrides.d.ts +51 -0
- package/dist/orchestrator/security-overrides.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides.js +165 -0
- package/dist/orchestrator/security-overrides.js.map +1 -0
- package/dist/orchestrator/templates/ai-src/agents/discovery.md +11 -3
- package/dist/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +70 -13
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +23 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +41 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/dist/orchestrator/templates/playwright.config.ts +4 -2
- package/dist/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/specs-url-assert.d.ts +17 -1
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +38 -4
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +47 -4
- package/dist/orchestrator/templates/wdio.conf.ts +24 -1
- package/dist/orchestrator/trace-mode-install.d.ts +14 -0
- package/dist/orchestrator/trace-mode-install.d.ts.map +1 -0
- package/dist/orchestrator/trace-mode-install.js +132 -0
- package/dist/orchestrator/trace-mode-install.js.map +1 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.d.ts +20 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js +45 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -0
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +24 -5
- package/src/cli/commands/capability.ts +51 -18
- package/src/cli/commands/changed.ts +2 -4
- package/src/cli/commands/dashboard.ts +1 -26
- package/src/cli/commands/delivery.ts +75 -48
- package/src/cli/commands/generate.ts +15 -7
- package/src/cli/commands/results-merge.ts +9 -9
- package/src/cli/commands/results-sanitize.ts +2 -9
- package/src/cli/commands/select.ts +88 -0
- package/src/cli/commands/update.ts +3 -0
- package/src/cli/index.ts +7 -2
- package/src/dashboard/snapshot-builder.ts +6 -8
- package/src/dashboard/templates/index.html +1 -1
- package/src/dashboard/types.ts +1 -0
- package/src/exporters/csv-exporter.ts +7 -10
- package/src/exporters/feature-parser.ts +32 -2
- package/src/exporters/json-exporter.ts +6 -1
- package/src/exporters/locale-variants.ts +23 -17
- package/src/exporters/manual-hybrid-oracle.ts +112 -0
- package/src/exporters/matrix/build.ts +20 -10
- package/src/exporters/matrix/carry-manual-results.ts +229 -0
- package/src/exporters/matrix/export.ts +14 -4
- package/src/exporters/matrix/gates.ts +6 -0
- package/src/exporters/matrix/render-testcase-sheet.ts +10 -4
- package/src/exporters/matrix/types.ts +8 -1
- package/src/exporters/matrix/wording.ts +36 -2
- package/src/exporters/run-environment.ts +89 -0
- package/src/exporters/scenario-merger.ts +49 -4
- package/src/exporters/test-data-resolver.ts +0 -6
- package/src/exporters/xlsx-exporter.ts +7 -2
- package/src/generators/gherkin-parser/index.ts +50 -18
- package/src/generators/test-generator/adapters/adapter-interface.ts +6 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/src/generators/test-generator/code-generator.ts +77 -5
- package/src/generators/test-generator/diagnostics.ts +36 -5
- package/src/generators/test-generator/patterns/index.ts +15 -8
- package/src/generators/test-generator/patterns/types.ts +15 -0
- package/src/generators/test-generator/patterns/with-parent-scope.ts +6 -0
- package/src/generators/test-generator/step-mapper.ts +90 -8
- package/src/generators/test-generator/utils/data-resolver.ts +41 -12
- package/src/generators/test-generator/utils/selector-entry-fields.ts +1 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +238 -82
- package/src/harness/audit.ts +37 -16
- package/src/harness/capability.ts +23 -12
- package/src/harness/catalog/universal-viewpoints.yaml +5 -5
- package/src/harness/config-file.ts +66 -0
- package/src/harness/data-driven-lint.ts +82 -24
- package/src/harness/depth-lint.ts +2 -1
- package/src/harness/flow-check.ts +6 -2
- package/src/harness/keyword-match.ts +85 -0
- package/src/harness/next-step.ts +1 -0
- package/src/harness/parse.ts +123 -16
- package/src/harness/perf.ts +15 -7
- package/src/harness/quality-gates.ts +22 -8
- package/src/harness/query-catalog.ts +0 -0
- package/src/harness/read-text.ts +32 -1
- package/src/harness/secret-scan.ts +60 -4
- package/src/harness/select-by-ids.ts +83 -0
- package/src/harness/sensors.ts +109 -28
- package/src/harness/spec-branches.ts +6 -3
- package/src/harness/spec-coverage.ts +14 -4
- package/src/harness/ui-inventory.ts +164 -0
- package/src/harness/viewpoint-ledger.ts +59 -12
- package/src/harness/write-atomic.ts +22 -0
- package/src/orchestrator/ai-skills/writer.ts +18 -3
- package/src/orchestrator/assets-drift.ts +10 -5
- package/src/orchestrator/project-deps-sync.ts +23 -1
- package/src/orchestrator/project-initializer.ts +54 -6
- package/src/orchestrator/screen-manager.ts +3 -11
- package/src/orchestrator/security-overrides-catalog.ts +69 -0
- package/src/orchestrator/security-overrides.ts +149 -0
- package/src/orchestrator/templates/ai-src/agents/discovery.md +11 -3
- package/src/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +70 -13
- package/src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +23 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +41 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/src/orchestrator/templates/playwright.config.ts +4 -2
- package/src/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/specs-url-assert.ts +47 -4
- package/src/orchestrator/templates/wdio.conf.ts +24 -1
- package/src/orchestrator/trace-mode-install.ts +94 -0
- package/src/orchestrator/viewpoint-pattern-catalog.ts +58 -0
|
@@ -629,7 +629,9 @@ on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`
|
|
|
629
629
|
| Tag | Effect |
|
|
630
630
|
|---|---|
|
|
631
631
|
| `@spec:<id>` | **Traceability**: this scenario proves requirement `<id>` from `requirements/spec.md` (`@spec:FR-003`). Repeatable — a scenario may prove several. Read by `sungen audit` (requirement coverage) and by delivery (the Coverage sheet's requirement table). Without it the link is only inferred from wording and cannot survive an edit. |
|
|
632
|
+
| `@ui:<UI-NNN>` | **Design traceability**: this scenario proves design item `UI-NNN` from `requirements/ui-inventory.md` (`@ui:UI-014`). Repeatable. Read by `sungen audit` (`UI-ITEM-UNCOVERED`). An item deliberately not tested is recorded as a comment instead: `# UI-DEFERRED: UI-002..013 — <reason>`. Rules: `sungen-tc-generation` § Design Inventory. |
|
|
632
633
|
| `@manual` | Skip in generation |
|
|
634
|
+
| `@known-defect` | The scenario is EXPECTED to be red — it asserts a live defect. It still runs and still reports as failed (never skipped, never "fixed"), and it stays a pass-through tag so `--grep @known-defect` selects it. **`[mobile]`** Its failure does NOT escalate the next scenario's `@reset:` tier: a red that was designed in is not evidence of leftover state |
|
|
633
635
|
| `@auth:role` | Use auth storage state for role |
|
|
634
636
|
| `@no-auth` | Disable inherited auth |
|
|
635
637
|
| `@steps:name` | Define reusable step block (base scenario) |
|
|
@@ -639,7 +641,8 @@ on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`
|
|
|
639
641
|
| `@cleanup:scroll` | Auto-cleanup: scroll to top after each test (cleanupPage) |
|
|
640
642
|
| `@cleanup:storage` | Auto-cleanup: clear sessionStorage after each test (cleanupPage) |
|
|
641
643
|
| `@screenshot:on-failure` | Auto-capture screenshot when test fails (base.ts fixture) |
|
|
642
|
-
| `@
|
|
644
|
+
| `@serial` | Opt-in: one shared page for an ordered, genuinely stateful sequence → `test.describe.serial()`. A failure skips the rest |
|
|
645
|
+
| `@parallel` | Names the default (fresh page per test) — changes nothing. Compiles on mobile too, but a single device/emulator gets no session-isolation benefit from it |
|
|
643
646
|
| `@beforeAll` | Hook: runs once before all tests → `test.beforeAll()` |
|
|
644
647
|
| `@afterEach` | Hook: runs after each test → `test.afterEach()` (custom cleanup) |
|
|
645
648
|
| `@afterAll` | Hook: runs once after all tests → `test.afterAll()` |
|
|
@@ -695,6 +698,14 @@ Rules that matter when generating:
|
|
|
695
698
|
relaunch does not undo a half-written local DB. One rung, one scenario, and never from `none`.
|
|
696
699
|
A **retry** of the failed scenario is NOT escalated — it re-runs at its declared tier, so a
|
|
697
700
|
state-dependent failure fails both attempts and is reported honestly instead of passing on attempt 2.
|
|
701
|
+
A scenario tagged **`@known-defect`** escalates nothing either: its red was designed in, so making
|
|
702
|
+
the next scenario pay a harder reset for it buys nothing, on every run, forever.
|
|
703
|
+
- **Escalation never picks a tier this session cannot perform.** Reinstalling needs the app's build
|
|
704
|
+
file, so when the app is attached by package the ladder caps at `data` (Android) / `soft` (iOS) and
|
|
705
|
+
says it capped, instead of throwing. Nothing asked for the escalated tier, and the throw would land
|
|
706
|
+
in `beforeEach` — a hook failure, which skips every remaining scenario in the FILE. The cap never
|
|
707
|
+
goes BELOW the declared tier: a tier you asked for that the session cannot do still throws by name,
|
|
708
|
+
because that one is an unmet contract rather than a recovery the runner chose.
|
|
698
709
|
- **`SUNGEN_RESET_TIER=data` raises the floor for a whole run** without regenerating specs — CI can run
|
|
699
710
|
stricter than local dev. It never lowers a declared tier.
|
|
700
711
|
- **`data`/`full` on iOS reinstall the app**, because `mobile: clearApp` is Android-only (UiAutomator2).
|
|
@@ -765,14 +776,14 @@ npx playwright test --grep "@smoke|@high" # smoke OR high
|
|
|
765
776
|
|
|
766
777
|
### Serial vs Parallel (test execution mode)
|
|
767
778
|
|
|
768
|
-
**Default:
|
|
779
|
+
**Default: isolated** — `test.describe()` with a fresh page per test. Background runs as `beforeEach`; a failing scenario does not skip the others. Write every scenario so it stands alone — no scenario may rely on state another one left behind. `@parallel` only names this default.
|
|
769
780
|
|
|
770
|
-
**`@
|
|
781
|
+
**`@serial` opt-in** — `test.describe.serial()` with one shared page. Background runs once in `beforeAll`; a failure skips the rest. Use it only for a genuinely stateful, ordered sequence.
|
|
771
782
|
|
|
772
783
|
| Mode | Generated | Page | Background | On fail |
|
|
773
784
|
|---|---|---|---|---|
|
|
774
|
-
|
|
|
775
|
-
| `@
|
|
785
|
+
| Isolated (default, `@parallel`) | `test.describe()` | Fresh per test | `beforeEach` (N goto) | Continue |
|
|
786
|
+
| `@serial` | `test.describe.serial()` | Shared | `beforeAll` (1 goto) | Skip remaining |
|
|
776
787
|
|
|
777
788
|
**Multiple auth groups need the default isolation.** A feature mixing `@auth:user` with `@no-auth` compiles as-is — features are parallel by default, and `@parallel` only names that default. What breaks it is `@serial`: one shared browser context cannot carry two different sessions, so the compiler errors and asks you to drop `@serial` or split the feature.
|
|
778
789
|
|
|
@@ -933,7 +944,13 @@ Feature: User Management
|
|
|
933
944
|
|
|
934
945
|
### Layer 2: `@afterEach` scenario (custom cleanup)
|
|
935
946
|
|
|
936
|
-
Only when `@cleanup:*` tags aren't enough — feature-specific logic.
|
|
947
|
+
Only when `@cleanup:*` tags aren't enough — feature-specific logic. `@afterEach` gets the per-test
|
|
948
|
+
`page`, so UI cleanup steps work in it.
|
|
949
|
+
|
|
950
|
+
It runs after every test it applies to — and it does NOT apply to the `@no-auth` group when the
|
|
951
|
+
feature mixes `@no-auth` with other scenarios: an anonymous test has an empty session, so a cleanup
|
|
952
|
+
that needs the signed-in page would fail it although its own steps passed. The hook is emitted inside
|
|
953
|
+
each other group instead. A feature whose scenarios are ALL `@no-auth` keeps the hook for them.
|
|
937
954
|
|
|
938
955
|
### Layer 3: `@beforeAll` / `@afterAll` (optional)
|
|
939
956
|
|
|
@@ -958,8 +975,9 @@ Scenario: drop the seeded data after the last scenario
|
|
|
958
975
|
recorded and the next step still runs; a failing action step (`Given`/`When`) stops the hook there.
|
|
959
976
|
The hook fails ONCE with the full list (`Hook preconditions not met`), not with the first failure.
|
|
960
977
|
- `@cases` and `@extend` are not supported on a hook — they are ignored with a compile warning.
|
|
961
|
-
-
|
|
962
|
-
`@query:` preconditions and `expect` assertions there.
|
|
978
|
+
- `@beforeAll` / `@afterAll` have no `page` fixture on an isolated (non-`@serial`) feature — keep
|
|
979
|
+
their steps to `@api:`/`@query:` preconditions and `expect` assertions there. `@afterEach` does get
|
|
980
|
+
the per-test `page`.
|
|
963
981
|
|
|
964
982
|
**Rendering order in `.spec.ts`:**
|
|
965
983
|
`test.describe` → `test.use(storageState)` → `test.use(autoCleanup)` → `test.beforeAll` → `test.beforeEach` → `test.afterEach` → `test.afterAll` → `test()` blocks.
|
|
@@ -225,12 +225,33 @@ Skip if `specs/.auth/<role>.json` already exists and a probe navigation reaches
|
|
|
225
225
|
|
|
226
226
|
---
|
|
227
227
|
|
|
228
|
+
## Where each wave writes its result
|
|
229
|
+
|
|
230
|
+
`sungen delivery` reads the per-target result file (`specs/generated/<name>/<name>-test-result.json`),
|
|
231
|
+
which the JSON reporter in `playwright.config.ts` writes. Keep it that way in every wave:
|
|
232
|
+
|
|
233
|
+
- **Never pass `--reporter`** — it replaces the config's reporters, the JSON reporter is switched off, and the run writes no result file.
|
|
234
|
+
- **Subset waves (Phase 1–2) write to `test-results/sungen-wave.json`** — never to the per-target file (a subset would overwrite the unit's full result) and never to the default `test-results/results.json` (delivery falls back to that file when a per-target one is missing, so a 5-scenario smoke would read as the unit's result). `test-results/` is Playwright's output dir, wiped on every run — nothing to clean up.
|
|
235
|
+
- **Full waves (Phase 3–4) write the per-target file** — the Phase 4 file is the result delivery shows.
|
|
236
|
+
- **Scope every run to the unit** — `specs/generated/<name>/` (flow: `specs/generated/flows/<name>/`). Without a path, `--grep` runs every unit in `specs/generated/`.
|
|
237
|
+
|
|
238
|
+
### Fix retries re-run only what the wave left red (Phase 2–3)
|
|
239
|
+
|
|
240
|
+
- **The first run of a phase is its full wave.** Each **fix retry** is the same command plus `--last-failed`, written to `test-results/sungen-wave.json` — never to the per-target file.
|
|
241
|
+
- **Retry immediately after the run whose failures you fixed.** `--last-failed` reads the failures of the *previous run only*: run any other test in between (a `-g` debug run) and the list now describes that run. If you did, re-run the full wave instead.
|
|
242
|
+
- **Nothing left red → no retry.** On an empty list Playwright prints "No tests found", exits 1 and still writes a zero-test report — that is not a failure of the unit, and it is why a retry never writes the per-target file.
|
|
243
|
+
- **A locale run (`SUNGEN_ENV=<env>`) writes `test-results/sungen-wave.<env>.json`** — the config inserts the env before `.json`; it is still a scratch file, nothing reads it.
|
|
244
|
+
- **A retry does not show whether your fix broke a test that was green.** Phase 3's full first run catches a Phase 2 fix that did; Phase 4 catches a Phase 3 one.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
228
248
|
## Phase 1: Smoke Check (catch fundamentals)
|
|
229
249
|
|
|
230
250
|
Run **up to 5 scenarios** — pick the first `@high` scenarios in the feature file.
|
|
231
251
|
|
|
232
252
|
```bash
|
|
233
|
-
|
|
253
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=test-results/sungen-wave.json \
|
|
254
|
+
npx playwright test specs/generated/<name>/ --grep "VP-.*-001|VP-.*-002|VP-.*-003|VP-.*-004|VP-.*-005"
|
|
234
255
|
```
|
|
235
256
|
|
|
236
257
|
**Purpose:** Detect broken fundamentals before running 50+ tests:
|
|
@@ -248,7 +269,15 @@ npx playwright test --grep "VP-.*-001|VP-.*-002|VP-.*-003|VP-.*-004|VP-.*-005" -
|
|
|
248
269
|
Run all `@high` scenarios:
|
|
249
270
|
|
|
250
271
|
```bash
|
|
251
|
-
|
|
272
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=test-results/sungen-wave.json \
|
|
273
|
+
npx playwright test specs/generated/<name>/ --grep "@high"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Fix retry — only the scenarios this wave left red:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=test-results/sungen-wave.json \
|
|
280
|
+
npx playwright test specs/generated/<name>/ --grep "@high" --last-failed
|
|
252
281
|
```
|
|
253
282
|
|
|
254
283
|
If your Playwright config doesn't support tag grep, use scenario name grep from the feature file — collect VP IDs of `@high` scenarios.
|
|
@@ -261,12 +290,24 @@ Max 2 fix attempts in this phase.
|
|
|
261
290
|
|
|
262
291
|
## Phase 3: Full Run (@normal + @low)
|
|
263
292
|
|
|
264
|
-
Run remaining scenarios:
|
|
293
|
+
Run remaining scenarios — the whole unit, into its per-target result file:
|
|
265
294
|
|
|
266
295
|
```bash
|
|
267
|
-
|
|
296
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=specs/generated/<name>/<name>-test-result.json \
|
|
297
|
+
npx playwright test specs/generated/<name>/<name>.spec.ts
|
|
268
298
|
```
|
|
269
299
|
|
|
300
|
+
A multi-feature screen (one result file per feature) or a locale run (`SUNGEN_ENV`): use the loop in `run-test` § "Playwright command guidelines".
|
|
301
|
+
|
|
302
|
+
Fix retry — the failures only, into the wave file (the per-target file keeps this phase's full run until Phase 4 rewrites it):
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=test-results/sungen-wave.json \
|
|
306
|
+
npx playwright test specs/generated/<name>/<name>.spec.ts --last-failed
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Multi-feature screen — no `--last-failed` here.** Phase 3 ran one Playwright run per feature spec, so the failure list holds only the LAST spec's failures; a retry would skip every earlier feature's red tests without a word. Re-run the per-spec loop instead.
|
|
310
|
+
|
|
270
311
|
Many selectors already fixed from Phase 2 (shared elements). Only diagnose **new** failures — selectors that only appear in lower-priority scenarios.
|
|
271
312
|
|
|
272
313
|
Max 1 fix attempt. If `@low` scenarios still fail after fix → **report and move on**, don't loop.
|
|
@@ -275,12 +316,15 @@ Max 1 fix attempt. If `@low` scenarios still fail after fix → **report and mov
|
|
|
275
316
|
|
|
276
317
|
## Phase 4: Regression
|
|
277
318
|
|
|
278
|
-
One final full run to confirm all phases together:
|
|
319
|
+
One final full run to confirm all phases together — this per-target file is the result `sungen delivery` reads:
|
|
279
320
|
|
|
280
321
|
```bash
|
|
281
|
-
|
|
322
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME=specs/generated/<name>/<name>-test-result.json \
|
|
323
|
+
npx playwright test specs/generated/<name>/<name>.spec.ts
|
|
282
324
|
```
|
|
283
325
|
|
|
326
|
+
Same multi-feature / locale form as Phase 3.
|
|
327
|
+
|
|
284
328
|
Report results. Do NOT enter another fix loop here.
|
|
285
329
|
|
|
286
330
|
---
|
|
@@ -458,13 +502,13 @@ user detail:
|
|
|
458
502
|
|
|
459
503
|
## Attempt Budget Summary
|
|
460
504
|
|
|
461
|
-
| Phase | What runs | Max fix attempts | On failure after max |
|
|
462
|
-
|
|
463
|
-
| 0. Pre-gen | Playwright MCP snapshot → write selectors.yaml | 1 snapshot | Ask user — skip or retry navigation |
|
|
464
|
-
| 0.5. Auth | Manual login in MCP browser → `browser_storage_state` → `specs/.auth/<role>.json` | 1 login | Ask user — retry login or fall back to `sungen makeauth` |
|
|
465
|
-
| 1. Smoke | First 5 @high | 2 | Ask user — fundamentals broken |
|
|
466
|
-
| 2. Priority | All @high | 2 | Report failures, continue to Phase 3 |
|
|
467
|
-
| 3. Full | All tests | 1 | Report @low/@normal failures, continue |
|
|
468
|
-
| 4. Regression | All tests | 0 | Report final results |
|
|
505
|
+
| Phase | What runs | Fix retry runs | Max fix attempts | On failure after max |
|
|
506
|
+
|---|---|---|---|---|
|
|
507
|
+
| 0. Pre-gen | Playwright MCP snapshot → write selectors.yaml | — | 1 snapshot | Ask user — skip or retry navigation |
|
|
508
|
+
| 0.5. Auth | Manual login in MCP browser → `browser_storage_state` → `specs/.auth/<role>.json` | — | 1 login | Ask user — retry login or fall back to `sungen makeauth` |
|
|
509
|
+
| 1. Smoke | First 5 @high | the same 5 | 2 | Ask user — fundamentals broken |
|
|
510
|
+
| 2. Priority | All @high | `--last-failed` | 2 | Report failures, continue to Phase 3 |
|
|
511
|
+
| 3. Full | All tests | `--last-failed` (multi-feature screen: the per-spec loop again) | 1 | Report @low/@normal failures, continue |
|
|
512
|
+
| 4. Regression | All tests | — | 0 | Report final results |
|
|
469
513
|
|
|
470
514
|
**Total worst case: 5 fix attempts** (2+2+1), not unbounded loops. Phases 0 and 0.5 don't count toward fix budget.
|
|
@@ -128,7 +128,10 @@ back to the feature's own path (or `/<ref>/`) and warns `SG-W013`: a test that r
|
|
|
128
128
|
|
|
129
129
|
If the label is used by only ONE type, the base key (no suffix) is fine — suffix only on collision.
|
|
130
130
|
|
|
131
|
-
Type aliases (normalized automatically)
|
|
131
|
+
Type aliases (normalized automatically). The suffix and the step may use **different spellings of
|
|
132
|
+
the same type** — `confirm--modal` resolves for `[Confirm] dialog` and `confirm--dialog` resolves
|
|
133
|
+
for `[Confirm] modal`. Prefer the normalized suffix for consistency; either is found.
|
|
134
|
+
|
|
132
135
|
| Gherkin type | Normalized suffix |
|
|
133
136
|
|---|---|
|
|
134
137
|
| title, label, caption, message | `text` |
|
|
@@ -143,6 +146,15 @@ Type aliases (normalized automatically):
|
|
|
143
146
|
| column | `columnheader` |
|
|
144
147
|
| list-item | `listitem` |
|
|
145
148
|
|
|
149
|
+
When the step names a type from this table, only a key whose suffix is a spelling of that type is
|
|
150
|
+
used — `[Confirm] dialog` will not fall back to an unrelated `confirm--popup` entry. It reports the
|
|
151
|
+
keys it looked for, so the fix is to add one of them.
|
|
152
|
+
|
|
153
|
+
A bare `[Confirm]` keeps the older, looser fallback and matches any `confirm--*` entry, because with
|
|
154
|
+
no type there is nothing to match a suffix against. So does a `[Ref]` followed by a word that is not
|
|
155
|
+
an element type at all — `Then User see [Quick Panel] is hidden` reads `is` as that word. This is
|
|
156
|
+
what keeps state-clause steps working on a label whose keys are all suffixed.
|
|
157
|
+
|
|
146
158
|
## Nth-Suffixed Keys
|
|
147
159
|
|
|
148
160
|
When targeting a specific occurrence by index, add `--N` suffix:
|
|
@@ -154,7 +154,9 @@ For append: read highest `VP-<CAT>-<NNN>`, continue from next number. Never modi
|
|
|
154
154
|
Auto-detected by `create-test` before invoking this skill:
|
|
155
155
|
- `spec.md` — primary, always read if present
|
|
156
156
|
- `spec_figma.md` — Figma supplement (PAT flow already done — just read the file)
|
|
157
|
-
- `ui/*.png` — visual context
|
|
157
|
+
- `ui/*.{png,jpg,jpeg,webp,gif}` — design images. **Every one is read**, and turned into the Design Inventory below — not just "visual context".
|
|
158
|
+
- `ui/figma-meta.md` — Figma MCP capture (`sungen-capture` mode figma-mcp): `## Components` and `## Text Content` feed the Design Inventory; `## Text Content` also supplies exact labels for `test-data.yaml`.
|
|
159
|
+
- `ui-inventory.md` — the Design Inventory (written by create-test, see § Design Inventory). When it already exists, keep its ids and append new items; never renumber.
|
|
158
160
|
- `test-viewpoint.md` — test conditions checklist; scan entirely regardless of format.
|
|
159
161
|
Format varies across projects: hierarchical table (Level 1–4 columns), flat checklist,
|
|
160
162
|
prose, or mixed. Regardless of format:
|
|
@@ -181,6 +183,40 @@ This means: do not test other screens' UI layout or navigation. It does NOT mean
|
|
|
181
183
|
|
|
182
184
|
---
|
|
183
185
|
|
|
186
|
+
## Design Inventory (UI items)
|
|
187
|
+
|
|
188
|
+
**When:** the unit holds any design source — images in `requirements/ui/`, `ui/figma-meta.md`, or `spec_figma.md`. Without one, skip this section.
|
|
189
|
+
|
|
190
|
+
**Why:** a design read "for visual context" leaves no trace. An item that exists only on the design (a column `spec.md` never names) or contradicts the spec is then missed by the author AND the reviewer, and "deliberately deferred" looks exactly like "never seen". `sungen audit` checks this file (`UI-INVENTORY-MISSING`, `UI-INVENTORY-INCOMPLETE`, `UI-ITEM-UNCOVERED`).
|
|
191
|
+
|
|
192
|
+
**Reading order:** `spec.md` (authoritative) → `spec_figma.md` → `ui/figma-meta.md` (`## Components`, `## Text Content`) → **every** image in `ui/`. Many images: group them (primary screen vs states/variants) but read them all — an unread state screen is an un-inventoried one.
|
|
193
|
+
|
|
194
|
+
**Write `requirements/ui-inventory.md`** — one row per component, table column, button/link, field, state (empty/error/loading/disabled), and design annotation:
|
|
195
|
+
|
|
196
|
+
```markdown
|
|
197
|
+
# UI Inventory — <screen>
|
|
198
|
+
images read: 3/3 · figma-meta.md: read · spec_figma.md: absent
|
|
199
|
+
|
|
200
|
+
| ID | Item | Kind | Source | Seen in |
|
|
201
|
+
|---|---|---|---|---|
|
|
202
|
+
| UI-001 | Job List table | table | spec+design | ui/list.png |
|
|
203
|
+
| UI-002 | Contract plan | column | spec+design | ui/list.png |
|
|
204
|
+
| UI-014 | Salary range | column | design-only | ui/list.png |
|
|
205
|
+
| UI-015 | Status badge "Closed" | column | conflict — spec says "Expired" | ui/list.png |
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
- **Source:** `spec+design` (both name it) · `design-only` (only the design shows it) · `conflict` (they disagree — say how in the cell). `spec.md` stays authoritative; a conflict is reported, never silently resolved in either direction.
|
|
209
|
+
- **IDs** `UI-NNN` are append-only, like scenario ids: re-runs keep existing rows and continue numbering.
|
|
210
|
+
- `images read: N/N` counts the images you actually opened against the images in the folder.
|
|
211
|
+
|
|
212
|
+
**Account for every `design-only` / `conflict` item in the `.feature`** — either:
|
|
213
|
+
- **cover it**: tag the scenario that proves it `@ui:UI-014`, or
|
|
214
|
+
- **defer it with a reason** in a feature comment: `# UI-DEFERRED: UI-002..013 — per-column checks are owned by job-detail`. A deferral without a reason defers nothing.
|
|
215
|
+
|
|
216
|
+
`spec+design` items are covered through the spec (`@spec:`), and citing their ids is optional.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
184
220
|
## Generation Workflow
|
|
185
221
|
|
|
186
222
|
### Step 1 — Orient: patterns + risk
|
|
@@ -219,6 +255,10 @@ Viewpoint items: [TV-01 – <exact wording from test-viewpoint row 1>]
|
|
|
219
255
|
[TV-02 – <exact wording from test-viewpoint row 2>]
|
|
220
256
|
→ one entry per row/bullet in test-viewpoint.md, regardless of format
|
|
221
257
|
→ leave empty if no test-viewpoint.md present
|
|
258
|
+
UI items: [UI-014 – Salary range column (design-only) → covered by VP-TAB-012]
|
|
259
|
+
[UI-002..013 – per-column checks → deferred: owned by job-detail]
|
|
260
|
+
→ one entry per Design Inventory row: covered / deferred-with-reason / uncovered
|
|
261
|
+
→ leave empty if the unit has no design source
|
|
222
262
|
Spec gaps: [G1 – behavior undocumented → verify with dev]
|
|
223
263
|
```
|
|
224
264
|
|
|
@@ -152,6 +152,7 @@ Build a mapping table: for each applicable group, does the feature have a matchi
|
|
|
152
152
|
| **Account Status** | Deleted / blocked / inactive account login | VP-LOGIC | corresponding account data → error message |
|
|
153
153
|
| **Security** | SQL Injection; XSS; Data Integrity (edit dropdown/remove `disabled` via DevTools → server rejects). SQL on LIKE/search fields → 2 TCs per the `sungen-viewpoint` SQL 2-layer rule. | VP-SEC | `with {{xss_*}}`/`{{sql_*}}`; data-integrity usually `@manual` |
|
|
154
154
|
| **Cross-surface outcomes** | Admin action → outcome on user-facing surface (portal, mobile, widget); spec defines display condition on another URL | VP-LOGIC-CS | `@high @manual` per surface per business rule — at minimum 1 per cross-surface rule; use `@auth:role` + `@extend` if surface reachable in same test run |
|
|
155
|
+
| **Design items** *(when `requirements/ui/*`, `ui/figma-meta.md` or `spec_figma.md` exist)* | Every component / column / button / state the design shows is in `ui-inventory.md`; each `design-only` / `conflict` item is covered or deferred with a reason. Read the images yourself — the inventory can be incomplete | UI-NNN | `@ui:UI-NNN` on the proving scenario, or `# UI-DEFERRED: UI-NNN — <reason>`; rules in `sungen-tc-generation` § Design Inventory |
|
|
155
156
|
|
|
156
157
|
**Mobile units add four categories** (`@platform:mobile/android/ios` only). Each maps to a theme the
|
|
157
158
|
`sungen audit` mobile catalog scores, so a gap here is a scored gap, not a stylistic note — the
|
|
@@ -374,6 +375,12 @@ If `selectors/<name>.yaml` exists, count lines matching `@needs-live-verify` (pr
|
|
|
374
375
|
| Behavior Handling / Account Status | ... | ... |
|
|
375
376
|
| Security (XSS/SQLi) | ... | ... |
|
|
376
377
|
| Cross-surface outcomes | ... | ... |
|
|
378
|
+
| Design items | n/a / ✓ / ✗ | ... |
|
|
379
|
+
|
|
380
|
+
### Design ↔ Feature (when the unit holds design sources)
|
|
381
|
+
- Images read: <n>/<n> · inventory items: <n> (covered <n> · deferred <n> · uncovered <n>)
|
|
382
|
+
- Not inventoried: <item> (<image>) …
|
|
383
|
+
- Mislabelled / unproven: UI-NNN …
|
|
377
384
|
|
|
378
385
|
### ✗ Must-fix
|
|
379
386
|
1. [DIMENSION] VP-XXX issue description
|
|
@@ -105,8 +105,10 @@ export default defineConfig({
|
|
|
105
105
|
/* Per-action timeout (click, fill, etc.) */
|
|
106
106
|
actionTimeout: 10_000,
|
|
107
107
|
|
|
108
|
-
/*
|
|
109
|
-
|
|
108
|
+
/* Trace for a failed test. CI: recorded on the first retry (CI retries). Local: no retries, so
|
|
109
|
+
'on-first-retry' would never record one — keep the trace of every failed test instead.
|
|
110
|
+
See https://playwright.dev/docs/trace-viewer */
|
|
111
|
+
trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure',
|
|
110
112
|
|
|
111
113
|
/**
|
|
112
114
|
* Capture screenshot after each test failure.
|
|
@@ -42,18 +42,30 @@ interface LocaleInjection {
|
|
|
42
42
|
|
|
43
43
|
let cached: LocaleInjection | null | undefined;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Absent config → null, and locale injection is simply off (the documented no-op).
|
|
47
|
+
*
|
|
48
|
+
* A config that EXISTS but does not parse is a different thing and must not read the same way: the
|
|
49
|
+
* header above tells you to hand-edit this file, and a trailing comma would otherwise turn a
|
|
50
|
+
* `SUNGEN_ENV=ja` run into a base-locale run that asserts base-locale strings — green, and wrong.
|
|
51
|
+
* We warn rather than throw, because the injection is genuinely optional and failing every test on
|
|
52
|
+
* it would be a larger change than the fault warrants; the line names the file and the consequence.
|
|
53
|
+
*/
|
|
45
54
|
function loadLocaleConfig(): LocaleInjection | null {
|
|
46
55
|
if (cached !== undefined) return cached;
|
|
56
|
+
const cfgPath = path.join(__dirname, 'locale-config.json');
|
|
57
|
+
if (!fs.existsSync(cfgPath)) {
|
|
58
|
+
cached = null;
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
47
61
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return parsed;
|
|
56
|
-
} catch {
|
|
62
|
+
cached = JSON.parse(fs.readFileSync(cfgPath, 'utf-8')) as LocaleInjection;
|
|
63
|
+
return cached;
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error(
|
|
66
|
+
`⚠️ ${cfgPath}: unreadable (${e instanceof Error ? e.message.split('\n')[0] : String(e)}) — ` +
|
|
67
|
+
`locale state is NOT injected, so this SUNGEN_ENV run behaves as the base locale.`,
|
|
68
|
+
);
|
|
57
69
|
cached = null;
|
|
58
70
|
return null;
|
|
59
71
|
}
|
|
Binary file
|
|
@@ -46,6 +46,16 @@ export interface UrlExpectation {
|
|
|
46
46
|
declared?: string;
|
|
47
47
|
/** The test-data key behind `declared`, so a failure can name what an author must go fix. */
|
|
48
48
|
dataRef?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The page selector declared `query: any` — the query string is not part of this page's identity,
|
|
51
|
+
* so SILENCE from the step stops meaning "there must be no query" (#748).
|
|
52
|
+
*
|
|
53
|
+
* Emitted only for a page whose entry says so. The default stays strict: a bare page means a bare
|
|
54
|
+
* page, which is the #464 assertion and the reason a filter regression cannot read as green.
|
|
55
|
+
* It is the no-`dataRef` branch alone that this relaxes — a step that declares `with {{v}}` still
|
|
56
|
+
* asserts every param it names, on this page as on any other.
|
|
57
|
+
*/
|
|
58
|
+
ignoreQuery?: boolean;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
/**
|
|
@@ -113,8 +123,14 @@ function locationMatches(pathname: RegExp, hostname: RegExp | undefined, u: URL)
|
|
|
113
123
|
* tracking/UTM param the test never cared about cannot turn it red.
|
|
114
124
|
* A declared fragment is asserted too; a declared value carrying no query
|
|
115
125
|
* at all constrains the path only (the compiler warns: SG-W014).
|
|
126
|
+
*
|
|
127
|
+
* `query: any` on the page's selector entry overrides the FIRST rule only (#748): silence then
|
|
128
|
+
* means "the query is not mine to judge" instead of "there must be none". It is the escape hatch
|
|
129
|
+
* for a page whose query is generated at run time — `/admins/detail?id=<the row just clicked>` —
|
|
130
|
+
* which the strict rule makes unassertable and `with {{v}}` cannot express, since that asserts a
|
|
131
|
+
* literal. The second rule is untouched: declare params and they are still all checked.
|
|
116
132
|
*/
|
|
117
|
-
export function urlMatches({ pathname, hostname, declared, dataRef }: UrlExpectation): (u: URL) => boolean {
|
|
133
|
+
export function urlMatches({ pathname, hostname, declared, dataRef, ignoreQuery }: UrlExpectation): (u: URL) => boolean {
|
|
118
134
|
return (u: URL): boolean => {
|
|
119
135
|
if (!locationMatches(pathname, hostname, u)) return false;
|
|
120
136
|
|
|
@@ -123,7 +139,13 @@ export function urlMatches({ pathname, hostname, declared, dataRef }: UrlExpecta
|
|
|
123
139
|
// undefined, and they mean opposite things: the first is a real assertion (a bare page must be
|
|
124
140
|
// bare), the second is broken test-data. Only the generator knows which, and it says so by
|
|
125
141
|
// emitting `dataRef` exactly when the step carried a `with {{v}}`.
|
|
126
|
-
|
|
142
|
+
// `query: any` on the page entry (#748): the query is not part of this page's identity, so
|
|
143
|
+
// silence from the step is not an assertion about it. Without this there is NO way to express
|
|
144
|
+
// "this path, whatever the query" — `with {{v}}` asserts a LITERAL value, and a detail page
|
|
145
|
+
// keyed by `?id=<the row just clicked>` has no literal to declare. Checked before the bare-page
|
|
146
|
+
// rule, never instead of the declared-value rule below: a step that names params still asserts
|
|
147
|
+
// them here exactly as anywhere else.
|
|
148
|
+
if (!dataRef) return ignoreQuery ? true : u.search === '';
|
|
127
149
|
|
|
128
150
|
// Silence is the whole bug class this assertion exists to kill, so an unusable declared value
|
|
129
151
|
// is an ERROR, not a quiet verdict. Returning false would read as "the app is wrong"; these
|
|
@@ -183,6 +205,26 @@ interface SettleOpts { settleMs?: number; networkIdleMs?: number }
|
|
|
183
205
|
* 3000) bounds step 1. Both are runtime knobs, so a slow environment is a config change, not a
|
|
184
206
|
* regenerate.
|
|
185
207
|
*/
|
|
208
|
+
/**
|
|
209
|
+
* A millisecond budget from the environment. `Number()` alone turned `"1s"` / `"abc"` into NaN and
|
|
210
|
+
* `""` into 0 (`??` keeps an empty string): the hold loop then never ran, so a late redirect passed
|
|
211
|
+
* unseen, and a 0 / NaN networkidle timeout means "no timeout" to Playwright — the step waited out
|
|
212
|
+
* the whole test (#856). Unset or empty means the default; a malformed value falls back to it and
|
|
213
|
+
* says so, once per variable. `allowZero` for a knob where 0 is a real choice (no hold).
|
|
214
|
+
*/
|
|
215
|
+
const warnedEnv = new Set<string>();
|
|
216
|
+
function msEnv(name: string, fallback: number, allowZero = false): number {
|
|
217
|
+
const raw = process.env[name]?.trim();
|
|
218
|
+
if (!raw) return fallback;
|
|
219
|
+
const value = Number(raw);
|
|
220
|
+
if (Number.isFinite(value) && (value > 0 || (allowZero && value === 0))) return value;
|
|
221
|
+
if (!warnedEnv.has(name)) {
|
|
222
|
+
warnedEnv.add(name);
|
|
223
|
+
console.warn(`[sungen] ${name}=${JSON.stringify(raw)} is not a ${allowZero ? 'non-negative' : 'positive'} number of milliseconds — using the default ${fallback}.`);
|
|
224
|
+
}
|
|
225
|
+
return fallback;
|
|
226
|
+
}
|
|
227
|
+
|
|
186
228
|
async function settleAndHold(
|
|
187
229
|
page: SettleablePage,
|
|
188
230
|
matches: (u: URL) => boolean,
|
|
@@ -190,8 +232,9 @@ async function settleAndHold(
|
|
|
190
232
|
label: string,
|
|
191
233
|
consequence: string,
|
|
192
234
|
): Promise<void> {
|
|
193
|
-
|
|
194
|
-
const
|
|
235
|
+
// 0 holds nothing (the operator's call); 0 for networkidle would mean "no timeout", so it is refused.
|
|
236
|
+
const settleMs = opts.settleMs ?? msEnv('SUNGEN_URL_SETTLE_MS', 1000, true);
|
|
237
|
+
const networkIdleMs = opts.networkIdleMs ?? msEnv('SUNGEN_URL_NETWORKIDLE_MS', 3000);
|
|
195
238
|
const arrivedAt = page.url();
|
|
196
239
|
|
|
197
240
|
await page.waitForLoadState('networkidle', { timeout: networkIdleMs }).catch(() => undefined);
|
|
@@ -48,6 +48,14 @@ function evidenceSlug(s: string): string {
|
|
|
48
48
|
.slice(0, 80) || 'unnamed';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* True only in the launcher process. `@wdio/runner` re-parses this very config inside EVERY worker
|
|
53
|
+
* it spawns, so anything logged at module scope prints once per spec unless it is gated. WDIO's own
|
|
54
|
+
* "am I a worker" test is this env var (`@wdio/local-runner` sets it when spawning; `@wdio/utils`
|
|
55
|
+
* reads it) — reuse it rather than inventing a second signal.
|
|
56
|
+
*/
|
|
57
|
+
const IS_LAUNCHER = !process.env.WDIO_WORKER_ID;
|
|
58
|
+
|
|
51
59
|
/**
|
|
52
60
|
* Auto-select MOBILE specs and classify each by its target OS (dual-target routing).
|
|
53
61
|
*
|
|
@@ -82,7 +90,12 @@ function classifyMobileSpecs(root: string): Array<{ file: string; platform: stri
|
|
|
82
90
|
.filter((full) => {
|
|
83
91
|
try {
|
|
84
92
|
return fs.readFileSync(full, 'utf-8').includes('@wdio/globals');
|
|
85
|
-
} catch {
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// A spec listed by readdir that cannot be read cannot be classified, so it is excluded.
|
|
95
|
+
// Excluding it SILENTLY is the fault: WDIO only fails the run when the spec list is
|
|
96
|
+
// EMPTY ("No specs found to run"), so dropping one spec out of ten leaves a green suite
|
|
97
|
+
// containing a test that never executed. Name it, and let the count below show the shortfall.
|
|
98
|
+
if (IS_LAUNCHER) console.warn(`[sungen] could not read ${full} — EXCLUDED from this run: ${(e as Error).message}`);
|
|
86
99
|
return false;
|
|
87
100
|
}
|
|
88
101
|
})
|
|
@@ -98,6 +111,16 @@ const androidSpecs = specEntries.filter((e) => e.platform !== 'ios').map((e) =>
|
|
|
98
111
|
const iosSpecs = specEntries.filter((e) => e.platform !== 'android').map((e) => e.file);
|
|
99
112
|
const mobileSpecs = specEntries.map((e) => e.file); // union — top-level fallback
|
|
100
113
|
|
|
114
|
+
// What DISCOVERY found — a mobile suite is only trustworthy if you can see how many specs it
|
|
115
|
+
// resolved: WDIO fails loudly on an EMPTY list but says nothing when the list is merely short,
|
|
116
|
+
// which is exactly what a dropped spec looks like.
|
|
117
|
+
//
|
|
118
|
+
// Two things this number is not. It is not an error on 0: WDIO already exits 1 there
|
|
119
|
+
// ("No specs found to run"), and a `--shard` that legitimately receives none must still return 0.
|
|
120
|
+
// And under `--shard` it is the PRE-SHARD total, because ConfigParser slices `specs` down to the
|
|
121
|
+
// shard's portion only after this file has finished evaluating.
|
|
122
|
+
if (IS_LAUNCHER) console.log(`[sungen] specs: ${specEntries.length} mobile spec(s) — android ${androidSpecs.length}, ios ${iosSpecs.length}`);
|
|
123
|
+
|
|
101
124
|
// App under test — env overrides the init-configured defaults.
|
|
102
125
|
const APP_PACKAGE = process.env.APP_PACKAGE || '__APP_PACKAGE__';
|
|
103
126
|
const APP_ACTIVITY = process.env.APP_ACTIVITY || '__APP_ACTIVITY__';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Move an existing project's `use.trace` off the old default so local runs keep a trace.
|
|
3
|
+
*
|
|
4
|
+
* The old template paired `trace: 'on-first-retry'` with `retries: process.env.CI ? 2 : 0`: off CI
|
|
5
|
+
* there is no retry, so there is never a "first retry" and a QA fixing a test on their machine never
|
|
6
|
+
* got a trace. The template now records `on-first-retry` on CI and `retain-on-failure` locally.
|
|
7
|
+
*
|
|
8
|
+
* `playwright.config.ts` is written once at `sungen init` and never re-synced (see
|
|
9
|
+
* report-sanitize-reporter-install.ts, the same problem for the sanitize reporter), so `sungen update`
|
|
10
|
+
* calls this. It rewrites the trace line ONLY while it is still the old default — any other value is
|
|
11
|
+
* a choice the user made, and a wrong edit to a user's config is worse than a missing trace. The same
|
|
12
|
+
* goes for a `trace` inside `projects: [...]`: that is a per-project override, not the template default.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
|
|
17
|
+
/** The template's trace value: cheap on CI (retries exist there), a trace for every local failure. */
|
|
18
|
+
export const TRACE_MODE_EXPRESSION = "process.env.CI ? 'on-first-retry' : 'retain-on-failure'";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 'patched' — the old default was rewritten now
|
|
22
|
+
* 'present' — already the template expression (idempotent)
|
|
23
|
+
* 'custom' — another value, no trace line, more than one old-default line, or one inside
|
|
24
|
+
* `projects: [...]`; nothing written
|
|
25
|
+
* 'absent' — no playwright.config.ts (a mobile-only project, or not a sungen project)
|
|
26
|
+
*/
|
|
27
|
+
export type TraceModeOutcome = 'patched' | 'present' | 'custom' | 'absent';
|
|
28
|
+
|
|
29
|
+
const escapeRegExp = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
30
|
+
|
|
31
|
+
// Both are anchored to the start of a line, so a commented-out copy (`// trace: …`) never counts.
|
|
32
|
+
// OLD_DEFAULT keeps the indentation in $1 and leaves the trailing comma, comment and EOL alone.
|
|
33
|
+
const OLD_DEFAULT = /^([ \t]*trace:[ \t]*)(['"])on-first-retry\2/gm;
|
|
34
|
+
const PRESENT = new RegExp(`^[ \\t]*trace:[ \\t]*${escapeRegExp(TRACE_MODE_EXPRESSION)}`, 'm');
|
|
35
|
+
|
|
36
|
+
// The old template's one-line comment, only when it sits directly above the (patched) trace line.
|
|
37
|
+
// A comment the user wrote is theirs and stays.
|
|
38
|
+
const OLD_COMMENT = new RegExp(
|
|
39
|
+
`^([ \\t]*)/\\* Collect trace when retrying the failed test\\. See https://playwright\\.dev/docs/trace-viewer \\*/(\\r?\\n)(?=[ \\t]*trace:[ \\t]*${escapeRegExp(TRACE_MODE_EXPRESSION)})`,
|
|
40
|
+
'm',
|
|
41
|
+
);
|
|
42
|
+
const newComment = (indent: string, eol: string): string =>
|
|
43
|
+
`${indent}/* Trace for a failed test. CI: recorded on the first retry (CI retries). Local: no retries, so${eol}` +
|
|
44
|
+
`${indent} 'on-first-retry' would never record one — keep the trace of every failed test instead.${eol}` +
|
|
45
|
+
`${indent} See https://playwright.dev/docs/trace-viewer */${eol}`;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* [start, end] offsets of each live `projects: [...]` array, matched by bracket depth. Brackets inside
|
|
49
|
+
* a `//` or `/* *\/` comment or a quoted string are skipped: one stray `]` in a note would otherwise
|
|
50
|
+
* close the span early and expose a per-project override to the patch.
|
|
51
|
+
*/
|
|
52
|
+
function projectsSpans(src: string): Array<[number, number]> {
|
|
53
|
+
const spans: Array<[number, number]> = [];
|
|
54
|
+
for (const m of src.matchAll(/^[ \t]*projects\s*:\s*\[/gm)) {
|
|
55
|
+
const open = m.index! + m[0].length - 1;
|
|
56
|
+
let depth = 0;
|
|
57
|
+
for (let i = open; i < src.length; i++) {
|
|
58
|
+
const ch = src[i];
|
|
59
|
+
if (ch === '/' && src[i + 1] === '/') { const nl = src.indexOf('\n', i); i = nl < 0 ? src.length : nl; continue; }
|
|
60
|
+
if (ch === '/' && src[i + 1] === '*') { const end = src.indexOf('*/', i + 2); i = end < 0 ? src.length : end + 1; continue; }
|
|
61
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
62
|
+
for (i++; i < src.length && src[i] !== ch; i++) if (src[i] === '\\') i++;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (ch === '[') depth++;
|
|
66
|
+
else if (ch === ']' && --depth === 0) { spans.push([open, i]); break; }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return spans;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function patchTraceMode(cwd: string, dryRun: boolean = false): TraceModeOutcome {
|
|
73
|
+
const configPath = path.join(cwd, 'playwright.config.ts');
|
|
74
|
+
if (!fs.existsSync(configPath)) return 'absent';
|
|
75
|
+
const src = fs.readFileSync(configPath, 'utf8');
|
|
76
|
+
if (PRESENT.test(src)) return 'present';
|
|
77
|
+
const hits = [...src.matchAll(OLD_DEFAULT)];
|
|
78
|
+
if (hits.length !== 1) return 'custom'; // none: the user's own value · several: ambiguous, don't guess
|
|
79
|
+
const at = hits[0].index!;
|
|
80
|
+
if (projectsSpans(src).some(([s, e]) => at > s && at < e)) return 'custom'; // a per-project override
|
|
81
|
+
if (!dryRun) {
|
|
82
|
+
const patched = src
|
|
83
|
+
.replace(OLD_DEFAULT, `$1${TRACE_MODE_EXPRESSION}`)
|
|
84
|
+
.replace(OLD_COMMENT, (_m, indent: string, eol: string) => newComment(indent, eol));
|
|
85
|
+
fs.writeFileSync(configPath, patched);
|
|
86
|
+
}
|
|
87
|
+
return 'patched';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `sungen update` step: patch the trace mode and say so when it changed. */
|
|
91
|
+
export function installTraceMode(cwd: string, dryRun: boolean = false): void {
|
|
92
|
+
if (patchTraceMode(cwd, dryRun) !== 'patched') return;
|
|
93
|
+
console.log(`${dryRun ? '📋 would update' : '✓ updated'} playwright.config.ts: local runs now keep a trace for every failed test (trace: ${TRACE_MODE_EXPRESSION})`);
|
|
94
|
+
}
|