@sun-asterisk/sungen 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/bin/sungen.js +12 -0
- package/dist/cli/commands/auto-tag-command.d.ts +8 -0
- package/dist/cli/commands/auto-tag-command.d.ts.map +1 -0
- package/dist/cli/commands/auto-tag-command.js +104 -0
- package/dist/cli/commands/auto-tag-command.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +196 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/ai-providers.yaml +56 -0
- package/dist/config/config-loader.d.ts +51 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +216 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/config-schema.d.ts +121 -0
- package/dist/config/config-schema.d.ts.map +1 -0
- package/dist/config/config-schema.js +7 -0
- package/dist/config/config-schema.js.map +1 -0
- package/dist/config/default.config.yaml +101 -0
- package/dist/config/framework.config.yaml +52 -0
- package/dist/config/routes.yaml +31 -0
- package/dist/core/selector-base/annotation-handler.d.ts +45 -0
- package/dist/core/selector-base/annotation-handler.d.ts.map +1 -0
- package/dist/core/selector-base/annotation-handler.js +102 -0
- package/dist/core/selector-base/annotation-handler.js.map +1 -0
- package/dist/core/selector-base/base-generator.d.ts +49 -0
- package/dist/core/selector-base/base-generator.d.ts.map +1 -0
- package/dist/core/selector-base/base-generator.js +214 -0
- package/dist/core/selector-base/base-generator.js.map +1 -0
- package/dist/core/selector-base/gherkin-parser.d.ts +24 -0
- package/dist/core/selector-base/gherkin-parser.d.ts.map +1 -0
- package/dist/core/selector-base/gherkin-parser.js +42 -0
- package/dist/core/selector-base/gherkin-parser.js.map +1 -0
- package/dist/core/selector-mapper/priority-mapper.d.ts +74 -0
- package/dist/core/selector-mapper/priority-mapper.d.ts.map +1 -0
- package/dist/core/selector-mapper/priority-mapper.js +477 -0
- package/dist/core/selector-mapper/priority-mapper.js.map +1 -0
- package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts +91 -0
- package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts.map +1 -0
- package/dist/core/ui-scanner/heuristics/base-heuristic.js +175 -0
- package/dist/core/ui-scanner/heuristics/base-heuristic.js.map +1 -0
- package/dist/core/ui-scanner/react-scanner.d.ts +32 -0
- package/dist/core/ui-scanner/react-scanner.d.ts.map +1 -0
- package/dist/core/ui-scanner/react-scanner.js +163 -0
- package/dist/core/ui-scanner/react-scanner.js.map +1 -0
- package/dist/core/ui-scanner/scanner-interface.d.ts +94 -0
- package/dist/core/ui-scanner/scanner-interface.d.ts.map +1 -0
- package/dist/core/ui-scanner/scanner-interface.js +33 -0
- package/dist/core/ui-scanner/scanner-interface.js.map +1 -0
- package/dist/core/ui-scanner/strict-scanner.d.ts +81 -0
- package/dist/core/ui-scanner/strict-scanner.d.ts.map +1 -0
- package/dist/core/ui-scanner/strict-scanner.js +511 -0
- package/dist/core/ui-scanner/strict-scanner.js.map +1 -0
- package/dist/executor/playwright/playwright-generator.d.ts +33 -0
- package/dist/executor/playwright/playwright-generator.d.ts.map +1 -0
- package/dist/executor/playwright/playwright-generator.js +136 -0
- package/dist/executor/playwright/playwright-generator.js.map +1 -0
- package/dist/executor/test-generator.d.ts +63 -0
- package/dist/executor/test-generator.d.ts.map +1 -0
- package/dist/executor/test-generator.js +30 -0
- package/dist/executor/test-generator.js.map +1 -0
- package/dist/external/ai-provider.d.ts +60 -0
- package/dist/external/ai-provider.d.ts.map +1 -0
- package/dist/external/ai-provider.js +30 -0
- package/dist/external/ai-provider.js.map +1 -0
- package/dist/external/anthropic-provider.d.ts +29 -0
- package/dist/external/anthropic-provider.d.ts.map +1 -0
- package/dist/external/anthropic-provider.js +85 -0
- package/dist/external/anthropic-provider.js.map +1 -0
- package/dist/generators/cache/cache-manager.d.ts +66 -0
- package/dist/generators/cache/cache-manager.d.ts.map +1 -0
- package/dist/generators/cache/cache-manager.js +286 -0
- package/dist/generators/cache/cache-manager.js.map +1 -0
- package/dist/generators/cli.d.ts +7 -0
- package/dist/generators/cli.d.ts.map +1 -0
- package/dist/generators/cli.js +570 -0
- package/dist/generators/cli.js.map +1 -0
- package/dist/generators/dsl-writer/index.d.ts +33 -0
- package/dist/generators/dsl-writer/index.d.ts.map +1 -0
- package/dist/generators/dsl-writer/index.js +226 -0
- package/dist/generators/dsl-writer/index.js.map +1 -0
- package/dist/generators/gherkin-parser/index.d.ts +47 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -0
- package/dist/generators/gherkin-parser/index.js +149 -0
- package/dist/generators/gherkin-parser/index.js.map +1 -0
- package/dist/generators/gherkin-parser/selector-extractor.d.ts +37 -0
- package/dist/generators/gherkin-parser/selector-extractor.d.ts.map +1 -0
- package/dist/generators/gherkin-parser/selector-extractor.js +108 -0
- package/dist/generators/gherkin-parser/selector-extractor.js.map +1 -0
- package/dist/generators/scaffold-generator/index.d.ts +111 -0
- package/dist/generators/scaffold-generator/index.d.ts.map +1 -0
- package/dist/generators/scaffold-generator/index.js +408 -0
- package/dist/generators/scaffold-generator/index.js.map +1 -0
- package/dist/generators/selector-mapper/ai-mapper.d.ts +56 -0
- package/dist/generators/selector-mapper/ai-mapper.d.ts.map +1 -0
- package/dist/generators/selector-mapper/ai-mapper.js +457 -0
- package/dist/generators/selector-mapper/ai-mapper.js.map +1 -0
- package/dist/generators/selector-mapper/hybrid-mapper.d.ts +67 -0
- package/dist/generators/selector-mapper/hybrid-mapper.d.ts.map +1 -0
- package/dist/generators/selector-mapper/hybrid-mapper.js +349 -0
- package/dist/generators/selector-mapper/hybrid-mapper.js.map +1 -0
- package/dist/generators/selector-mapper/index.d.ts +8 -0
- package/dist/generators/selector-mapper/index.d.ts.map +1 -0
- package/dist/generators/selector-mapper/index.js +12 -0
- package/dist/generators/selector-mapper/index.js.map +1 -0
- package/dist/generators/selector-mapper/intelligent-mapper.d.ts +125 -0
- package/dist/generators/selector-mapper/intelligent-mapper.d.ts.map +1 -0
- package/dist/generators/selector-mapper/intelligent-mapper.js +391 -0
- package/dist/generators/selector-mapper/intelligent-mapper.js.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +49 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-interface.js +7 -0
- package/dist/generators/test-generator/adapters/adapter-interface.js.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-registry.d.ts +29 -0
- package/dist/generators/test-generator/adapters/adapter-registry.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-registry.js +50 -0
- package/dist/generators/test-generator/adapters/adapter-registry.js.map +1 -0
- package/dist/generators/test-generator/adapters/index.d.ts +4 -0
- package/dist/generators/test-generator/adapters/index.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/index.js +13 -0
- package/dist/generators/test-generator/adapters/index.js.map +1 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +23 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js +38 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/before-each.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/clear-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/double-click-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/hover-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/press-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/active-state-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/ai-response-assertion-selector.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/ai-response-assertion-simple.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/application-running.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/check-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/checkbox.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-auth.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-browser-state.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-database.hbs +4 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/clear.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/click-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/click.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/contain-text-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/contains-text-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/count-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/count-greater-than.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/count-less-than.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/disabled-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/displayed-containing-text.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/displayed-with-text.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/double-click-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/empty-assertion-advanced.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/empty-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/enabled-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/error-message-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/fill-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/fill.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/focused-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/generic-message-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-attribute.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-class.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-count.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-image-src.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-link.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-placeholder.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/has-value.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/have-text-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/hover-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/html5-validation-check.hbs +4 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/is-checked.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/is-editable.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/is-focused.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/is-hidden.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/is-unchecked.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/locator.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/login.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/message-assertion-body.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/message-assertion-selector.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/message-count-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/route-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-timeout.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/not-checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/not-visible-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/not-visible.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/notification-assertion.hbs +4 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/press-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/press-enter.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/redirect-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/route-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/screen-navigation.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/scroll-bottom-assertion.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/select-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/select.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/application-running.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-auth.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-browser-state.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-database.hbs +4 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/user-login-todo.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/text-matches-pattern.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/uncheck-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/user-login-todo.hbs +6 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/visibility-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/visible-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/wait-for-element.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/wait-timeout.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/wait.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +19 -0
- package/dist/generators/test-generator/ai-step-mapper.d.ts +27 -0
- package/dist/generators/test-generator/ai-step-mapper.d.ts.map +1 -0
- package/dist/generators/test-generator/ai-step-mapper.js +204 -0
- package/dist/generators/test-generator/ai-step-mapper.js.map +1 -0
- package/dist/generators/test-generator/code-generator.d.ts +52 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -0
- package/dist/generators/test-generator/code-generator.js +191 -0
- package/dist/generators/test-generator/code-generator.js.map +1 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.js +173 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/form-patterns.d.ts +8 -0
- package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/form-patterns.js +110 -0
- package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/index.d.ts +45 -0
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/index.js +106 -0
- package/dist/generators/test-generator/patterns/index.js.map +1 -0
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/interaction-patterns.js +100 -0
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +8 -0
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/navigation-patterns.js +92 -0
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/setup-patterns.js +84 -0
- package/dist/generators/test-generator/patterns/setup-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/types.d.ts +38 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/types.js +3 -0
- package/dist/generators/test-generator/patterns/types.js.map +1 -0
- package/dist/generators/test-generator/step-mapper-old.d.ts +180 -0
- package/dist/generators/test-generator/step-mapper-old.d.ts.map +1 -0
- package/dist/generators/test-generator/step-mapper-old.js +752 -0
- package/dist/generators/test-generator/step-mapper-old.js.map +1 -0
- package/dist/generators/test-generator/step-mapper-refactored.d.ts +47 -0
- package/dist/generators/test-generator/step-mapper-refactored.d.ts.map +1 -0
- package/dist/generators/test-generator/step-mapper-refactored.js +182 -0
- package/dist/generators/test-generator/step-mapper-refactored.js.map +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts +66 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -0
- package/dist/generators/test-generator/step-mapper.js +248 -0
- package/dist/generators/test-generator/step-mapper.js.map +1 -0
- package/dist/generators/test-generator/template-engine.d.ts +33 -0
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -0
- package/dist/generators/test-generator/template-engine.js +129 -0
- package/dist/generators/test-generator/template-engine.js.map +1 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts +39 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/data-resolver.js +162 -0
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -0
- package/dist/generators/test-generator/utils/path-inference.d.ts +49 -0
- package/dist/generators/test-generator/utils/path-inference.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/path-inference.js +286 -0
- package/dist/generators/test-generator/utils/path-inference.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +93 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-resolver.js +408 -0
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -0
- package/dist/generators/types.d.ts +118 -0
- package/dist/generators/types.d.ts.map +1 -0
- package/dist/generators/types.js +48 -0
- package/dist/generators/types.js.map +1 -0
- package/dist/generators/ui-model-builder/deep-scanner.d.ts +121 -0
- package/dist/generators/ui-model-builder/deep-scanner.d.ts.map +1 -0
- package/dist/generators/ui-model-builder/deep-scanner.js +1113 -0
- package/dist/generators/ui-model-builder/deep-scanner.js.map +1 -0
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts +110 -0
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts.map +1 -0
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.js +608 -0
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.js.map +1 -0
- package/dist/generators/ui-model-builder/react-scanner.d.ts +107 -0
- package/dist/generators/ui-model-builder/react-scanner.d.ts.map +1 -0
- package/dist/generators/ui-model-builder/react-scanner.js +797 -0
- package/dist/generators/ui-model-builder/react-scanner.js.map +1 -0
- package/dist/input/cli-adapter.d.ts +63 -0
- package/dist/input/cli-adapter.d.ts.map +1 -0
- package/dist/input/cli-adapter.js +173 -0
- package/dist/input/cli-adapter.js.map +1 -0
- package/dist/input/config-adapter.d.ts +25 -0
- package/dist/input/config-adapter.d.ts.map +1 -0
- package/dist/input/config-adapter.js +70 -0
- package/dist/input/config-adapter.js.map +1 -0
- package/dist/input/input-adapter.d.ts +28 -0
- package/dist/input/input-adapter.d.ts.map +1 -0
- package/dist/input/input-adapter.js +17 -0
- package/dist/input/input-adapter.js.map +1 -0
- package/dist/input/vscode-adapter.d.ts +62 -0
- package/dist/input/vscode-adapter.d.ts.map +1 -0
- package/dist/input/vscode-adapter.js +64 -0
- package/dist/input/vscode-adapter.js.map +1 -0
- package/dist/orchestrator/cache-manager.d.ts +37 -0
- package/dist/orchestrator/cache-manager.d.ts.map +1 -0
- package/dist/orchestrator/cache-manager.js +148 -0
- package/dist/orchestrator/cache-manager.js.map +1 -0
- package/dist/orchestrator/pipeline.d.ts +73 -0
- package/dist/orchestrator/pipeline.d.ts.map +1 -0
- package/dist/orchestrator/pipeline.js +607 -0
- package/dist/orchestrator/pipeline.js.map +1 -0
- package/dist/orchestrator/project-initializer.d.ts +51 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -0
- package/dist/orchestrator/project-initializer.js +326 -0
- package/dist/orchestrator/project-initializer.js.map +1 -0
- package/dist/orchestrator/reporter.d.ts +15 -0
- package/dist/orchestrator/reporter.d.ts.map +1 -0
- package/dist/orchestrator/reporter.js +30 -0
- package/dist/orchestrator/reporter.js.map +1 -0
- package/dist/orchestrator/screen-manager.d.ts +47 -0
- package/dist/orchestrator/screen-manager.d.ts.map +1 -0
- package/dist/orchestrator/screen-manager.js +271 -0
- package/dist/orchestrator/screen-manager.js.map +1 -0
- package/dist/tools/auto-tagger.d.ts +107 -0
- package/dist/tools/auto-tagger.d.ts.map +1 -0
- package/dist/tools/auto-tagger.js +502 -0
- package/dist/tools/auto-tagger.js.map +1 -0
- package/package.json +73 -0
- package/src/cli/commands/auto-tag-command.ts +80 -0
- package/src/cli/index.ts +205 -0
- package/src/config/ai-providers.yaml +56 -0
- package/src/config/config-loader.ts +248 -0
- package/src/config/config-schema.ts +148 -0
- package/src/config/default.config.yaml +101 -0
- package/src/config/framework.config.yaml +52 -0
- package/src/config/routes.yaml +31 -0
- package/src/core/selector-base/annotation-handler.ts +127 -0
- package/src/core/selector-base/base-generator.ts +234 -0
- package/src/core/selector-base/gherkin-parser.ts +57 -0
- package/src/core/selector-mapper/priority-mapper.ts +607 -0
- package/src/core/ui-scanner/heuristics/base-heuristic.ts +216 -0
- package/src/core/ui-scanner/react-scanner.ts +156 -0
- package/src/core/ui-scanner/scanner-interface.ts +133 -0
- package/src/core/ui-scanner/strict-scanner.ts +629 -0
- package/src/executor/playwright/playwright-generator.ts +125 -0
- package/src/executor/test-generator.ts +90 -0
- package/src/external/ai-provider.ts +90 -0
- package/src/external/anthropic-provider.ts +114 -0
- package/src/generators/README.md +410 -0
- package/src/generators/cache/cache-manager.ts +322 -0
- package/src/generators/cli.ts +640 -0
- package/src/generators/dsl-writer/index.ts +253 -0
- package/src/generators/gherkin-parser/index.ts +155 -0
- package/src/generators/gherkin-parser/selector-extractor.ts +142 -0
- package/src/generators/scaffold-generator/index.ts +524 -0
- package/src/generators/selector-mapper/ai-mapper.ts +528 -0
- package/src/generators/selector-mapper/hybrid-mapper.ts +427 -0
- package/src/generators/selector-mapper/index.ts +10 -0
- package/src/generators/selector-mapper/intelligent-mapper.ts +530 -0
- package/src/generators/test-generator/adapters/adapter-interface.ts +49 -0
- package/src/generators/test-generator/adapters/adapter-registry.ts +56 -0
- package/src/generators/test-generator/adapters/index.ts +9 -0
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +40 -0
- package/src/generators/test-generator/adapters/playwright/templates/before-each.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +5 -0
- package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/clear-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/double-click-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/hover-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/press-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/route-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-timeout.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/setup/application-running.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-auth.hbs +6 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-browser-state.hbs +6 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-database.hbs +4 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/setup/user-login-todo.hbs +6 -0
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +19 -0
- package/src/generators/test-generator/ai-step-mapper.ts +224 -0
- package/src/generators/test-generator/code-generator.ts +235 -0
- package/src/generators/test-generator/patterns/assertion-patterns.ts +183 -0
- package/src/generators/test-generator/patterns/form-patterns.ts +124 -0
- package/src/generators/test-generator/patterns/index.ts +97 -0
- package/src/generators/test-generator/patterns/interaction-patterns.ts +119 -0
- package/src/generators/test-generator/patterns/navigation-patterns.ts +110 -0
- package/src/generators/test-generator/patterns/setup-patterns.ts +94 -0
- package/src/generators/test-generator/patterns/types.ts +41 -0
- package/src/generators/test-generator/step-mapper.ts +254 -0
- package/src/generators/test-generator/template-engine.ts +160 -0
- package/src/generators/test-generator/utils/data-resolver.ts +147 -0
- package/src/generators/test-generator/utils/path-inference.ts +344 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +480 -0
- package/src/generators/types.ts +226 -0
- package/src/generators/ui-model-builder/deep-scanner.ts +1244 -0
- package/src/generators/ui-model-builder/enhanced-deep-scanner.ts +731 -0
- package/src/generators/ui-model-builder/react-scanner.ts +959 -0
- package/src/input/cli-adapter.ts +185 -0
- package/src/input/config-adapter.ts +71 -0
- package/src/input/input-adapter.ts +32 -0
- package/src/input/vscode-adapter.ts +90 -0
- package/src/orchestrator/cache-manager.ts +138 -0
- package/src/orchestrator/pipeline.ts +713 -0
- package/src/orchestrator/project-initializer.ts +315 -0
- package/src/orchestrator/reporter.ts +36 -0
- package/src/orchestrator/screen-manager.ts +268 -0
- package/src/tools/auto-tagger.ts +572 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import yaml from 'yaml';
|
|
4
|
+
|
|
5
|
+
// New structured selector format
|
|
6
|
+
interface SelectorEntry {
|
|
7
|
+
selector?: string; // CSS selector (optional, empty string means use type/value)
|
|
8
|
+
type?: 'placeholder' | 'role' | 'testid' | 'label' | 'text'; // Locator strategy
|
|
9
|
+
value?: string; // Natural language value for the locator (e.g., 'button', 'link' for role)
|
|
10
|
+
name?: string; // Accessible name for role-based selectors (e.g., 'Login', 'Submit')
|
|
11
|
+
nth?: number; // Element index (0 = no index, 1+ = append .nth())
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// New selector file structure: flat key-value pairs
|
|
15
|
+
type SelectorFile = Record<string, SelectorEntry>;
|
|
16
|
+
|
|
17
|
+
// Legacy format for backward compatibility
|
|
18
|
+
interface LegacySelectorElement {
|
|
19
|
+
selector: string;
|
|
20
|
+
type?: 'selector' | 'data' | 'testid' | 'placeholder' | 'label' | 'text' | 'id' | 'button';
|
|
21
|
+
value?: string;
|
|
22
|
+
nth?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface LegacySelectorFile {
|
|
26
|
+
screen: string;
|
|
27
|
+
elements: Record<string, LegacySelectorElement>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ResolvedSelector {
|
|
31
|
+
strategy: 'testid' | 'id' | 'placeholder' | 'label' | 'text' | 'css' | 'role';
|
|
32
|
+
value: string;
|
|
33
|
+
role?: string; // For role-based selectors (e.g., 'button', 'link')
|
|
34
|
+
name?: string; // For accessible name in role selectors
|
|
35
|
+
selector?: string; // CSS selector for fallback
|
|
36
|
+
nth?: number; // Index for .nth() if multiple matches exist
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* SelectorResolver - Resolves selector references to direct Playwright locator code
|
|
41
|
+
* Supports natural language references with auto-generated keys
|
|
42
|
+
*/
|
|
43
|
+
export class SelectorResolver {
|
|
44
|
+
private selectorCache = new Map<string, SelectorFile | LegacySelectorFile>();
|
|
45
|
+
private selectorsDir: string;
|
|
46
|
+
private featureName?: string;
|
|
47
|
+
private screenName?: string;
|
|
48
|
+
|
|
49
|
+
constructor(selectorsDir?: string, screenName?: string) {
|
|
50
|
+
this.selectorsDir = selectorsDir || path.join(process.cwd(), 'qa', 'selectors', 'screens');
|
|
51
|
+
this.screenName = screenName;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Set screen context for base selector file lookup
|
|
56
|
+
*/
|
|
57
|
+
setScreenContext(screenName: string): void {
|
|
58
|
+
this.screenName = screenName;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Set feature context for automatic file inference
|
|
63
|
+
*/
|
|
64
|
+
setFeatureContext(featureName: string): void {
|
|
65
|
+
this.featureName = featureName;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generate selector key from natural language label
|
|
70
|
+
* "Email Address" → "email.address"
|
|
71
|
+
* "Submit Button" → "submit.button"
|
|
72
|
+
* "User's Profile" → "users.profile"
|
|
73
|
+
*/
|
|
74
|
+
static generateKey(label: string): string {
|
|
75
|
+
return label
|
|
76
|
+
.toLowerCase()
|
|
77
|
+
.replace(/['\u2019]s/g, 's') // Apostrophe s to just s ("User's" → "users")
|
|
78
|
+
.replace(/['\u2019]/g, '') // Remove remaining apostrophes
|
|
79
|
+
.replace(/[^a-z0-9\s]/g, '') // Remove special chars except spaces
|
|
80
|
+
.trim()
|
|
81
|
+
.replace(/\s+/g, '.'); // Spaces to dots
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve selector reference to Playwright locator code
|
|
86
|
+
* Supports:
|
|
87
|
+
* - Natural language: "Email Address", "Password" (with feature context)
|
|
88
|
+
* - Legacy format: "login:email_field" or "login.email_field" (with screen prefix)
|
|
89
|
+
*/
|
|
90
|
+
resolveSelector(selectorRef: string, featureName?: string): ResolvedSelector {
|
|
91
|
+
const contextName = featureName || this.featureName;
|
|
92
|
+
|
|
93
|
+
// Check if it's legacy format (contains : or has dot with screen prefix)
|
|
94
|
+
// Legacy: "login:email_field" or "login.email_field"
|
|
95
|
+
// Natural: "Email Address", "Password", "Submit Button"
|
|
96
|
+
const hasColonSeparator = selectorRef.includes(':');
|
|
97
|
+
const hasDotSeparator = selectorRef.includes('.');
|
|
98
|
+
|
|
99
|
+
// If it has colon, it's definitely legacy
|
|
100
|
+
if (hasColonSeparator) {
|
|
101
|
+
return this.resolveLegacyFormat(selectorRef);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If it has dots and looks like a technical identifier (lowercase, no spaces), it's legacy
|
|
105
|
+
if (hasDotSeparator && /^[a-z0-9\-\_\.]+$/.test(selectorRef)) {
|
|
106
|
+
return this.resolveLegacyFormat(selectorRef);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Otherwise, it's natural language with feature context
|
|
110
|
+
if (!contextName) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Natural language selector "${selectorRef}" requires feature context. ` +
|
|
113
|
+
`Use setFeatureContext() or pass featureName parameter.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return this.resolveNaturalLanguage(selectorRef, contextName);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Resolve natural language selector
|
|
122
|
+
*/
|
|
123
|
+
private resolveNaturalLanguage(label: string, featureName: string): ResolvedSelector {
|
|
124
|
+
const key = SelectorResolver.generateKey(label);
|
|
125
|
+
const selectorFile = this.loadNewSelectorFile(featureName);
|
|
126
|
+
|
|
127
|
+
const entry = selectorFile[key];
|
|
128
|
+
if (!entry) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Selector "${label}" (key: ${key}) not found in ${featureName}.yaml. ` +
|
|
131
|
+
`Available selectors: ${Object.keys(selectorFile).join(', ')}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.resolveFromEntry(entry, label);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Resolve legacy format selector
|
|
140
|
+
* Now uses feature context instead of requiring screen prefix in selector
|
|
141
|
+
*/
|
|
142
|
+
private resolveLegacyFormat(selectorRef: string): ResolvedSelector {
|
|
143
|
+
// Extract screen and element
|
|
144
|
+
const cleaned = selectorRef.replace(/[\[\]]/g, '');
|
|
145
|
+
|
|
146
|
+
// Check if it has screen prefix (legacy format: "login:email_field" or "login.email_field")
|
|
147
|
+
if (cleaned.includes(':') || cleaned.includes('.')) {
|
|
148
|
+
const parts = cleaned.split(/[.:]/);
|
|
149
|
+
|
|
150
|
+
if (parts.length < 2) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Invalid legacy selector reference: ${selectorRef}. ` +
|
|
153
|
+
`Expected format with screen prefix: [screen:element] or [screen.element]`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const screenId = parts[0];
|
|
158
|
+
const elementId = parts.slice(1).join('_');
|
|
159
|
+
|
|
160
|
+
// Load selector metadata from legacy file
|
|
161
|
+
const element = this.loadLegacySelectorElement(screenId, elementId);
|
|
162
|
+
|
|
163
|
+
// Priority chain: testid → id → placeholder → label → text → css
|
|
164
|
+
return this.resolveByPriority(element);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Single word without separator - treat as natural language with feature context
|
|
168
|
+
if (!this.featureName) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Selector "${selectorRef}" requires feature context. ` +
|
|
171
|
+
`Use setFeatureContext() or pass featureName parameter.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return this.resolveNaturalLanguage(selectorRef, this.featureName);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Resolve from new structured entry format
|
|
180
|
+
*/
|
|
181
|
+
private resolveFromEntry(entry: SelectorEntry, originalLabel: string): ResolvedSelector {
|
|
182
|
+
// Default values
|
|
183
|
+
const selector = entry.selector || '';
|
|
184
|
+
const type = entry.type || 'placeholder';
|
|
185
|
+
const value = entry.value || originalLabel;
|
|
186
|
+
const name = entry.name || originalLabel; // Use name field if provided, otherwise use label
|
|
187
|
+
const nth = entry.nth || 0;
|
|
188
|
+
|
|
189
|
+
// If custom selector is provided, use it
|
|
190
|
+
if (selector && selector.trim()) {
|
|
191
|
+
return {
|
|
192
|
+
strategy: 'css',
|
|
193
|
+
value: selector,
|
|
194
|
+
selector: selector,
|
|
195
|
+
nth,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Use type-based strategy
|
|
200
|
+
switch (type) {
|
|
201
|
+
case 'placeholder':
|
|
202
|
+
return {
|
|
203
|
+
strategy: 'placeholder',
|
|
204
|
+
value,
|
|
205
|
+
nth,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
case 'role':
|
|
209
|
+
return {
|
|
210
|
+
strategy: 'role',
|
|
211
|
+
role: value,
|
|
212
|
+
name: name, // Use the name field for accessible name
|
|
213
|
+
value,
|
|
214
|
+
nth,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
case 'testid':
|
|
218
|
+
return {
|
|
219
|
+
strategy: 'testid',
|
|
220
|
+
value,
|
|
221
|
+
nth,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
case 'label':
|
|
225
|
+
return {
|
|
226
|
+
strategy: 'label',
|
|
227
|
+
value,
|
|
228
|
+
nth,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
case 'text':
|
|
232
|
+
return {
|
|
233
|
+
strategy: 'text',
|
|
234
|
+
value,
|
|
235
|
+
nth,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
default:
|
|
239
|
+
// Fallback to placeholder
|
|
240
|
+
return {
|
|
241
|
+
strategy: 'placeholder',
|
|
242
|
+
value,
|
|
243
|
+
nth,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Resolve selector by priority chain (legacy)
|
|
250
|
+
* Returns metadata only - no framework-specific code
|
|
251
|
+
*/
|
|
252
|
+
private resolveByPriority(element: LegacySelectorElement): ResolvedSelector {
|
|
253
|
+
// Extract data-testid from selector if present
|
|
254
|
+
const testIdMatch = element.selector.match(/\[data-testid=["']([^"']+)["']\]/);
|
|
255
|
+
if (testIdMatch) {
|
|
256
|
+
return {
|
|
257
|
+
strategy: 'testid',
|
|
258
|
+
value: testIdMatch[1],
|
|
259
|
+
nth: element.nth,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check type-based strategies
|
|
264
|
+
if (element.type === 'button' && element.value) {
|
|
265
|
+
return {
|
|
266
|
+
strategy: 'role',
|
|
267
|
+
role: 'button',
|
|
268
|
+
name: element.value,
|
|
269
|
+
value: element.value,
|
|
270
|
+
nth: element.nth,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (element.type === 'placeholder' && element.value) {
|
|
275
|
+
return {
|
|
276
|
+
strategy: 'placeholder',
|
|
277
|
+
value: element.value,
|
|
278
|
+
nth: element.nth,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (element.type === 'label' && element.value) {
|
|
283
|
+
return {
|
|
284
|
+
strategy: 'label',
|
|
285
|
+
value: element.value,
|
|
286
|
+
nth: element.nth,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (element.type === 'text' && element.value) {
|
|
291
|
+
return {
|
|
292
|
+
strategy: 'text',
|
|
293
|
+
value: element.value,
|
|
294
|
+
nth: element.nth,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Extract #id from selector if present
|
|
299
|
+
const idMatch = element.selector.match(/#([\w-]+)/);
|
|
300
|
+
if (idMatch) {
|
|
301
|
+
return {
|
|
302
|
+
strategy: 'id',
|
|
303
|
+
value: idMatch[1],
|
|
304
|
+
selector: `#${idMatch[1]}`,
|
|
305
|
+
nth: element.nth,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Fallback to CSS selector
|
|
310
|
+
return {
|
|
311
|
+
strategy: 'css',
|
|
312
|
+
value: element.selector,
|
|
313
|
+
selector: element.selector,
|
|
314
|
+
nth: element.nth,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Load new format selector file (flat key-value structure)
|
|
320
|
+
* Supports base + feature-specific merging:
|
|
321
|
+
* 1. Load base (screen-level) selectors - OPTIONAL
|
|
322
|
+
* 2. Load feature-specific selectors - REQUIRED
|
|
323
|
+
* 3. Merge (feature overrides base)
|
|
324
|
+
*
|
|
325
|
+
* Priority: override > normal, checks both old and new folder structures
|
|
326
|
+
* Old: qa/selectors/screens/<feature>.yaml
|
|
327
|
+
* New: qa/screens/<feature>/selectors/<feature>.yaml
|
|
328
|
+
*/
|
|
329
|
+
private loadNewSelectorFile(featureName: string): SelectorFile {
|
|
330
|
+
const cacheKey = `new:${featureName}`;
|
|
331
|
+
if (this.selectorCache.has(cacheKey)) {
|
|
332
|
+
return this.selectorCache.get(cacheKey) as SelectorFile;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const selectors: SelectorFile = {};
|
|
336
|
+
|
|
337
|
+
// 1. Try load base file (screen-level shared selectors) - OPTIONAL
|
|
338
|
+
if (this.screenName) {
|
|
339
|
+
const basePath = this.findSelectorPath(this.screenName);
|
|
340
|
+
if (basePath && fs.existsSync(basePath)) {
|
|
341
|
+
const baseContent = fs.readFileSync(basePath, 'utf-8');
|
|
342
|
+
const baseSelectors = yaml.parse(baseContent) as SelectorFile;
|
|
343
|
+
Object.assign(selectors, baseSelectors);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 2. Load feature-specific selectors - REQUIRED
|
|
348
|
+
const featurePath = this.findSelectorPath(featureName);
|
|
349
|
+
if (!featurePath) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Selector file not found for feature "${featureName}". ` +
|
|
352
|
+
`Searched paths for ${featureName}.yaml`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const featureContent = fs.readFileSync(featurePath, 'utf-8');
|
|
357
|
+
const featureSelectors = yaml.parse(featureContent) as SelectorFile;
|
|
358
|
+
|
|
359
|
+
// 3. Merge: feature-specific overrides base
|
|
360
|
+
Object.assign(selectors, featureSelectors);
|
|
361
|
+
|
|
362
|
+
this.selectorCache.set(cacheKey, selectors);
|
|
363
|
+
return selectors;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Find selector file path (checks both old and new structures)
|
|
368
|
+
*/
|
|
369
|
+
private findSelectorPath(name: string): string | null {
|
|
370
|
+
// Build list of possible paths (priority order)
|
|
371
|
+
const possiblePaths: string[] = [];
|
|
372
|
+
|
|
373
|
+
// Old structure: qa/selectors/screens/<name>.yaml
|
|
374
|
+
const oldOverridePath = path.join(this.selectorsDir, `${name}-override.yaml`);
|
|
375
|
+
const oldNormalPath = path.join(this.selectorsDir, `${name}.yaml`);
|
|
376
|
+
possiblePaths.push(oldOverridePath, oldNormalPath);
|
|
377
|
+
|
|
378
|
+
// New structure: qa/screens/<screenName>/selectors/<name>.yaml
|
|
379
|
+
const qaDir = path.dirname(path.dirname(this.selectorsDir)); // go up from qa/selectors/screens to qa/
|
|
380
|
+
|
|
381
|
+
if (this.screenName) {
|
|
382
|
+
// Use screenName for directory, name for filename
|
|
383
|
+
const newOverridePath = path.join(qaDir, 'screens', this.screenName, 'selectors', `${name}-override.yaml`);
|
|
384
|
+
const newNormalPath = path.join(qaDir, 'screens', this.screenName, 'selectors', `${name}.yaml`);
|
|
385
|
+
possiblePaths.push(newOverridePath, newNormalPath);
|
|
386
|
+
} else {
|
|
387
|
+
// Fallback: assume name is both screen and feature
|
|
388
|
+
const newOverridePath = path.join(qaDir, 'screens', name, 'selectors', `${name}-override.yaml`);
|
|
389
|
+
const newNormalPath = path.join(qaDir, 'screens', name, 'selectors', `${name}.yaml`);
|
|
390
|
+
possiblePaths.push(newOverridePath, newNormalPath);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Find first existing file
|
|
394
|
+
for (const p of possiblePaths) {
|
|
395
|
+
if (fs.existsSync(p)) {
|
|
396
|
+
return p;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Load selector element from legacy YAML file
|
|
405
|
+
*/
|
|
406
|
+
private loadLegacySelectorElement(screenId: string, elementId: string): LegacySelectorElement {
|
|
407
|
+
const selectorFile = this.loadLegacySelectorFile(screenId);
|
|
408
|
+
|
|
409
|
+
const element = selectorFile.elements[elementId];
|
|
410
|
+
if (!element) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Element "${elementId}" not found in screen "${screenId}". ` +
|
|
413
|
+
`Available elements: ${Object.keys(selectorFile.elements).join(', ')}`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return element;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Load legacy selector file from disk (with caching)
|
|
422
|
+
* Checks both old and new folder structures
|
|
423
|
+
*/
|
|
424
|
+
private loadLegacySelectorFile(screenId: string): LegacySelectorFile {
|
|
425
|
+
const cacheKey = `legacy:${screenId}`;
|
|
426
|
+
if (this.selectorCache.has(cacheKey)) {
|
|
427
|
+
return this.selectorCache.get(cacheKey) as LegacySelectorFile;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Build list of possible paths (priority order)
|
|
431
|
+
const possiblePaths: string[] = [];
|
|
432
|
+
|
|
433
|
+
// Old structure: qa/selectors/screens/<screen>.yaml
|
|
434
|
+
const oldOverridePath = path.join(this.selectorsDir, `${screenId}-override.yaml`);
|
|
435
|
+
const oldNormalPath = path.join(this.selectorsDir, `${screenId}.yaml`);
|
|
436
|
+
possiblePaths.push(oldOverridePath, oldNormalPath);
|
|
437
|
+
|
|
438
|
+
// New structure: qa/screens/<screen>/selectors/<screen>.yaml
|
|
439
|
+
const qaDir = path.dirname(path.dirname(this.selectorsDir));
|
|
440
|
+
const newOverridePath = path.join(qaDir, 'screens', screenId, 'selectors', `${screenId}-override.yaml`);
|
|
441
|
+
const newNormalPath = path.join(qaDir, 'screens', screenId, 'selectors', `${screenId}.yaml`);
|
|
442
|
+
possiblePaths.push(newOverridePath, newNormalPath);
|
|
443
|
+
|
|
444
|
+
// Find first existing file
|
|
445
|
+
let filePath: string | undefined;
|
|
446
|
+
for (const p of possiblePaths) {
|
|
447
|
+
if (fs.existsSync(p)) {
|
|
448
|
+
filePath = p;
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!filePath) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
`Selector file not found for screen "${screenId}". ` +
|
|
456
|
+
`Searched: ${possiblePaths.join(', ')}`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
461
|
+
const selectorFile = yaml.parse(content) as LegacySelectorFile;
|
|
462
|
+
|
|
463
|
+
this.selectorCache.set(cacheKey, selectorFile);
|
|
464
|
+
return selectorFile;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Escape single quotes in strings
|
|
469
|
+
*/
|
|
470
|
+
private escapeQuotes(text: string): string {
|
|
471
|
+
return text.replace(/'/g, "\\'");
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Clear cache (useful for testing)
|
|
476
|
+
*/
|
|
477
|
+
clearCache(): void {
|
|
478
|
+
this.selectorCache.clear();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for Selector DSL Framework
|
|
3
|
+
* Framework-agnostic types for multi-platform support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// UI Model Types
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export interface UIElement {
|
|
11
|
+
key: string; // Unique key within screen (e1, e2, ...)
|
|
12
|
+
tag: string; // HTML/component tag (input, button, View, TextField, ...)
|
|
13
|
+
role?: string; // ARIA role or semantic role
|
|
14
|
+
name?: string; // name attribute
|
|
15
|
+
id?: string; // id/resource-id attribute
|
|
16
|
+
placeholder?: string; // placeholder text
|
|
17
|
+
label?: string; // Associated label text
|
|
18
|
+
text?: string; // Text content
|
|
19
|
+
ariaLabel?: string; // aria-label attribute
|
|
20
|
+
testId?: string; // data-testid / test ID
|
|
21
|
+
source: string; // Source file path
|
|
22
|
+
framework?: string; // Framework-specific type (TextField, EditText, etc.)
|
|
23
|
+
props?: Record<string, any>; // Additional framework-specific props
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UIModel {
|
|
27
|
+
screenId: string; // Screen identifier (login, dashboard, ...)
|
|
28
|
+
routeId: string; // Route path or identifier
|
|
29
|
+
framework: FrameworkType; // Framework type
|
|
30
|
+
sourceFiles: string[]; // All source files merged
|
|
31
|
+
elements: UIElement[]; // All UI elements on this screen
|
|
32
|
+
metadata?: {
|
|
33
|
+
scannedAt: string; // ISO timestamp
|
|
34
|
+
version: string; // Tool version
|
|
35
|
+
hash: string; // Content hash for cache
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Selector DSL Types
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
export type DiscoveryMethod =
|
|
44
|
+
| 'ai-static-analysis' // Generated by AI from static code analysis
|
|
45
|
+
| 'manual' // Manually written by QA
|
|
46
|
+
| 'runtime'; // Discovered at runtime
|
|
47
|
+
|
|
48
|
+
export type LocatorType =
|
|
49
|
+
| 'css' // CSS selector (web)
|
|
50
|
+
| 'xpath' // XPath selector
|
|
51
|
+
| 'resource-id' // Android resource-id
|
|
52
|
+
| 'accessibility-id' // iOS accessibility identifier
|
|
53
|
+
| 'test-id' // Framework test ID (data-testid, etc.)
|
|
54
|
+
| 'key' // Flutter Key
|
|
55
|
+
| 'text' // Text-based locator
|
|
56
|
+
| 'role'; // ARIA role locator
|
|
57
|
+
|
|
58
|
+
export interface SelectorEntry {
|
|
59
|
+
selector: string; // The actual selector string
|
|
60
|
+
type: string; // Element type (input, button, link, text, ...)
|
|
61
|
+
description: string; // Human-readable description
|
|
62
|
+
discoveryMethod: DiscoveryMethod;
|
|
63
|
+
locatorType?: LocatorType; // Type of locator (for multi-platform)
|
|
64
|
+
confidence?: 'high' | 'medium' | 'low'; // AI confidence level
|
|
65
|
+
alternatives?: string[]; // Alternative selectors (fallbacks)
|
|
66
|
+
metadata?: {
|
|
67
|
+
lastValidated?: string; // ISO timestamp
|
|
68
|
+
validationStatus?: 'valid' | 'invalid' | 'unknown';
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SelectorDSL {
|
|
73
|
+
[elementId: string]: SelectorEntry;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Framework Types
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
export type FrameworkType =
|
|
81
|
+
| 'react'
|
|
82
|
+
| 'react-nextjs'
|
|
83
|
+
| 'vue'
|
|
84
|
+
| 'angular'
|
|
85
|
+
| 'html'
|
|
86
|
+
| 'flutter'
|
|
87
|
+
| 'jetpack-compose'
|
|
88
|
+
| 'swiftui';
|
|
89
|
+
|
|
90
|
+
export type TargetPlatform =
|
|
91
|
+
| 'web'
|
|
92
|
+
| 'android'
|
|
93
|
+
| 'ios'
|
|
94
|
+
| 'desktop';
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Executor Types
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
export type ExecutorType =
|
|
101
|
+
| 'playwright' // Web - Playwright
|
|
102
|
+
| 'puppeteer' // Web - Puppeteer
|
|
103
|
+
| 'selenium' // Web - Selenium
|
|
104
|
+
| 'appium-android' // Android - Appium
|
|
105
|
+
| 'appium-ios' // iOS - Appium
|
|
106
|
+
| 'flutter-driver' // Flutter Driver
|
|
107
|
+
| 'espresso' // Android - Espresso
|
|
108
|
+
| 'xcuitest'; // iOS - XCUITest
|
|
109
|
+
|
|
110
|
+
export interface ExecutorConfig {
|
|
111
|
+
type: ExecutorType;
|
|
112
|
+
platform: TargetPlatform;
|
|
113
|
+
selectorPath: string; // Path to selector DSL files
|
|
114
|
+
overridePath?: string; // Path to override files
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Cache Types
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
export interface CacheEntry {
|
|
122
|
+
screenId: string;
|
|
123
|
+
hash: string; // SHA-256 hash of UI Model + element IDs
|
|
124
|
+
selectors: SelectorDSL;
|
|
125
|
+
metadata: {
|
|
126
|
+
createdAt: string; // ISO timestamp
|
|
127
|
+
aiModel?: string; // AI model used (claude-3-5-sonnet, gemini-2.0-flash, ...)
|
|
128
|
+
tokenCost?: number; // Token usage
|
|
129
|
+
framework: FrameworkType;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Generator Config Types
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
export interface GeneratorConfig {
|
|
138
|
+
// AI Provider
|
|
139
|
+
aiProvider: 'anthropic' | 'google' | 'openai';
|
|
140
|
+
aiModel?: string; // Optional: specific model (defaults to best available)
|
|
141
|
+
|
|
142
|
+
// Paths
|
|
143
|
+
sourceRoot: string; // Root directory to scan (e.g., 'app', 'src')
|
|
144
|
+
uiModelPath: string; // Where to save UI models
|
|
145
|
+
selectorPath: string; // Where to save selectors
|
|
146
|
+
cachePath: string; // Cache directory
|
|
147
|
+
|
|
148
|
+
// Framework
|
|
149
|
+
framework: FrameworkType;
|
|
150
|
+
|
|
151
|
+
// Options
|
|
152
|
+
enableCache: boolean;
|
|
153
|
+
cacheExpiry?: number; // Cache expiry in milliseconds
|
|
154
|
+
parallelScans?: number; // Number of parallel scans
|
|
155
|
+
verbose?: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// CLI Types
|
|
160
|
+
// ============================================================================
|
|
161
|
+
|
|
162
|
+
export interface DiscoverOptions {
|
|
163
|
+
screenId?: string; // Specific screen to discover
|
|
164
|
+
all?: boolean; // Discover all screens
|
|
165
|
+
force?: boolean; // Force re-scan even if cached
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface GenerateOptions {
|
|
169
|
+
screenId?: string; // Specific screen to generate
|
|
170
|
+
all?: boolean; // Generate all screens
|
|
171
|
+
force?: boolean; // Force re-generation
|
|
172
|
+
skipCache?: boolean; // Skip cache lookup
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ValidateOptions {
|
|
176
|
+
screenId?: string; // Specific screen to validate
|
|
177
|
+
all?: boolean; // Validate all screens
|
|
178
|
+
executor?: ExecutorType; // Executor to validate against
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Gherkin Integration Types
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
export interface GherkinElementReference {
|
|
186
|
+
elementId: string; // e.g., "[login.email]"
|
|
187
|
+
screenId: string; // e.g., "login"
|
|
188
|
+
elementName: string; // e.g., "email"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function parseElementReference(ref: string): GherkinElementReference | null {
|
|
192
|
+
// Parse "[screen.element]" format
|
|
193
|
+
const match = ref.match(/^\[([^.]+)\.([^\]]+)\]$/);
|
|
194
|
+
if (!match) return null;
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
elementId: ref,
|
|
198
|
+
screenId: match[1],
|
|
199
|
+
elementName: match[2]
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Error Types
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
export class SelectorNotFoundError extends Error {
|
|
208
|
+
constructor(public elementId: string, public screenId: string) {
|
|
209
|
+
super(`Selector not found: ${elementId} on screen ${screenId}`);
|
|
210
|
+
this.name = 'SelectorNotFoundError';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export class UIModelNotFoundError extends Error {
|
|
215
|
+
constructor(public screenId: string) {
|
|
216
|
+
super(`UI Model not found for screen: ${screenId}`);
|
|
217
|
+
this.name = 'UIModelNotFoundError';
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export class GeneratorError extends Error {
|
|
222
|
+
constructor(message: string, public cause?: Error) {
|
|
223
|
+
super(message);
|
|
224
|
+
this.name = 'GeneratorError';
|
|
225
|
+
}
|
|
226
|
+
}
|