@sun-asterisk/sungen 3.2.29 → 3.2.31
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/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/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/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +4 -2
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/render-coverage-sheet.d.ts.map +1 -1
- package/dist/exporters/matrix/render-coverage-sheet.js +3 -1
- package/dist/exporters/matrix/render-coverage-sheet.js.map +1 -1
- 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.map +1 -1
- package/dist/exporters/matrix/render-overview-sheet.js +5 -9
- package/dist/exporters/matrix/render-overview-sheet.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.map +1 -1
- package/dist/exporters/matrix/testcase-summary-band.js +2 -6
- package/dist/exporters/matrix/testcase-summary-band.js.map +1 -1
- 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/generators/gherkin-parser/index.d.ts +3 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +17 -9
- 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/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/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/table-paginate-action.hbs +3 -0
- 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/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/partials/locator.hbs +9 -1
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +54 -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 +3 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +3 -1
- 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 +14 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +13 -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-nth.hbs +1 -1
- 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 +160 -39
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +79 -5
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +78 -4
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +24 -5
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +92 -14
- package/dist/generators/test-generator/patterns/index.js.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 +63 -9
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +166 -28
- 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 +30 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +195 -15
- 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/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/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/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/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-error-mapping/SKILL.md +14 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +108 -8
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +9 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +4 -4
- package/dist/orchestrator/templates/playwright.config.ts +7 -0
- 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-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/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/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/exporters/api-testcase-formatter.ts +5 -7
- package/src/exporters/matrix/build.ts +4 -2
- package/src/exporters/matrix/render-coverage-sheet.ts +3 -1
- package/src/exporters/matrix/render-csv.ts +3 -1
- package/src/exporters/matrix/render-overview-sheet.ts +5 -10
- package/src/exporters/matrix/result-totals.ts +37 -0
- package/src/exporters/matrix/testcase-summary-band.ts +2 -7
- 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/generators/gherkin-parser/index.ts +21 -10
- 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/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/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/table-paginate-action.hbs +3 -0
- 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/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/partials/locator.hbs +9 -1
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +54 -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 +3 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +3 -1
- 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 +14 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +13 -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-nth.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
- package/src/generators/test-generator/code-generator.ts +176 -45
- package/src/generators/test-generator/diagnostics.ts +79 -5
- package/src/generators/test-generator/patterns/index.ts +97 -16
- package/src/generators/test-generator/patterns/with-parent-scope.ts +41 -0
- package/src/generators/test-generator/step-mapper.ts +170 -26
- 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 +245 -16
- package/src/generators/test-generator/utils/selector-type-validation.ts +87 -0
- package/src/harness/data-driven-lint.ts +192 -32
- package/src/harness/script-check.ts +186 -21
- 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/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-error-mapping/SKILL.md +14 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +108 -8
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +9 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +4 -4
- package/src/orchestrator/templates/playwright.config.ts +7 -0
- package/src/orchestrator/templates/specs-checked-state.ts +324 -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/utils/selector-types.ts +54 -5
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sanitizeReportPaths = sanitizeReportPaths;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
class ReportPathSanitizeReporter {
|
|
40
|
+
onBegin(config) {
|
|
41
|
+
this.config = config;
|
|
42
|
+
}
|
|
43
|
+
printsToStdio() {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
async onExit() {
|
|
47
|
+
const file = this.jsonOutputFile();
|
|
48
|
+
if (!file || !fs.existsSync(file))
|
|
49
|
+
return;
|
|
50
|
+
const root = this.configDir();
|
|
51
|
+
let doc;
|
|
52
|
+
try {
|
|
53
|
+
doc = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return; // not ours to repair — leave whatever the JSON reporter wrote
|
|
57
|
+
}
|
|
58
|
+
const { doc: clean, rewritten } = sanitizeReportPaths(doc, { root });
|
|
59
|
+
if (rewritten === 0)
|
|
60
|
+
return;
|
|
61
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
62
|
+
fs.writeFileSync(tmp, JSON.stringify(clean, null, 2));
|
|
63
|
+
fs.renameSync(tmp, file);
|
|
64
|
+
}
|
|
65
|
+
configDir() {
|
|
66
|
+
return this.config?.configFile ? path.dirname(this.config.configFile) : process.cwd();
|
|
67
|
+
}
|
|
68
|
+
/** The JSON reporter's output file: its `outputFile` option, else the env var the config reads. */
|
|
69
|
+
jsonOutputFile() {
|
|
70
|
+
const entry = (this.config?.reporter ?? []).find(([name]) => name === 'json');
|
|
71
|
+
const opt = entry?.[1]?.outputFile
|
|
72
|
+
?? process.env.PLAYWRIGHT_JSON_OUTPUT_FILE // Playwright's own override
|
|
73
|
+
?? process.env.PLAYWRIGHT_JSON_OUTPUT_NAME; // the name sungen's config template reads
|
|
74
|
+
if (!opt)
|
|
75
|
+
return undefined;
|
|
76
|
+
if (path.isAbsolute(opt))
|
|
77
|
+
return opt;
|
|
78
|
+
// Playwright resolves a relative outputFile against the config directory; the env var against cwd.
|
|
79
|
+
const candidates = [path.resolve(this.configDir(), opt), path.resolve(process.cwd(), opt)];
|
|
80
|
+
return candidates.find((c) => fs.existsSync(c)) ?? candidates[0];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.default = ReportPathSanitizeReporter;
|
|
84
|
+
// ── sanitizer — the same rules as sungen's `report-path-sanitizer.ts`, embedded so this file needs no
|
|
85
|
+
// import from the compiler package (a parity test in sungen keeps the two in step) ──────────────
|
|
86
|
+
const ABSOLUTE = /^(?:file:\/\/\/?|[A-Za-z]:[\\/]|\/)/;
|
|
87
|
+
const WINDOWS_DRIVE = /^[A-Za-z]:/;
|
|
88
|
+
const HOME_DIR = /(?<![\w.:/\\-])(?:file:\/\/\/?)?(?:\/(?:Users|home)\/[^/\\\s"'`)\]]+[/\\]|[A-Za-z]:[\\/]Users[\\/][^/\\\s"'`)\]]+[\\/])/g;
|
|
89
|
+
function sanitizeReportPaths(doc, opts) {
|
|
90
|
+
const root = normalizeRoot(opts.root);
|
|
91
|
+
if (root.posix.length < 2)
|
|
92
|
+
return { doc, rewritten: 0 }; // a `/` root would shred the document
|
|
93
|
+
let rewritten = 0;
|
|
94
|
+
const walk = (v) => {
|
|
95
|
+
if (typeof v === 'string') {
|
|
96
|
+
const next = sanitizeString(v, root);
|
|
97
|
+
if (next !== v)
|
|
98
|
+
rewritten++;
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(v))
|
|
102
|
+
return v.map(walk);
|
|
103
|
+
if (v && typeof v === 'object') {
|
|
104
|
+
const out = {};
|
|
105
|
+
for (const [k, x] of Object.entries(v))
|
|
106
|
+
out[k] = walk(x);
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
return v;
|
|
110
|
+
};
|
|
111
|
+
return { doc: walk(doc), rewritten };
|
|
112
|
+
}
|
|
113
|
+
function normalizeRoot(raw) {
|
|
114
|
+
const posix = toPosix(stripFileScheme(raw)).replace(/\/+$/, '');
|
|
115
|
+
const caseInsensitive = WINDOWS_DRIVE.test(posix);
|
|
116
|
+
const body = posix.split('/').map(escapeRegExp).join('[\\\\/]');
|
|
117
|
+
return { posix, caseInsensitive, pattern: new RegExp(`(?:file:\\/\\/\\/?)?${body}(?:[\\\\/](?![\\\\/])|(?=$|[\\s"'\`):,]))`, caseInsensitive ? 'gi' : 'g') };
|
|
118
|
+
}
|
|
119
|
+
function sanitizeString(value, root) {
|
|
120
|
+
const trimmed = value.trim();
|
|
121
|
+
if (ABSOLUTE.test(trimmed) && !/\s/.test(trimmed)) {
|
|
122
|
+
const p = toPosix(stripFileScheme(trimmed)).replace(/\/+$/, '');
|
|
123
|
+
const a = root.caseInsensitive ? p.toLowerCase() : p;
|
|
124
|
+
const b = root.caseInsensitive ? root.posix.toLowerCase() : root.posix;
|
|
125
|
+
if (a === b)
|
|
126
|
+
return '.';
|
|
127
|
+
if (a.startsWith(b + '/'))
|
|
128
|
+
return p.slice(root.posix.length + 1);
|
|
129
|
+
// outside the root: only a machine path (home dir, drive) is cut to its basename — `/products` is a route
|
|
130
|
+
if (WINDOWS_DRIVE.test(p) || /\/(?:Users|home)\/[^/]+\/./.test(p)) {
|
|
131
|
+
// another machine's checkout of this project → relative from the project directory; else basename
|
|
132
|
+
const marker = '/' + root.posix.slice(root.posix.lastIndexOf('/') + 1) + '/';
|
|
133
|
+
const i = marker.length > 2 ? (root.caseInsensitive ? p.toLowerCase().indexOf(marker.toLowerCase()) : p.indexOf(marker)) : -1;
|
|
134
|
+
if (i >= 0)
|
|
135
|
+
return p.slice(i + marker.length);
|
|
136
|
+
return p.slice(p.lastIndexOf('/') + 1) || p;
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
const out = value.replace(root.pattern, (m) => (/[\\/]$/.test(m) ? '' : '.'));
|
|
141
|
+
return out.replace(HOME_DIR, '~/');
|
|
142
|
+
}
|
|
143
|
+
function stripFileScheme(p) {
|
|
144
|
+
if (!p.startsWith('file://'))
|
|
145
|
+
return p;
|
|
146
|
+
const rest = p.slice('file://'.length);
|
|
147
|
+
return /^\/[A-Za-z]:/.test(rest) ? rest.slice(1) : rest;
|
|
148
|
+
}
|
|
149
|
+
function toPosix(p) {
|
|
150
|
+
return p.replace(/\\/g, '/');
|
|
151
|
+
}
|
|
152
|
+
function escapeRegExp(s) {
|
|
153
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=specs-report-path-sanitize-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specs-report-path-sanitize-reporter.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-report-path-sanitize-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright reporter that rewrites the operator's machine paths out of the JSON result file.
|
|
3
|
+
*
|
|
4
|
+
* The JSON reporter records absolute paths — `config.configFile`, `rootDir`, `projects[]`, every
|
|
5
|
+
* `error.location.file`, attachment paths, and the paths quoted inside stack traces and messages.
|
|
6
|
+
* Teams commit that file next to the spec (sungen delivery reads it), so a username and a machine
|
|
7
|
+
* layout travel into the repository and the deliverable. Nothing downstream needs them absolute.
|
|
8
|
+
*
|
|
9
|
+
* Runs in `onExit()` — after every reporter, the JSON one included, has finished writing — reads the
|
|
10
|
+
* JSON reporter's `outputFile`, rewrites it in place, and stays silent otherwise. Paths under the
|
|
11
|
+
* project root (the directory of `playwright.config.ts`) become project-relative; outside it a path that
|
|
12
|
+
* names a machine (a home directory, a drive) keeps only its basename while any other absolute string
|
|
13
|
+
* — a URL route — is left alone; home directories quoted inside text become `~/`. `sungen results
|
|
14
|
+
* sanitize` applies the same rules to files that already exist.
|
|
15
|
+
*
|
|
16
|
+
* Wired in playwright.config.ts: reporter: [['html'], ['json', {…}], ['./specs/generated/report-path-sanitize-reporter.ts']].
|
|
17
|
+
* Regenerated by `sungen generate` / `sungen update` — DO NOT EDIT.
|
|
18
|
+
*/
|
|
19
|
+
import type { FullConfig, Reporter } from '@playwright/test/reporter';
|
|
20
|
+
import * as fs from 'node:fs';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
|
|
23
|
+
class ReportPathSanitizeReporter implements Reporter {
|
|
24
|
+
private config?: FullConfig;
|
|
25
|
+
|
|
26
|
+
onBegin(config: FullConfig): void {
|
|
27
|
+
this.config = config;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
printsToStdio(): boolean {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async onExit(): Promise<void> {
|
|
35
|
+
const file = this.jsonOutputFile();
|
|
36
|
+
if (!file || !fs.existsSync(file)) return;
|
|
37
|
+
const root = this.configDir();
|
|
38
|
+
let doc: unknown;
|
|
39
|
+
try {
|
|
40
|
+
doc = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
41
|
+
} catch {
|
|
42
|
+
return; // not ours to repair — leave whatever the JSON reporter wrote
|
|
43
|
+
}
|
|
44
|
+
const { doc: clean, rewritten } = sanitizeReportPaths(doc, { root });
|
|
45
|
+
if (rewritten === 0) return;
|
|
46
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
47
|
+
fs.writeFileSync(tmp, JSON.stringify(clean, null, 2));
|
|
48
|
+
fs.renameSync(tmp, file);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private configDir(): string {
|
|
52
|
+
return this.config?.configFile ? path.dirname(this.config.configFile) : process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The JSON reporter's output file: its `outputFile` option, else the env var the config reads. */
|
|
56
|
+
private jsonOutputFile(): string | undefined {
|
|
57
|
+
const entry = (this.config?.reporter ?? []).find(([name]) => name === 'json');
|
|
58
|
+
const opt = (entry?.[1] as { outputFile?: string } | undefined)?.outputFile
|
|
59
|
+
?? process.env.PLAYWRIGHT_JSON_OUTPUT_FILE // Playwright's own override
|
|
60
|
+
?? process.env.PLAYWRIGHT_JSON_OUTPUT_NAME; // the name sungen's config template reads
|
|
61
|
+
if (!opt) return undefined;
|
|
62
|
+
if (path.isAbsolute(opt)) return opt;
|
|
63
|
+
// Playwright resolves a relative outputFile against the config directory; the env var against cwd.
|
|
64
|
+
const candidates = [path.resolve(this.configDir(), opt), path.resolve(process.cwd(), opt)];
|
|
65
|
+
return candidates.find((c) => fs.existsSync(c)) ?? candidates[0];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default ReportPathSanitizeReporter;
|
|
70
|
+
|
|
71
|
+
// ── sanitizer — the same rules as sungen's `report-path-sanitizer.ts`, embedded so this file needs no
|
|
72
|
+
// import from the compiler package (a parity test in sungen keeps the two in step) ──────────────
|
|
73
|
+
|
|
74
|
+
const ABSOLUTE = /^(?:file:\/\/\/?|[A-Za-z]:[\\/]|\/)/;
|
|
75
|
+
const WINDOWS_DRIVE = /^[A-Za-z]:/;
|
|
76
|
+
const HOME_DIR = /(?<![\w.:/\\-])(?:file:\/\/\/?)?(?:\/(?:Users|home)\/[^/\\\s"'`)\]]+[/\\]|[A-Za-z]:[\\/]Users[\\/][^/\\\s"'`)\]]+[\\/])/g;
|
|
77
|
+
|
|
78
|
+
interface Root { posix: string; caseInsensitive: boolean; pattern: RegExp }
|
|
79
|
+
|
|
80
|
+
export function sanitizeReportPaths<T>(doc: T, opts: { root: string }): { doc: T; rewritten: number } {
|
|
81
|
+
const root = normalizeRoot(opts.root);
|
|
82
|
+
if (root.posix.length < 2) return { doc, rewritten: 0 }; // a `/` root would shred the document
|
|
83
|
+
let rewritten = 0;
|
|
84
|
+
const walk = (v: unknown): unknown => {
|
|
85
|
+
if (typeof v === 'string') {
|
|
86
|
+
const next = sanitizeString(v, root);
|
|
87
|
+
if (next !== v) rewritten++;
|
|
88
|
+
return next;
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
91
|
+
if (v && typeof v === 'object') {
|
|
92
|
+
const out: Record<string, unknown> = {};
|
|
93
|
+
for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = walk(x);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
return v;
|
|
97
|
+
};
|
|
98
|
+
return { doc: walk(doc) as T, rewritten };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeRoot(raw: string): Root {
|
|
102
|
+
const posix = toPosix(stripFileScheme(raw)).replace(/\/+$/, '');
|
|
103
|
+
const caseInsensitive = WINDOWS_DRIVE.test(posix);
|
|
104
|
+
const body = posix.split('/').map(escapeRegExp).join('[\\\\/]');
|
|
105
|
+
return { posix, caseInsensitive, pattern: new RegExp(`(?:file:\\/\\/\\/?)?${body}(?:[\\\\/](?![\\\\/])|(?=$|[\\s"'\`):,]))`, caseInsensitive ? 'gi' : 'g') };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sanitizeString(value: string, root: Root): string {
|
|
109
|
+
const trimmed = value.trim();
|
|
110
|
+
if (ABSOLUTE.test(trimmed) && !/\s/.test(trimmed)) {
|
|
111
|
+
const p = toPosix(stripFileScheme(trimmed)).replace(/\/+$/, '');
|
|
112
|
+
const a = root.caseInsensitive ? p.toLowerCase() : p;
|
|
113
|
+
const b = root.caseInsensitive ? root.posix.toLowerCase() : root.posix;
|
|
114
|
+
if (a === b) return '.';
|
|
115
|
+
if (a.startsWith(b + '/')) return p.slice(root.posix.length + 1);
|
|
116
|
+
// outside the root: only a machine path (home dir, drive) is cut to its basename — `/products` is a route
|
|
117
|
+
if (WINDOWS_DRIVE.test(p) || /\/(?:Users|home)\/[^/]+\/./.test(p)) {
|
|
118
|
+
// another machine's checkout of this project → relative from the project directory; else basename
|
|
119
|
+
const marker = '/' + root.posix.slice(root.posix.lastIndexOf('/') + 1) + '/';
|
|
120
|
+
const i = marker.length > 2 ? (root.caseInsensitive ? p.toLowerCase().indexOf(marker.toLowerCase()) : p.indexOf(marker)) : -1;
|
|
121
|
+
if (i >= 0) return p.slice(i + marker.length);
|
|
122
|
+
return p.slice(p.lastIndexOf('/') + 1) || p;
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
const out = value.replace(root.pattern, (m: string) => (/[\\/]$/.test(m) ? '' : '.'));
|
|
127
|
+
return out.replace(HOME_DIR, '~/');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function stripFileScheme(p: string): string {
|
|
131
|
+
if (!p.startsWith('file://')) return p;
|
|
132
|
+
const rest = p.slice('file://'.length);
|
|
133
|
+
return /^\/[A-Za-z]:/.test(rest) ? rest.slice(1) : rest;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function toPosix(p: string): string {
|
|
137
|
+
return p.replace(/\\/g, '/');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function escapeRegExp(s: string): string {
|
|
141
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
142
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sungen URL assertion helper — auto-generated into specs/url-assert.ts. DO NOT EDIT.
|
|
3
3
|
*
|
|
4
|
-
* Every Then-side
|
|
4
|
+
* Every URL step compiles to one call here: the Then-side assertions (`see [X] page`,
|
|
5
|
+
* `is on [X] page`) to `expectPage`, and `wait for [X] page` to `waitForPage`.
|
|
5
6
|
*
|
|
6
7
|
* Why a helper and not an inline predicate. The predicate used to be emitted inline into every
|
|
7
8
|
* spec, so each correction to what "the URL matches" MEANS was a breaking change for every
|
|
@@ -24,6 +25,16 @@ export interface UrlExpectation {
|
|
|
24
25
|
* against a full URL.
|
|
25
26
|
*/
|
|
26
27
|
pathname: RegExp;
|
|
28
|
+
/**
|
|
29
|
+
* Anchored hostname pattern, emitted ONLY when the page selector's `value` is an ABSOLUTE URL
|
|
30
|
+
* (`https://app.example.com/reception/:id`) — a destination on a different host from the suite's
|
|
31
|
+
* baseURL. Absent means "whatever host we are on", which is what a relative page value says.
|
|
32
|
+
*
|
|
33
|
+
* It is a SEPARATE field rather than part of `pathname` because the two are read off different
|
|
34
|
+
* properties of the parsed URL: `u.hostname` never appears in `u.pathname`. One concatenated
|
|
35
|
+
* regex only worked while the match ran unanchored over the whole URL string (#677).
|
|
36
|
+
*/
|
|
37
|
+
hostname?: RegExp;
|
|
27
38
|
/**
|
|
28
39
|
* The `with {{v}}` value, raw from test-data: '?q=1' · '/vi/search?q=1' · '#tab' · ''.
|
|
29
40
|
* `undefined` means the step declared nothing, which is itself an assertion — see below.
|
|
@@ -63,36 +74,50 @@ export declare function joinPath(base: string, path: string, declared?: string |
|
|
|
63
74
|
* A declared fragment is asserted too; a declared value carrying no query
|
|
64
75
|
* at all constrains the path only (the compiler warns: SG-W014).
|
|
65
76
|
*/
|
|
66
|
-
export declare function urlMatches({ pathname, declared, dataRef }: UrlExpectation): (u: URL) => boolean;
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
* router pushes the destination URL before the route guard or the API has answered, so a user
|
|
70
|
-
* with no permission is "on" `/items/detail` for a few hundred milliseconds — long enough for
|
|
71
|
-
* the assertion to sample it, pass, and end the test green — and is then bounced to `/forbidden`
|
|
72
|
-
* with no assertion left to see it. A real project shipped a permission matrix on that oracle;
|
|
73
|
-
* the 403s were found by hand (#662).
|
|
74
|
-
*
|
|
75
|
-
* `expectPage` is the page oracle `see [X] page` compiles to now. Arriving is not enough: the URL
|
|
76
|
-
* has to be STILL matching once the page has settled.
|
|
77
|
-
* 1. arrive — the same predicate `toHaveURL` always used;
|
|
78
|
-
* 2. settle — wait for network to go quiet (bounded: an app that polls forever must not hang
|
|
79
|
-
* the test, so a timeout here is not a failure);
|
|
80
|
-
* 3. hold — sample the URL over a settle window; the moment it stops matching, fail and
|
|
81
|
-
* NAME where it went, because that destination is the finding;
|
|
82
|
-
* 4. re-assert — the arrival predicate, once more, on the settled page.
|
|
83
|
-
*
|
|
84
|
-
* `SUNGEN_URL_SETTLE_MS` (default 1000) is the hold window; `SUNGEN_URL_NETWORKIDLE_MS` (default
|
|
85
|
-
* 3000) bounds step 2. Both are runtime knobs, so a slow environment is a config change, not a
|
|
86
|
-
* regenerate.
|
|
87
|
-
*/
|
|
88
|
-
export declare function expectPage(page: {
|
|
77
|
+
export declare function urlMatches({ pathname, hostname, declared, dataRef }: UrlExpectation): (u: URL) => boolean;
|
|
78
|
+
/** What `settleAndHold` needs of a page — Playwright's `Page` satisfies it; so does a test fake. */
|
|
79
|
+
type SettleablePage = {
|
|
89
80
|
url(): string;
|
|
90
81
|
waitForLoadState(state: 'networkidle', opts?: {
|
|
91
82
|
timeout?: number;
|
|
92
83
|
}): Promise<void>;
|
|
93
|
-
}
|
|
84
|
+
};
|
|
85
|
+
/** The two runtime knobs, shared by both URL steps. */
|
|
86
|
+
interface SettleOpts {
|
|
94
87
|
settleMs?: number;
|
|
95
88
|
networkIdleMs?: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* `expectPage` is the page oracle `see [X] page` / `is on [X] page` compile to. Arriving is not
|
|
92
|
+
* enough: the URL has to be STILL matching once the page has settled.
|
|
93
|
+
* 1. arrive — the same predicate `toHaveURL` always used;
|
|
94
|
+
* 2. settle + hold — see `settleAndHold`;
|
|
95
|
+
* 3. re-assert — the arrival predicate, once more, on the settled page.
|
|
96
|
+
*/
|
|
97
|
+
export declare function expectPage(page: SettleablePage, expectation: UrlExpectation, opts?: SettleOpts & {
|
|
96
98
|
assertUrl?: (matches: (u: URL) => boolean) => Promise<void>;
|
|
97
99
|
}): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* `waitForPage` is what `When User wait for [X] page` compiles to (#677).
|
|
102
|
+
*
|
|
103
|
+
* A bare `page.waitForURL(/…/)` resolves on the first URL sample that matches — the transient URL
|
|
104
|
+
* the router pushed before its guard decided. The wait then returned, and every step AFTER it ran
|
|
105
|
+
* against a page mid-redirect, so their failures pointed at the wrong thing. It was also the third
|
|
106
|
+
* hand-rolled copy of "does this URL match", drifting from the pair #662 consolidated here.
|
|
107
|
+
*
|
|
108
|
+
* Where this deliberately DIFFERS from `expectPage`: the query is not constrained. A Then-side page
|
|
109
|
+
* assertion is an oracle, so a bare page must be bare — `/vi/search` fails on `/vi/search?x=1`. A
|
|
110
|
+
* wait is not an oracle: `wait for [Results] page` after submitting a search legitimately lands on
|
|
111
|
+
* `/results?q=abc`, and reddening there would assert something the step never said. Prove the query
|
|
112
|
+
* with the `Then User see [Results] page with {{v}}` that follows.
|
|
113
|
+
*/
|
|
114
|
+
export declare function waitForPage(page: SettleablePage & {
|
|
115
|
+
waitForURL(url: (u: URL) => boolean, opts?: {
|
|
116
|
+
timeout?: number;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
}, expectation: UrlExpectation, opts?: SettleOpts & {
|
|
119
|
+
timeout?: number;
|
|
120
|
+
waitUrl?: (matches: (u: URL) => boolean) => Promise<void>;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
export {};
|
|
98
123
|
//# sourceMappingURL=specs-url-assert.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-url-assert.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-url-assert.ts"],"names":[],"mappings":"AACA
|
|
1
|
+
{"version":3,"file":"specs-url-assert.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-url-assert.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAYrF;AAwBD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,cAAc,GAAG,CAAC,CAAC,EAAE,GAAG,KAAK,OAAO,CAuCzG;AAED,oGAAoG;AACpG,KAAK,cAAc,GAAG;IACpB,GAAG,IAAI,MAAM,CAAC;IACd,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpF,CAAC;AAEF,uDAAuD;AACvD,UAAU,UAAU;IAAG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;AAgDlE;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,cAAc,EAC3B,IAAI,GAAE,UAAU,GAAG;IAAE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GACtF,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,GAAG;IAAE,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,EAC3G,WAAW,EAAE,cAAc,EAC3B,IAAI,GAAE,UAAU,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GACtG,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Sungen URL assertion helper — auto-generated into specs/url-assert.ts. DO NOT EDIT.
|
|
5
5
|
*
|
|
6
|
-
* Every Then-side
|
|
6
|
+
* Every URL step compiles to one call here: the Then-side assertions (`see [X] page`,
|
|
7
|
+
* `is on [X] page`) to `expectPage`, and `wait for [X] page` to `waitForPage`.
|
|
7
8
|
*
|
|
8
9
|
* Why a helper and not an inline predicate. The predicate used to be emitted inline into every
|
|
9
10
|
* spec, so each correction to what "the URL matches" MEANS was a breaking change for every
|
|
@@ -22,6 +23,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
23
|
exports.joinPath = joinPath;
|
|
23
24
|
exports.urlMatches = urlMatches;
|
|
24
25
|
exports.expectPage = expectPage;
|
|
26
|
+
exports.waitForPage = waitForPage;
|
|
25
27
|
const test_1 = require("@playwright/test");
|
|
26
28
|
/**
|
|
27
29
|
* Join a page's URL with the `with {{v}}` value for a NAVIGATION step
|
|
@@ -72,6 +74,15 @@ function decodedPath(pathname) {
|
|
|
72
74
|
return pathname;
|
|
73
75
|
}
|
|
74
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Is the URL at the expected LOCATION — right host, right path? The half every URL step shares.
|
|
79
|
+
* The Then-side assertion goes on to judge the query; the wait stops here (see `waitForPage`).
|
|
80
|
+
*/
|
|
81
|
+
function locationMatches(pathname, hostname, u) {
|
|
82
|
+
if (hostname && !hostname.test(u.hostname))
|
|
83
|
+
return false;
|
|
84
|
+
return pathname.test(u.pathname) || pathname.test(decodedPath(u.pathname));
|
|
85
|
+
}
|
|
75
86
|
/**
|
|
76
87
|
* Build the predicate for `expect(page).toHaveURL(...)`.
|
|
77
88
|
*
|
|
@@ -83,9 +94,9 @@ function decodedPath(pathname) {
|
|
|
83
94
|
* A declared fragment is asserted too; a declared value carrying no query
|
|
84
95
|
* at all constrains the path only (the compiler warns: SG-W014).
|
|
85
96
|
*/
|
|
86
|
-
function urlMatches({ pathname, declared, dataRef }) {
|
|
97
|
+
function urlMatches({ pathname, hostname, declared, dataRef }) {
|
|
87
98
|
return (u) => {
|
|
88
|
-
if (!
|
|
99
|
+
if (!locationMatches(pathname, hostname, u))
|
|
89
100
|
return false;
|
|
90
101
|
// `dataRef`, not `declared`, decides which rule applies. Both "the step declared nothing" and
|
|
91
102
|
// "the step declared {{v}} but the key resolved to nothing at runtime" arrive here as
|
|
@@ -121,44 +132,74 @@ function urlMatches({ pathname, declared, dataRef }) {
|
|
|
121
132
|
};
|
|
122
133
|
}
|
|
123
134
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
135
|
+
* A URL that MATCHES is not a page the user is still on. A client-side router pushes the
|
|
136
|
+
* destination URL before the route guard or the API has answered, so a user with no permission is
|
|
137
|
+
* "on" `/items/detail` for a few hundred milliseconds — long enough for a polling assertion to
|
|
138
|
+
* sample it and pass, or for a `waitForURL` to resolve — and is then bounced to `/forbidden` with
|
|
139
|
+
* nothing left watching. A real project shipped a permission matrix on that oracle; the 403s were
|
|
140
|
+
* found by hand (#662).
|
|
130
141
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* 2.
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* NAME where it went, because that destination is the finding;
|
|
138
|
-
* 4. re-assert — the arrival predicate, once more, on the settled page.
|
|
142
|
+
* So both URL steps go through this: having arrived,
|
|
143
|
+
* 1. settle — wait for network to go quiet (bounded: an app that polls forever must not hang
|
|
144
|
+
* the test, so a timeout here is not a failure);
|
|
145
|
+
* 2. hold — sample the URL over a settle window; the moment it stops matching, fail and
|
|
146
|
+
* NAME where it went, because that destination is the finding.
|
|
147
|
+
* The caller decides what arriving meant and what to do afterwards.
|
|
139
148
|
*
|
|
140
149
|
* `SUNGEN_URL_SETTLE_MS` (default 1000) is the hold window; `SUNGEN_URL_NETWORKIDLE_MS` (default
|
|
141
|
-
* 3000) bounds step
|
|
150
|
+
* 3000) bounds step 1. Both are runtime knobs, so a slow environment is a config change, not a
|
|
142
151
|
* regenerate.
|
|
143
152
|
*/
|
|
144
|
-
async function
|
|
153
|
+
async function settleAndHold(page, matches, opts, label, consequence) {
|
|
145
154
|
const settleMs = opts.settleMs ?? Number(process.env.SUNGEN_URL_SETTLE_MS ?? 1000);
|
|
146
155
|
const networkIdleMs = opts.networkIdleMs ?? Number(process.env.SUNGEN_URL_NETWORKIDLE_MS ?? 3000);
|
|
147
|
-
const matches = urlMatches(expectation);
|
|
148
|
-
const assertUrl = opts.assertUrl ?? (async (m) => { await (0, test_1.expect)(page).toHaveURL(m); });
|
|
149
|
-
await assertUrl(matches);
|
|
150
156
|
const arrivedAt = page.url();
|
|
151
157
|
await page.waitForLoadState('networkidle', { timeout: networkIdleMs }).catch(() => undefined);
|
|
152
158
|
const started = Date.now();
|
|
153
159
|
while (Date.now() - started < settleMs) {
|
|
154
160
|
const now = page.url();
|
|
155
161
|
if (!matches(new URL(now))) {
|
|
156
|
-
throw new Error(
|
|
162
|
+
throw new Error(`${label}: reached ${arrivedAt} and then left for ${now} after ${Date.now() - started}ms — `
|
|
157
163
|
+ 'a route guard, an error boundary or a redirect moved the user away after the URL had already '
|
|
158
|
-
+
|
|
164
|
+
+ `matched. ${consequence}`);
|
|
159
165
|
}
|
|
160
166
|
await new Promise((r) => setTimeout(r, 50));
|
|
161
167
|
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* `expectPage` is the page oracle `see [X] page` / `is on [X] page` compile to. Arriving is not
|
|
171
|
+
* enough: the URL has to be STILL matching once the page has settled.
|
|
172
|
+
* 1. arrive — the same predicate `toHaveURL` always used;
|
|
173
|
+
* 2. settle + hold — see `settleAndHold`;
|
|
174
|
+
* 3. re-assert — the arrival predicate, once more, on the settled page.
|
|
175
|
+
*/
|
|
176
|
+
async function expectPage(page, expectation, opts = {}) {
|
|
177
|
+
const matches = urlMatches(expectation);
|
|
178
|
+
const assertUrl = opts.assertUrl ?? (async (m) => { await (0, test_1.expect)(page).toHaveURL(m); });
|
|
162
179
|
await assertUrl(matches);
|
|
180
|
+
await settleAndHold(page, matches, opts, 'page assertion', 'The page the test asserted is not the page the user ended on.');
|
|
181
|
+
await assertUrl(matches);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* `waitForPage` is what `When User wait for [X] page` compiles to (#677).
|
|
185
|
+
*
|
|
186
|
+
* A bare `page.waitForURL(/…/)` resolves on the first URL sample that matches — the transient URL
|
|
187
|
+
* the router pushed before its guard decided. The wait then returned, and every step AFTER it ran
|
|
188
|
+
* against a page mid-redirect, so their failures pointed at the wrong thing. It was also the third
|
|
189
|
+
* hand-rolled copy of "does this URL match", drifting from the pair #662 consolidated here.
|
|
190
|
+
*
|
|
191
|
+
* Where this deliberately DIFFERS from `expectPage`: the query is not constrained. A Then-side page
|
|
192
|
+
* assertion is an oracle, so a bare page must be bare — `/vi/search` fails on `/vi/search?x=1`. A
|
|
193
|
+
* wait is not an oracle: `wait for [Results] page` after submitting a search legitimately lands on
|
|
194
|
+
* `/results?q=abc`, and reddening there would assert something the step never said. Prove the query
|
|
195
|
+
* with the `Then User see [Results] page with {{v}}` that follows.
|
|
196
|
+
*/
|
|
197
|
+
async function waitForPage(page, expectation, opts = {}) {
|
|
198
|
+
const { pathname, hostname } = expectation;
|
|
199
|
+
const matches = (u) => locationMatches(pathname, hostname, u);
|
|
200
|
+
// Playwright's own navigation timeout governs the arrival; only the hold is sungen's.
|
|
201
|
+
const arrive = opts.waitUrl ?? (async (m) => { await page.waitForURL(m, { timeout: opts.timeout }); });
|
|
202
|
+
await arrive(matches);
|
|
203
|
+
await settleAndHold(page, matches, opts, 'page wait', 'The steps after this wait would have run against a page the user is already being moved off.');
|
|
163
204
|
}
|
|
164
205
|
//# sourceMappingURL=specs-url-assert.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-url-assert.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-url-assert.ts"],"names":[],"mappings":";AAAA,oBAAoB;AACpB
|
|
1
|
+
{"version":3,"file":"specs-url-assert.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-url-assert.ts"],"names":[],"mappings":"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"}
|