@sun-asterisk/sungen 3.2.24-beta.6 → 3.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/context-router.d.ts.map +1 -1
- package/dist/capabilities/context-router.js +15 -7
- package/dist/capabilities/context-router.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +6 -48
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/changed.d.ts +14 -0
- package/dist/cli/commands/changed.d.ts.map +1 -0
- package/dist/cli/commands/changed.js +128 -0
- package/dist/cli/commands/changed.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +25 -1
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.js +1 -1
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts +19 -0
- package/dist/cli/commands/results-merge.d.ts.map +1 -0
- package/dist/cli/commands/results-merge.js +191 -0
- package/dist/cli/commands/results-merge.js.map +1 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +100 -12
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/exporters/api-catalog-loader.d.ts.map +1 -1
- package/dist/exporters/api-catalog-loader.js +10 -3
- package/dist/exporters/api-catalog-loader.js.map +1 -1
- package/dist/exporters/api-testcase-formatter.d.ts +9 -0
- package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
- package/dist/exporters/api-testcase-formatter.js +55 -3
- package/dist/exporters/api-testcase-formatter.js.map +1 -1
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +10 -1
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/json-exporter.d.ts.map +1 -1
- package/dist/exporters/json-exporter.js +6 -1
- package/dist/exporters/json-exporter.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +10 -0
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/export.js +1 -1
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +5 -1
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +4 -1
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +7 -0
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts +17 -2
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +68 -8
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/results-merge.d.ts +46 -0
- package/dist/exporters/results-merge.d.ts.map +1 -0
- package/dist/exporters/results-merge.js +148 -0
- package/dist/exporters/results-merge.js.map +1 -0
- package/dist/exporters/selector-key-resolver.d.ts +4 -0
- package/dist/exporters/selector-key-resolver.d.ts.map +1 -1
- package/dist/exporters/selector-key-resolver.js +101 -1
- package/dist/exporters/selector-key-resolver.js.map +1 -1
- package/dist/exporters/types.d.ts +8 -1
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/exporters/xlsx-report-builder.d.ts.map +1 -1
- package/dist/exporters/xlsx-report-builder.js +5 -4
- package/dist/exporters/xlsx-report-builder.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +2 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +52 -1
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +20 -10
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +2 -11
- 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/imports.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/biometric-action.hbs +9 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +11 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/date-picker-unsupported.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/deep-link-action.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/lifecycle-missing-app-id.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/location-services-action.hbs +11 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +21 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/restart-app-action.hbs +16 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/set-connectivity-action.hbs +11 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +9 -5
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +10 -4
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/device-log-assertion.hbs +16 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disappears-within-assertion.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/matches-format-assertion.hbs +15 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +6 -4
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +4 -3
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/still-visible-after-assertion.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +5 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +7 -1
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +302 -29
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +2 -11
- 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/imports.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +17 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +6 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +18 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/matches-format-assertion.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +4 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/recipe-expect-text.hbs +1 -1
- package/dist/generators/test-generator/code-generator.d.ts +6 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +80 -7
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +60 -6
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +59 -5
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/expect-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/expect-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/expect-patterns.js +36 -2
- package/dist/generators/test-generator/patterns/expect-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +19 -12
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +58 -38
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +23 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/unsupported-step-code.d.ts +18 -0
- package/dist/generators/test-generator/patterns/unsupported-step-code.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/unsupported-step-code.js +23 -0
- package/dist/generators/test-generator/patterns/unsupported-step-code.js.map +1 -0
- package/dist/generators/test-generator/recipe-expander.js +9 -1
- package/dist/generators/test-generator/recipe-expander.js.map +1 -1
- package/dist/generators/test-generator/reset-tier.d.ts +68 -0
- package/dist/generators/test-generator/reset-tier.d.ts.map +1 -0
- package/dist/generators/test-generator/reset-tier.js +94 -0
- package/dist/generators/test-generator/reset-tier.js.map +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts +39 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +144 -5
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +2 -10
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +19 -2
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/format-pattern.d.ts +35 -0
- package/dist/generators/test-generator/utils/format-pattern.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/format-pattern.js +82 -0
- package/dist/generators/test-generator/utils/format-pattern.js.map +1 -0
- package/dist/generators/test-generator/utils/js-literal-scanner.d.ts +42 -0
- package/dist/generators/test-generator/utils/js-literal-scanner.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/js-literal-scanner.js +142 -0
- package/dist/generators/test-generator/utils/js-literal-scanner.js.map +1 -0
- package/dist/generators/test-generator/utils/mask-refs-for-matching.d.ts +18 -0
- package/dist/generators/test-generator/utils/mask-refs-for-matching.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/mask-refs-for-matching.js +25 -0
- package/dist/generators/test-generator/utils/mask-refs-for-matching.js.map +1 -0
- package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts +9 -1
- package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/runtime-data-transformer.js +124 -63
- package/dist/generators/test-generator/utils/runtime-data-transformer.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +24 -1
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/spec-syntax-check.d.ts +14 -0
- package/dist/generators/test-generator/utils/spec-syntax-check.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/spec-syntax-check.js +26 -0
- package/dist/generators/test-generator/utils/spec-syntax-check.js.map +1 -0
- package/dist/harness/annotation-overrides.d.ts +0 -12
- package/dist/harness/annotation-overrides.d.ts.map +1 -1
- package/dist/harness/annotation-overrides.js +9 -5
- package/dist/harness/annotation-overrides.js.map +1 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +7 -5
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability-plan.d.ts +2 -1
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +14 -4
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/capability.d.ts +11 -0
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js +8 -0
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +5 -0
- package/dist/harness/parse.d.ts +1 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +6 -1
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/run-manifest.d.ts +58 -0
- package/dist/harness/run-manifest.d.ts.map +1 -0
- package/dist/harness/run-manifest.js +264 -0
- package/dist/harness/run-manifest.js.map +1 -0
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +6 -2
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +5 -1
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts +1 -1
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +25 -4
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/lockstep-version.d.ts +22 -0
- package/dist/orchestrator/lockstep-version.d.ts.map +1 -0
- package/dist/orchestrator/lockstep-version.js +58 -0
- package/dist/orchestrator/lockstep-version.js.map +1 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.js +5 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
- package/dist/orchestrator/project-deps-sync.d.ts +39 -0
- package/dist/orchestrator/project-deps-sync.d.ts.map +1 -0
- package/dist/orchestrator/project-deps-sync.js +175 -0
- package/dist/orchestrator/project-deps-sync.js.map +1 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +5 -2
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +109 -0
- package/dist/orchestrator/templates/ai-src/config/claude.md +1 -1
- package/dist/orchestrator/templates/ai-src/config/copilot.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +49 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +120 -10
- package/dist/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +31 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +9 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +15 -0
- package/dist/orchestrator/templates/env.appium.example +21 -0
- package/dist/orchestrator/templates/specs-api.d.ts +4 -2
- package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-api.js +233 -10
- package/dist/orchestrator/templates/specs-api.js.map +1 -1
- package/dist/orchestrator/templates/specs-api.ts +214 -11
- package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-db.js +79 -6
- package/dist/orchestrator/templates/specs-db.js.map +1 -1
- package/dist/orchestrator/templates/specs-db.ts +70 -7
- package/dist/orchestrator/templates/specs-regex-escape.d.ts +13 -0
- package/dist/orchestrator/templates/specs-regex-escape.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-regex-escape.js +19 -0
- package/dist/orchestrator/templates/specs-regex-escape.js.map +1 -0
- package/dist/orchestrator/templates/specs-regex-escape.ts +16 -0
- package/dist/orchestrator/templates/specs-test-data.ts +53 -6
- package/dist/orchestrator/templates/wdio.conf.ts +126 -10
- package/dist/utils/selector-types.d.ts +1 -1
- package/dist/utils/selector-types.d.ts.map +1 -1
- package/dist/utils/selector-types.js +1 -0
- package/dist/utils/selector-types.js.map +1 -1
- package/package.json +8 -6
- package/src/capabilities/context-router.ts +13 -5
- package/src/cli/commands/capability.ts +4 -38
- package/src/cli/commands/changed.ts +104 -0
- package/src/cli/commands/delivery.ts +27 -2
- package/src/cli/commands/generate.ts +1 -1
- package/src/cli/commands/results-merge.ts +153 -0
- package/src/cli/commands/update.ts +72 -13
- package/src/cli/index.ts +4 -0
- package/src/exporters/api-catalog-loader.ts +10 -2
- package/src/exporters/api-testcase-formatter.ts +59 -3
- package/src/exporters/csv-exporter.ts +11 -2
- package/src/exporters/json-exporter.ts +7 -2
- package/src/exporters/matrix/build.ts +9 -0
- package/src/exporters/matrix/export.ts +1 -1
- package/src/exporters/matrix/render-csv.ts +5 -1
- package/src/exporters/matrix/render-xlsx.ts +4 -1
- package/src/exporters/matrix/types.ts +7 -0
- package/src/exporters/playwright-report-parser.ts +68 -9
- package/src/exporters/results-merge.ts +150 -0
- package/src/exporters/selector-key-resolver.ts +81 -1
- package/src/exporters/types.ts +8 -1
- package/src/exporters/xlsx-report-builder.ts +5 -4
- package/src/generators/gherkin-parser/index.ts +55 -1
- package/src/generators/test-generator/adapters/adapter-interface.ts +23 -1
- package/src/generators/test-generator/adapters/appium/appium-adapter.ts +2 -2
- package/src/generators/test-generator/adapters/appium/templates/imports.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/biometric-action.hbs +9 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +11 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/date-picker-unsupported.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/deep-link-action.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/lifecycle-missing-app-id.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/location-services-action.hbs +11 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +21 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/restart-app-action.hbs +16 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/set-connectivity-action.hbs +11 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +9 -5
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +10 -4
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/device-log-assertion.hbs +16 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disappears-within-assertion.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/matches-format-assertion.hbs +15 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +6 -4
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +4 -3
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/still-visible-after-assertion.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +5 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +7 -1
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +302 -29
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +17 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +6 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +18 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/matches-format-assertion.hbs +5 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +4 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/recipe-expect-text.hbs +1 -1
- package/src/generators/test-generator/code-generator.ts +86 -7
- package/src/generators/test-generator/diagnostics.ts +60 -6
- package/src/generators/test-generator/patterns/expect-patterns.ts +37 -2
- package/src/generators/test-generator/patterns/index.ts +58 -33
- package/src/generators/test-generator/patterns/types.ts +36 -0
- package/src/generators/test-generator/patterns/unsupported-step-code.ts +19 -0
- package/src/generators/test-generator/recipe-expander.ts +9 -1
- package/src/generators/test-generator/reset-tier.ts +93 -0
- package/src/generators/test-generator/step-mapper.ts +149 -6
- package/src/generators/test-generator/template-engine.ts +22 -3
- package/src/generators/test-generator/utils/format-pattern.ts +79 -0
- package/src/generators/test-generator/utils/js-literal-scanner.ts +132 -0
- package/src/generators/test-generator/utils/mask-refs-for-matching.ts +21 -0
- package/src/generators/test-generator/utils/runtime-data-transformer.ts +126 -76
- package/src/generators/test-generator/utils/selector-resolver.ts +26 -1
- package/src/generators/test-generator/utils/spec-syntax-check.ts +22 -0
- package/src/harness/annotation-overrides.ts +10 -5
- package/src/harness/audit.ts +7 -5
- package/src/harness/capability-plan.ts +13 -6
- package/src/harness/capability.ts +16 -0
- package/src/harness/catalog/drivers.yaml +5 -0
- package/src/harness/parse.ts +7 -1
- package/src/harness/run-manifest.ts +254 -0
- package/src/harness/script-check.ts +6 -2
- package/src/harness/sensors.ts +5 -1
- package/src/harness/viewpoint-ledger.ts +26 -5
- package/src/index.ts +1 -0
- package/src/orchestrator/lockstep-version.ts +46 -0
- package/src/orchestrator/mobile-runtime-scaffolder.ts +5 -1
- package/src/orchestrator/project-deps-sync.ts +175 -0
- package/src/orchestrator/project-initializer.ts +5 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +109 -0
- package/src/orchestrator/templates/ai-src/config/claude.md +1 -1
- package/src/orchestrator/templates/ai-src/config/copilot.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +49 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +120 -10
- package/src/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +31 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +9 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +15 -0
- package/src/orchestrator/templates/env.appium.example +21 -0
- package/src/orchestrator/templates/specs-api.ts +214 -11
- package/src/orchestrator/templates/specs-db.ts +70 -7
- package/src/orchestrator/templates/specs-regex-escape.ts +16 -0
- package/src/orchestrator/templates/specs-test-data.ts +53 -6
- package/src/orchestrator/templates/wdio.conf.ts +126 -10
- package/src/utils/selector-types.ts +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format placeholders → a regex source, for `see [T] <type> matches "<format>"`.
|
|
3
|
+
*
|
|
4
|
+
* QA specs state a label's contract as a SHAPE, not a value: "Format: yyyy/mm/dd ~ yyyy/mm/dd",
|
|
5
|
+
* "Format: 残りX個". Neither existing text assertion can express that — an exact-value assert
|
|
6
|
+
* pins data that changes daily (red while the app is right), and `contains` proves almost
|
|
7
|
+
* nothing ("abc ~ def" passes). This translator keeps the spec's own vocabulary in the Gherkin
|
|
8
|
+
* so the step reads like the requirement, and no regex reaches a file QA has to review.
|
|
9
|
+
*
|
|
10
|
+
* Tokens (everything else is a literal, regex-escaped):
|
|
11
|
+
* yyyy → a 4-digit year mm | dd | hh | ss → a zero-padded 2-digit part
|
|
12
|
+
* X → an integer, thousand separators allowed (the app renders prices as "2,800円")
|
|
13
|
+
* * → any run of characters, for a label that carries surrounding content
|
|
14
|
+
*
|
|
15
|
+
* A token only counts when it is not glued to an ASCII letter, so a literal English prefix
|
|
16
|
+
* survives ("Comment" keeps its "mm"; "yyyy/mm/dd" does not). Japanese/symbol literals are
|
|
17
|
+
* unaffected either way.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Translate a format string into an ANCHORED regex source (no delimiters, no flags).
|
|
21
|
+
* Anchored because the spec means the whole label: "Format: X件" is not satisfied by "12件です".
|
|
22
|
+
* Use the `*` token where the label legitimately carries more (a fused mobile node).
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatToRegexSource(format: string): string;
|
|
25
|
+
/** True when the string carries at least one token — i.e. it is a FORMAT, not a plain value. */
|
|
26
|
+
export declare function hasFormatToken(format: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The same pattern as a JS **regex literal**, ready to paste into generated code.
|
|
29
|
+
*
|
|
30
|
+
* Not a string: `new RegExp('^\d{4}$')` is a trap — in a single-quoted JS literal `\d` collapses
|
|
31
|
+
* to `d`, so the compiled test would silently match the letter instead of a digit. A regex literal
|
|
32
|
+
* carries the backslashes verbatim; only `/` needs escaping, and date formats are full of them.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatToRegexLiteral(format: string): string;
|
|
35
|
+
//# sourceMappingURL=format-pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-pattern.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/format-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgBH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwB1D;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Format placeholders → a regex source, for `see [T] <type> matches "<format>"`.
|
|
4
|
+
*
|
|
5
|
+
* QA specs state a label's contract as a SHAPE, not a value: "Format: yyyy/mm/dd ~ yyyy/mm/dd",
|
|
6
|
+
* "Format: 残りX個". Neither existing text assertion can express that — an exact-value assert
|
|
7
|
+
* pins data that changes daily (red while the app is right), and `contains` proves almost
|
|
8
|
+
* nothing ("abc ~ def" passes). This translator keeps the spec's own vocabulary in the Gherkin
|
|
9
|
+
* so the step reads like the requirement, and no regex reaches a file QA has to review.
|
|
10
|
+
*
|
|
11
|
+
* Tokens (everything else is a literal, regex-escaped):
|
|
12
|
+
* yyyy → a 4-digit year mm | dd | hh | ss → a zero-padded 2-digit part
|
|
13
|
+
* X → an integer, thousand separators allowed (the app renders prices as "2,800円")
|
|
14
|
+
* * → any run of characters, for a label that carries surrounding content
|
|
15
|
+
*
|
|
16
|
+
* A token only counts when it is not glued to an ASCII letter, so a literal English prefix
|
|
17
|
+
* survives ("Comment" keeps its "mm"; "yyyy/mm/dd" does not). Japanese/symbol literals are
|
|
18
|
+
* unaffected either way.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.formatToRegexSource = formatToRegexSource;
|
|
22
|
+
exports.hasFormatToken = hasFormatToken;
|
|
23
|
+
exports.formatToRegexLiteral = formatToRegexLiteral;
|
|
24
|
+
/** Longest-first: `yyyy` must win over a bare `yy`-style prefix of another token. */
|
|
25
|
+
const TOKENS = [
|
|
26
|
+
['yyyy', '\\d{4}'],
|
|
27
|
+
['mm', '\\d{2}'],
|
|
28
|
+
['dd', '\\d{2}'],
|
|
29
|
+
['hh', '\\d{2}'],
|
|
30
|
+
['ss', '\\d{2}'],
|
|
31
|
+
['X', '\\d+(?:,\\d{3})*'],
|
|
32
|
+
['*', '[\\s\\S]*'],
|
|
33
|
+
];
|
|
34
|
+
const isAsciiLetter = (ch) => !!ch && /[A-Za-z]/.test(ch);
|
|
35
|
+
const escapeLiteral = (ch) => ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
36
|
+
/**
|
|
37
|
+
* Translate a format string into an ANCHORED regex source (no delimiters, no flags).
|
|
38
|
+
* Anchored because the spec means the whole label: "Format: X件" is not satisfied by "12件です".
|
|
39
|
+
* Use the `*` token where the label legitimately carries more (a fused mobile node).
|
|
40
|
+
*/
|
|
41
|
+
function formatToRegexSource(format) {
|
|
42
|
+
let out = '';
|
|
43
|
+
let i = 0;
|
|
44
|
+
while (i < format.length) {
|
|
45
|
+
const prev = i > 0 ? format[i - 1] : undefined;
|
|
46
|
+
let matched = false;
|
|
47
|
+
for (const [token, regex] of TOKENS) {
|
|
48
|
+
if (!format.startsWith(token, i))
|
|
49
|
+
continue;
|
|
50
|
+
// `*` is punctuation — the letter-adjacency rule protects word-like tokens only.
|
|
51
|
+
if (token !== '*') {
|
|
52
|
+
const next = format[i + token.length];
|
|
53
|
+
if (isAsciiLetter(prev) || isAsciiLetter(next))
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
out += regex;
|
|
57
|
+
i += token.length;
|
|
58
|
+
matched = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (!matched) {
|
|
62
|
+
out += escapeLiteral(format[i]);
|
|
63
|
+
i += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return `^${out}$`;
|
|
67
|
+
}
|
|
68
|
+
/** True when the string carries at least one token — i.e. it is a FORMAT, not a plain value. */
|
|
69
|
+
function hasFormatToken(format) {
|
|
70
|
+
return formatToRegexSource(format) !== `^${format.split('').map(escapeLiteral).join('')}$`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The same pattern as a JS **regex literal**, ready to paste into generated code.
|
|
74
|
+
*
|
|
75
|
+
* Not a string: `new RegExp('^\d{4}$')` is a trap — in a single-quoted JS literal `\d` collapses
|
|
76
|
+
* to `d`, so the compiled test would silently match the letter instead of a digit. A regex literal
|
|
77
|
+
* carries the backslashes verbatim; only `/` needs escaping, and date formats are full of them.
|
|
78
|
+
*/
|
|
79
|
+
function formatToRegexLiteral(format) {
|
|
80
|
+
return `/${formatToRegexSource(format).replace(/\//g, '\\/')}/`;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=format-pattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-pattern.js","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/format-pattern.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;AAqBH,kDAwBC;AAGD,wCAEC;AASD,oDAEC;AA3DD,qFAAqF;AACrF,MAAM,MAAM,GAA4B;IACtC,CAAC,MAAM,EAAE,QAAQ,CAAC;IAClB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACzB,CAAC,GAAG,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,EAAsB,EAAW,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvF,MAAM,aAAa,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAExF;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,MAAc;IAChD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;gBAAE,SAAS;YAC3C,iFAAiF;YACjF,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;gBAClB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;gBACtC,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC;oBAAE,SAAS;YAC3D,CAAC;YACD,GAAG,IAAI,KAAK,CAAC;YACb,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC;YAClB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM;QACR,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;IACH,CAAC;IACD,OAAO,IAAI,GAAG,GAAG,CAAC;AACpB,CAAC;AAED,gGAAgG;AAChG,SAAgB,cAAc,CAAC,MAAc;IAC3C,OAAO,mBAAmB,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;AAC7F,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAAC,MAAc;IACjD,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal left-to-right lexer that segments generated JS into the four contexts a
|
|
3
|
+
* post-processor must tell apart: string/template literals, regex literals,
|
|
4
|
+
* comments, and everything else.
|
|
5
|
+
*
|
|
6
|
+
* WHY this exists instead of a regex pass per context: independent global regexes
|
|
7
|
+
* have no idea where a literal actually STARTS. `String.replace` retries at every
|
|
8
|
+
* offset, so a pattern that wants "quote … marker … quote" happily pairs the
|
|
9
|
+
* CLOSING quote of one literal with the OPENING quote of the next and rewrites the
|
|
10
|
+
* code in between. Field report (3.2.23, every version 2.0.0+):
|
|
11
|
+
*
|
|
12
|
+
* await page.getByText('Tìm thấy').filter({ hasText: /^§marker§$/ }).waitFor({ state: 'visible' });
|
|
13
|
+
*
|
|
14
|
+
* The marker sits in a REGEX literal, but the string-literal pass matched from the
|
|
15
|
+
* `'` after `thấy` to the `'` before `visible` and emitted
|
|
16
|
+
* `getByText('Tìm thấy`)` … `state: `visible' })` — the whole .spec.ts stopped
|
|
17
|
+
* parsing. Scanning once, in order, makes that class of fault unrepresentable: a
|
|
18
|
+
* quote can only ever open a literal when the scanner is not already inside one.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately NOT a full JS parser — it only needs to find literal boundaries:
|
|
21
|
+
* - `${…}` inside a template literal is body text, not nested code (matches what
|
|
22
|
+
* the previous passes did, and generated templates never nest a literal there).
|
|
23
|
+
* - A `'`/`"` literal must close on its own line; an unterminated one is treated
|
|
24
|
+
* as ordinary code so a stray apostrophe (prose in a comment) cannot desync the
|
|
25
|
+
* rest of the file. Backticks may span lines, but an unterminated one to EOF is
|
|
26
|
+
* likewise demoted to code.
|
|
27
|
+
* - A `/` opens a regex only in expression position and only when it closes on the
|
|
28
|
+
* same line. Both guards keep a division (or a stray slash) from swallowing the
|
|
29
|
+
* literals after it.
|
|
30
|
+
*/
|
|
31
|
+
export type SegmentKind = 'code' | 'string' | 'regex' | 'comment';
|
|
32
|
+
export interface Segment {
|
|
33
|
+
kind: SegmentKind;
|
|
34
|
+
/** Verbatim source slice, INCLUDING delimiters (quotes, slashes, `//`). */
|
|
35
|
+
text: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Split `code` into ordered segments. Concatenating every `text` reproduces the
|
|
39
|
+
* input byte for byte — callers rewrite only the segments they care about.
|
|
40
|
+
*/
|
|
41
|
+
export declare function scanSegments(code: string): Segment[];
|
|
42
|
+
//# sourceMappingURL=js-literal-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-literal-scanner.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/js-literal-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAElE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;CACd;AAyCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAiDpD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal left-to-right lexer that segments generated JS into the four contexts a
|
|
4
|
+
* post-processor must tell apart: string/template literals, regex literals,
|
|
5
|
+
* comments, and everything else.
|
|
6
|
+
*
|
|
7
|
+
* WHY this exists instead of a regex pass per context: independent global regexes
|
|
8
|
+
* have no idea where a literal actually STARTS. `String.replace` retries at every
|
|
9
|
+
* offset, so a pattern that wants "quote … marker … quote" happily pairs the
|
|
10
|
+
* CLOSING quote of one literal with the OPENING quote of the next and rewrites the
|
|
11
|
+
* code in between. Field report (3.2.23, every version 2.0.0+):
|
|
12
|
+
*
|
|
13
|
+
* await page.getByText('Tìm thấy').filter({ hasText: /^§marker§$/ }).waitFor({ state: 'visible' });
|
|
14
|
+
*
|
|
15
|
+
* The marker sits in a REGEX literal, but the string-literal pass matched from the
|
|
16
|
+
* `'` after `thấy` to the `'` before `visible` and emitted
|
|
17
|
+
* `getByText('Tìm thấy`)` … `state: `visible' })` — the whole .spec.ts stopped
|
|
18
|
+
* parsing. Scanning once, in order, makes that class of fault unrepresentable: a
|
|
19
|
+
* quote can only ever open a literal when the scanner is not already inside one.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately NOT a full JS parser — it only needs to find literal boundaries:
|
|
22
|
+
* - `${…}` inside a template literal is body text, not nested code (matches what
|
|
23
|
+
* the previous passes did, and generated templates never nest a literal there).
|
|
24
|
+
* - A `'`/`"` literal must close on its own line; an unterminated one is treated
|
|
25
|
+
* as ordinary code so a stray apostrophe (prose in a comment) cannot desync the
|
|
26
|
+
* rest of the file. Backticks may span lines, but an unterminated one to EOF is
|
|
27
|
+
* likewise demoted to code.
|
|
28
|
+
* - A `/` opens a regex only in expression position and only when it closes on the
|
|
29
|
+
* same line. Both guards keep a division (or a stray slash) from swallowing the
|
|
30
|
+
* literals after it.
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.scanSegments = scanSegments;
|
|
34
|
+
/** Chars that, immediately before a `/`, make it division or a member access — never a regex. */
|
|
35
|
+
const NOT_REGEX_PREFIX = /[\w$)\]]/;
|
|
36
|
+
const REGEX_FLAGS = /[dgimsuvy]/;
|
|
37
|
+
/** Index just past the closing quote, or -1 when the literal never closes (see module note). */
|
|
38
|
+
function scanQuoted(code, start, quote) {
|
|
39
|
+
const spansLines = quote === '`';
|
|
40
|
+
for (let i = start + 1; i < code.length; i++) {
|
|
41
|
+
const c = code[i];
|
|
42
|
+
if (c === '\\') {
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (c === quote)
|
|
47
|
+
return i + 1;
|
|
48
|
+
if (c === '\n' && !spansLines)
|
|
49
|
+
return -1;
|
|
50
|
+
}
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
/** Index just past the regex's flags, or -1 when it is not a single-line regex literal. */
|
|
54
|
+
function scanRegex(code, start) {
|
|
55
|
+
// Expression-position guard: skip back over spaces/tabs to the last significant char.
|
|
56
|
+
let p = start - 1;
|
|
57
|
+
while (p >= 0 && (code[p] === ' ' || code[p] === '\t'))
|
|
58
|
+
p--;
|
|
59
|
+
if (p >= 0 && NOT_REGEX_PREFIX.test(code[p]))
|
|
60
|
+
return -1;
|
|
61
|
+
let inClass = false;
|
|
62
|
+
for (let i = start + 1; i < code.length; i++) {
|
|
63
|
+
const c = code[i];
|
|
64
|
+
if (c === '\n')
|
|
65
|
+
return -1;
|
|
66
|
+
if (c === '\\') {
|
|
67
|
+
i++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (inClass) {
|
|
71
|
+
if (c === ']')
|
|
72
|
+
inClass = false;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (c === '[') {
|
|
76
|
+
inClass = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (c === '/') {
|
|
80
|
+
let j = i + 1;
|
|
81
|
+
while (j < code.length && REGEX_FLAGS.test(code[j]))
|
|
82
|
+
j++;
|
|
83
|
+
return j;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return -1;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Split `code` into ordered segments. Concatenating every `text` reproduces the
|
|
90
|
+
* input byte for byte — callers rewrite only the segments they care about.
|
|
91
|
+
*/
|
|
92
|
+
function scanSegments(code) {
|
|
93
|
+
const out = [];
|
|
94
|
+
let codeStart = 0;
|
|
95
|
+
let i = 0;
|
|
96
|
+
const flushCode = (end) => {
|
|
97
|
+
if (end > codeStart)
|
|
98
|
+
out.push({ kind: 'code', text: code.slice(codeStart, end) });
|
|
99
|
+
};
|
|
100
|
+
const take = (kind, end) => {
|
|
101
|
+
flushCode(i);
|
|
102
|
+
out.push({ kind, text: code.slice(i, end) });
|
|
103
|
+
i = codeStart = end;
|
|
104
|
+
};
|
|
105
|
+
while (i < code.length) {
|
|
106
|
+
const c = code[i];
|
|
107
|
+
// Comments first: a `//` or `/*` reached in code position can never be a regex,
|
|
108
|
+
// and consuming them keeps an apostrophe in prose from opening a literal.
|
|
109
|
+
if (c === '/' && code[i + 1] === '/') {
|
|
110
|
+
const nl = code.indexOf('\n', i);
|
|
111
|
+
take('comment', nl === -1 ? code.length : nl);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (c === '/' && code[i + 1] === '*') {
|
|
115
|
+
const close = code.indexOf('*/', i + 2);
|
|
116
|
+
take('comment', close === -1 ? code.length : close + 2);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (c === '\'' || c === '"' || c === '`') {
|
|
120
|
+
const end = scanQuoted(code, i, c);
|
|
121
|
+
if (end !== -1) {
|
|
122
|
+
take('string', end);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
i++; // unterminated → ordinary code
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (c === '/') {
|
|
129
|
+
const end = scanRegex(code, i);
|
|
130
|
+
if (end !== -1) {
|
|
131
|
+
take('regex', end);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
i++; // division or stray slash → ordinary code
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
i++;
|
|
138
|
+
}
|
|
139
|
+
flushCode(code.length);
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=js-literal-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-literal-scanner.js","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/js-literal-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mask the CONTENT of selector refs `[...]` and data placeholders `{{...}}` before verb matching,
|
|
3
|
+
* keeping the delimiters. A step's verb/keywords come from the SENTENCE — never from a selector or
|
|
4
|
+
* variable NAME — but matchers scan `step.text` with substring checks (e.g. `includes('click')`),
|
|
5
|
+
* so a data ref like `{{double_click_guard_account.email}}` would spuriously satisfy the click
|
|
6
|
+
* matcher and pre-empt fill. The content is replaced with an inert placeholder (`~` — carries no
|
|
7
|
+
* verb/keyword/digit substring) rather than emptied, so structural matchers that require a NON-EMPTY
|
|
8
|
+
* ref survive too — e.g. `expect {{a}} is {{b}}` matches `\{\{[^}]+\}\}`, and `in [X] table` /
|
|
9
|
+
* `/\bto\s+\[/` keep their delimiters. The resolver/generator always receives the ORIGINAL step, so
|
|
10
|
+
* value extraction and codegen are unaffected.
|
|
11
|
+
*
|
|
12
|
+
* Lives in `utils/` rather than in `patterns/index.ts` so the Gherkin parser can reuse it for
|
|
13
|
+
* state-clause extraction (SG-E021) without importing the pattern registry — which would pull the
|
|
14
|
+
* whole capability graph into the parser and close a require cycle. `patterns/index.ts` re-exports
|
|
15
|
+
* it, so its historical import path still resolves.
|
|
16
|
+
*/
|
|
17
|
+
export declare function maskRefsForMatching(text: string): string;
|
|
18
|
+
//# sourceMappingURL=mask-refs-for-matching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-refs-for-matching.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/mask-refs-for-matching.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIxD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maskRefsForMatching = maskRefsForMatching;
|
|
4
|
+
/**
|
|
5
|
+
* Mask the CONTENT of selector refs `[...]` and data placeholders `{{...}}` before verb matching,
|
|
6
|
+
* keeping the delimiters. A step's verb/keywords come from the SENTENCE — never from a selector or
|
|
7
|
+
* variable NAME — but matchers scan `step.text` with substring checks (e.g. `includes('click')`),
|
|
8
|
+
* so a data ref like `{{double_click_guard_account.email}}` would spuriously satisfy the click
|
|
9
|
+
* matcher and pre-empt fill. The content is replaced with an inert placeholder (`~` — carries no
|
|
10
|
+
* verb/keyword/digit substring) rather than emptied, so structural matchers that require a NON-EMPTY
|
|
11
|
+
* ref survive too — e.g. `expect {{a}} is {{b}}` matches `\{\{[^}]+\}\}`, and `in [X] table` /
|
|
12
|
+
* `/\bto\s+\[/` keep their delimiters. The resolver/generator always receives the ORIGINAL step, so
|
|
13
|
+
* value extraction and codegen are unaffected.
|
|
14
|
+
*
|
|
15
|
+
* Lives in `utils/` rather than in `patterns/index.ts` so the Gherkin parser can reuse it for
|
|
16
|
+
* state-clause extraction (SG-E021) without importing the pattern registry — which would pull the
|
|
17
|
+
* whole capability graph into the parser and close a require cycle. `patterns/index.ts` re-exports
|
|
18
|
+
* it, so its historical import path still resolves.
|
|
19
|
+
*/
|
|
20
|
+
function maskRefsForMatching(text) {
|
|
21
|
+
return text
|
|
22
|
+
.replace(/\{\{[^}]*\}\}/g, '{{~}}')
|
|
23
|
+
.replace(/\[[^\]]*\]/g, '[~]');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=mask-refs-for-matching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-refs-for-matching.js","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/mask-refs-for-matching.ts"],"names":[],"mappings":";;AAgBA,kDAIC;AApBD;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI;SACR,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC;SAClC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Replace __SUNGEN_TD$…$__ markers with testData.get() calls in generated code.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* The code is scanned ONCE into literal/regex/comment/code segments and each
|
|
5
|
+
* segment is rewritten for the context it is actually in. This replaced four
|
|
6
|
+
* independent global-regex passes whose ordering was the defect: the string pass
|
|
7
|
+
* ran first and, finding no marker inside a genuine literal, re-matched from that
|
|
8
|
+
* literal's CLOSING quote — pairing it with the OPENING quote of the next literal
|
|
9
|
+
* and rewriting the regex literal between them into a broken backtick template
|
|
10
|
+
* (`getByText('Tìm thấy`)` … `state: `visible' })`, a .spec.ts that would not
|
|
11
|
+
* parse at all). See js-literal-scanner.ts for the full field report.
|
|
4
12
|
*/
|
|
5
13
|
export declare function transformToRuntimeData(code: string, accessor?: string): string;
|
|
6
14
|
export declare function hasRuntimeDataMarkers(code: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-data-transformer.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/runtime-data-transformer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runtime-data-transformer.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/utils/runtime-data-transformer.ts"],"names":[],"mappings":"AA0CA;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAmB,GAAG,MAAM,CAG1F;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.transformToRuntimeData = transformToRuntimeData;
|
|
4
4
|
exports.hasRuntimeDataMarkers = hasRuntimeDataMarkers;
|
|
5
|
+
const js_literal_scanner_1 = require("./js-literal-scanner");
|
|
5
6
|
/**
|
|
6
7
|
* Wire format of a runtime test-data marker: `__SUNGEN_TD$<key>$__`.
|
|
7
8
|
*
|
|
@@ -18,12 +19,12 @@ exports.hasRuntimeDataMarkers = hasRuntimeDataMarkers;
|
|
|
18
19
|
* swallowed one `_` of the `___` run and decoding produced `get('code_')` plus
|
|
19
20
|
* a detached `ACCESS_TOKEN`. `$` is also identifier-safe — the whole marker
|
|
20
21
|
* still parses as ONE bare JS token, so it survives the bare-identifier
|
|
21
|
-
* (numeric) positions
|
|
22
|
+
* (numeric) positions the code context handles.
|
|
22
23
|
*
|
|
23
|
-
* Single source of truth: every
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* Single source of truth: every context below is built from these constants.
|
|
25
|
+
* Invariant: every marker producer goes through DataResolver.encodeMarker and
|
|
26
|
+
* every consumer through this module — a drifting hand-copied char class is what
|
|
27
|
+
* let the suffix bug hide.
|
|
27
28
|
*/
|
|
28
29
|
const MARKER_KEY = '[A-Za-z0-9_-]+';
|
|
29
30
|
// Templates that embed a value into a regex literal run it through the escapeRegex
|
|
@@ -31,82 +32,142 @@ const MARKER_KEY = '[A-Za-z0-9_-]+';
|
|
|
31
32
|
// arrive here spelled `__SUNGEN_TD\$key\$__`. `\\\\?` accepts that optional backslash
|
|
32
33
|
// before each delimiter; the replacement consumes it, so no escape leaks to output.
|
|
33
34
|
const DELIM = '\\\\?\\$';
|
|
34
|
-
/** Marker with no capture group — for "contains a marker"
|
|
35
|
+
/** Marker with no capture group — for "contains a marker" probes. */
|
|
35
36
|
const MARKER = `__SUNGEN_TD${DELIM}${MARKER_KEY}${DELIM}__`;
|
|
36
37
|
/** Marker capturing the encoded key. */
|
|
37
38
|
const MARKER_CAP = `__SUNGEN_TD${DELIM}(${MARKER_KEY})${DELIM}__`;
|
|
38
39
|
const MARKER_PATTERN = new RegExp(MARKER);
|
|
39
|
-
|
|
40
|
-
const RE_COMMENT_LINE = new RegExp(`^(\\s*)\\/\\/(.*)${MARKER_CAP}(.*)$`, 'gm');
|
|
41
|
-
// Pass 1: a quoted string literal containing at least one marker. Covers ', " and
|
|
42
|
-
// backtick containers; `\\.` lets an ESCAPED quote (`'it\'s {{x}}'`) sit inside the
|
|
43
|
-
// body instead of terminating the literal early. The single-char branch excludes `\`
|
|
44
|
-
// so every backslash is consumed by exactly ONE branch — an ambiguous `(?!\1).`
|
|
45
|
-
// alternative enumerates 2^n segmentations when a marker-less escape-heavy literal
|
|
46
|
-
// (escapeQuotes output: multi-line Flutter content-desc) fails to match, hanging
|
|
47
|
-
// the generate run. `\n` exclusion just makes the dot's no-newline behavior explicit.
|
|
48
|
-
const RE_STRING_LITERAL = new RegExp('([\'"`])((?:\\\\.|(?!\\1)[^\\\\\\n])*?' + MARKER + '(?:\\\\.|(?!\\1)[^\\\\\\n])*)\\1', 'g');
|
|
49
|
-
// Pass 2: a single-line regex literal containing at least one marker.
|
|
50
|
-
const RE_REGEX_LITERAL = new RegExp(`\\/((?:[^/\\\\\\n]|\\\\.)*?${MARKER}(?:[^/\\\\\\n]|\\\\.)*)\\/([gimsuy]*)`, 'g');
|
|
51
|
-
// Whole-literal probe: the body is EXACTLY one marker (bare accessor call, no template).
|
|
40
|
+
/** Whole-literal probe: the body is EXACTLY one marker (bare accessor call, no template). */
|
|
52
41
|
const RE_WHOLE_MARKER = new RegExp(`^${MARKER_CAP}$`);
|
|
53
|
-
|
|
42
|
+
/** Every marker occurrence, capturing the key. */
|
|
54
43
|
const RE_MARKER_G = new RegExp(MARKER_CAP, 'g');
|
|
55
44
|
/**
|
|
56
45
|
* Replace __SUNGEN_TD$…$__ markers with testData.get() calls in generated code.
|
|
57
|
-
*
|
|
46
|
+
*
|
|
47
|
+
* The code is scanned ONCE into literal/regex/comment/code segments and each
|
|
48
|
+
* segment is rewritten for the context it is actually in. This replaced four
|
|
49
|
+
* independent global-regex passes whose ordering was the defect: the string pass
|
|
50
|
+
* ran first and, finding no marker inside a genuine literal, re-matched from that
|
|
51
|
+
* literal's CLOSING quote — pairing it with the OPENING quote of the next literal
|
|
52
|
+
* and rewriting the regex literal between them into a broken backtick template
|
|
53
|
+
* (`getByText('Tìm thấy`)` … `state: `visible' })`, a .spec.ts that would not
|
|
54
|
+
* parse at all). See js-literal-scanner.ts for the full field report.
|
|
58
55
|
*/
|
|
59
56
|
function transformToRuntimeData(code, accessor = 'testData') {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// — e.g. a URL `https://careerforum.aki-inc.jp/` in a selector name — is NOT mistaken
|
|
64
|
-
// for a comment. Before this anchor, such a URL preceding a marker on the same line
|
|
65
|
-
// made Pass 0 strip the marker to its bare key, so Pass 1 never wrapped it in
|
|
66
|
-
// testData.get() (silent: `toHaveAttribute('href', 'official_site_url')`). Generated
|
|
67
|
-
// comments are always full-line, so anchoring loses nothing.
|
|
68
|
-
code = code.replace(RE_COMMENT_LINE, (_, indent, before, enc, after) => `${indent}//${before}${decodeKey(enc)}${after}`);
|
|
69
|
-
// Pass 1: String literal context — a literal may carry SEVERAL markers
|
|
70
|
-
// ('/{{code}}/car-search?{{prefill}}' compiles to two markers in ONE literal).
|
|
71
|
-
// Match the whole quoted literal that contains at least one marker, then
|
|
72
|
-
// interpolate EVERY marker inside it. The old shape (one marker + lazy
|
|
73
|
-
// prefix/suffix) swallowed trailing markers into its suffix verbatim; they
|
|
74
|
-
// escaped the quote context (the literal had become a backtick template) and
|
|
75
|
-
// Pass 3's numeric catch-all emitted them as literal
|
|
76
|
-
// `Number(testData.get(...))` TEXT inside the template — a compiling spec
|
|
77
|
-
// navigating to a garbage URL.
|
|
78
|
-
// 'marker' → testData.get('key')
|
|
79
|
-
// 'a__m1__b__m2__c' → `a${testData.get('k1')}b${testData.get('k2')}c`
|
|
80
|
-
code = code.replace(RE_STRING_LITERAL, (_, _quote, body) => {
|
|
81
|
-
const whole = RE_WHOLE_MARKER.exec(body);
|
|
82
|
-
if (whole)
|
|
83
|
-
return `${accessor}.get('${decodeKey(whole[1])}')`;
|
|
84
|
-
return `\`${interpolateMarkers(body, accessor)}\``;
|
|
85
|
-
});
|
|
86
|
-
// Pass 2: Regex literal context — /…$marker$…/ → new RegExp(`…`). Same
|
|
87
|
-
// several-markers-per-literal rule as Pass 1 (/{{min}}-{{max}} items/).
|
|
88
|
-
// Only matches within a single line (no newlines in the body).
|
|
89
|
-
code = code.replace(RE_REGEX_LITERAL, (_, body, flags) => {
|
|
90
|
-
const flagStr = flags ? `, '${flags}'` : '';
|
|
91
|
-
const whole = RE_WHOLE_MARKER.exec(body);
|
|
92
|
-
if (whole)
|
|
93
|
-
return `new RegExp(${accessor}.get('${decodeKey(whole[1])}')${flagStr})`;
|
|
94
|
-
return `new RegExp(\`${interpolateMarkers(body, accessor)}\`${flagStr})`;
|
|
95
|
-
});
|
|
96
|
-
// Pass 3: Bare-identifier context — any marker left after the string/regex passes
|
|
97
|
-
// sits as a bare token in a numeric position (e.g. `toHaveCount(__marker__)` from the
|
|
98
|
-
// table/list count templates). testData.get() returns a string, so coerce with Number().
|
|
99
|
-
code = code.replace(RE_MARKER_G, (_, enc) => `Number(${accessor}.get('${decodeKey(enc)}'))`);
|
|
100
|
-
return code;
|
|
57
|
+
if (!MARKER_PATTERN.test(code))
|
|
58
|
+
return code;
|
|
59
|
+
return (0, js_literal_scanner_1.scanSegments)(code).map((seg) => transformSegment(seg, accessor)).join('');
|
|
101
60
|
}
|
|
102
61
|
function hasRuntimeDataMarkers(code) {
|
|
103
62
|
return MARKER_PATTERN.test(code);
|
|
104
63
|
}
|
|
64
|
+
function transformSegment(seg, accessor) {
|
|
65
|
+
if (!MARKER_PATTERN.test(seg.text))
|
|
66
|
+
return seg.text;
|
|
67
|
+
switch (seg.kind) {
|
|
68
|
+
// Decode to the plain key: a marker in a comment documents the ref, it is not code.
|
|
69
|
+
case 'comment':
|
|
70
|
+
return seg.text.replace(RE_MARKER_G, (_, enc) => decodeKey(enc));
|
|
71
|
+
case 'string':
|
|
72
|
+
return transformStringLiteral(seg.text, accessor);
|
|
73
|
+
case 'regex':
|
|
74
|
+
return transformRegexLiteral(seg.text, accessor);
|
|
75
|
+
// Bare-token position (e.g. `toHaveCount(__marker__)` from the table/list count
|
|
76
|
+
// templates). testData.get() returns a string, so coerce with Number().
|
|
77
|
+
case 'code':
|
|
78
|
+
return seg.text.replace(RE_MARKER_G, (_, enc) => `Number(${accessor}.get('${decodeKey(enc)}'))`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 'marker' → testData.get('key')
|
|
83
|
+
* 'a__m1__b__m2__c' → `a${testData.get('k1')}b${testData.get('k2')}c`
|
|
84
|
+
*
|
|
85
|
+
* A literal may carry SEVERAL markers ('/{{code}}/car-search?{{prefill}}' compiles
|
|
86
|
+
* to two markers in ONE literal), so every marker inside it is interpolated.
|
|
87
|
+
*/
|
|
88
|
+
function transformStringLiteral(text, accessor) {
|
|
89
|
+
const quote = text[0];
|
|
90
|
+
const body = text.slice(1, -1);
|
|
91
|
+
const whole = RE_WHOLE_MARKER.exec(body);
|
|
92
|
+
if (whole)
|
|
93
|
+
return `${accessor}.get('${decodeKey(whole[1])}')`;
|
|
94
|
+
return `\`${interpolateMarkers(escapeForTemplate(body, quote === '`'), accessor)}\``;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* /…$marker$…/flags → new RegExp(`…`, 'flags'). Same several-markers rule as above.
|
|
98
|
+
*
|
|
99
|
+
* Two regex-only twists on top of the string path:
|
|
100
|
+
* - The runtime VALUE is literal text (test-data holds data, not patterns), so every
|
|
101
|
+
* accessor is wrapped in the runtime escapeRegExp helper — a value containing
|
|
102
|
+
* `( [ $ . * +` must not throw a SyntaxError or silently widen the match. The helper
|
|
103
|
+
* lives in the synced specs/regex-escape.ts; the generator imports it when the
|
|
104
|
+
* transformed body references it. Templates that intend PATTERN semantics (attribute
|
|
105
|
+
* `pattern:`) emit a string container instead of a regex literal, so they skip this.
|
|
106
|
+
* - Marker-less SEGMENTS get their backslashes doubled for the template context: regex
|
|
107
|
+
* source text like a hand-written `\s*` (or escapeRegex output `\)`) means one thing
|
|
108
|
+
* in a /…/ literal but loses its backslash when the template cooks one escape level —
|
|
109
|
+
* the constructed RegExp would read `s*` / a bare `)`. Segments are escaped AFTER the
|
|
110
|
+
* markers are cut out, so an escapeRegex-spelled marker delimiter (`\$`) is never
|
|
111
|
+
* double-escaped into something the marker regex can no longer see.
|
|
112
|
+
*/
|
|
113
|
+
function transformRegexLiteral(text, accessor) {
|
|
114
|
+
// Flags never contain `/`, so the last slash always closes the pattern.
|
|
115
|
+
const close = text.lastIndexOf('/');
|
|
116
|
+
const body = text.slice(1, close);
|
|
117
|
+
const flags = text.slice(close + 1);
|
|
118
|
+
const flagStr = flags ? `, '${flags}'` : '';
|
|
119
|
+
const whole = RE_WHOLE_MARKER.exec(body);
|
|
120
|
+
if (whole)
|
|
121
|
+
return `new RegExp(escapeRegExp(${accessor}.get('${decodeKey(whole[1])}'))${flagStr})`;
|
|
122
|
+
return `new RegExp(\`${interpolateMarkersInRegexBody(body, accessor)}\`${flagStr})`;
|
|
123
|
+
}
|
|
105
124
|
function decodeKey(encoded) {
|
|
106
125
|
return encoded.replace(/_D_/g, '.');
|
|
107
126
|
}
|
|
108
|
-
/** Replace every marker in a
|
|
127
|
+
/** Replace every marker in a body with a `${accessor.get('key')}` interpolation. */
|
|
109
128
|
function interpolateMarkers(body, accessor) {
|
|
110
129
|
return body.replace(RE_MARKER_G, (_, enc) => `\${${accessor}.get('${decodeKey(enc)}')}`);
|
|
111
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Regex-body variant: markers are cut out FIRST, each accessor is wrapped in the
|
|
133
|
+
* runtime escapeRegExp helper, and only the marker-less segments between them are
|
|
134
|
+
* re-escaped for the template context (backslashes doubled, backtick/`${` neutralized).
|
|
135
|
+
*/
|
|
136
|
+
function interpolateMarkersInRegexBody(body, accessor) {
|
|
137
|
+
let out = '';
|
|
138
|
+
let last = 0;
|
|
139
|
+
for (const m of body.matchAll(RE_MARKER_G)) {
|
|
140
|
+
out += escapeRegexSegmentForTemplate(body.slice(last, m.index)) +
|
|
141
|
+
`\${escapeRegExp(${accessor}.get('${decodeKey(m[1])}'))}`;
|
|
142
|
+
last = m.index + m[0].length;
|
|
143
|
+
}
|
|
144
|
+
return out + escapeRegexSegmentForTemplate(body.slice(last));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Regex pattern text moving into a template literal: the template layer cooks one
|
|
148
|
+
* level of escapes BEFORE RegExp ever sees the string, so `\)` arrives as a bare `)`
|
|
149
|
+
* (SyntaxError: unmatched paren) and `\.` as a match-anything dot. Double the
|
|
150
|
+
* backslashes and neutralize the template's own metacharacters so the constructed
|
|
151
|
+
* RegExp source equals the original pattern text.
|
|
152
|
+
*/
|
|
153
|
+
const escapeRegexSegmentForTemplate = (segment) => segment.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
154
|
+
/**
|
|
155
|
+
* Both rewrites move a body into a backtick template, where a raw backtick or `${`
|
|
156
|
+
* would be read as syntax rather than text — a `'…'` selector value or a regex
|
|
157
|
+
* pattern is allowed to contain either (escapeQuotes/escapeRegex escape neither).
|
|
158
|
+
* Escape before interpolating so the `${…}` this module inserts is the only live one.
|
|
159
|
+
* An already-backtick container needs no pass: its body was valid template text.
|
|
160
|
+
*
|
|
161
|
+
* PRECONDITION: the body must already have its backslashes DOUBLED — which every caller
|
|
162
|
+
* satisfies, because every dynamic value reaches a literal through escapeQuotes or
|
|
163
|
+
* escapeRegex (template-engine.ts) and both double `\` before anything else. An unpaired
|
|
164
|
+
* backslash immediately before a backtick would make the pass below emit `\\\``, i.e. an
|
|
165
|
+
* escaped backslash followed by a LIVE backtick, ending the template early. If you
|
|
166
|
+
* hand-write a backslash into a .hbs template, keep it paired.
|
|
167
|
+
*/
|
|
168
|
+
function escapeForTemplate(body, alreadyTemplate) {
|
|
169
|
+
if (alreadyTemplate)
|
|
170
|
+
return body;
|
|
171
|
+
return body.replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
172
|
+
}
|
|
112
173
|
//# sourceMappingURL=runtime-data-transformer.js.map
|