@sun-asterisk/sungen 1.0.23 → 2.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/README.md +198 -74
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/add.js +5 -3
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +110 -35
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +5 -16
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/frame-enter-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/frame-exit-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/keyboard-global-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/scroll-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +11 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +11 -2
- package/dist/generators/test-generator/code-generator.d.ts +0 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +0 -47
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +33 -8
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +4 -1
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +17 -5
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/keyboard-patterns.js +47 -0
- package/dist/generators/test-generator/patterns/keyboard-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/scope-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/scope-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/scope-patterns.js +36 -0
- package/dist/generators/test-generator/patterns/scope-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/scroll-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/scroll-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/scroll-patterns.js +25 -0
- package/dist/generators/test-generator/patterns/scroll-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/table-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/table-patterns.js +192 -0
- package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts +1 -3
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +36 -34
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +4 -1
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/types.d.ts +7 -24
- package/dist/generators/test-generator/types.d.ts.map +1 -1
- package/dist/generators/test-generator/types.js +2 -101
- package/dist/generators/test-generator/types.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +14 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +37 -11
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +8 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +343 -32
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts +1 -32
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +55 -216
- package/dist/orchestrator/screen-manager.js.map +1 -1
- 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 +3 -0
- package/dist/utils/selector-types.js.map +1 -1
- package/package.json +2 -2
- package/src/cli/commands/add.ts +5 -3
- package/src/cli/commands/generate.ts +90 -38
- package/src/cli/index.ts +5 -16
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/frame-enter-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/frame-exit-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/keyboard-global-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/scroll-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +11 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +11 -2
- package/src/generators/test-generator/code-generator.ts +0 -59
- package/src/generators/test-generator/patterns/assertion-patterns.ts +39 -8
- package/src/generators/test-generator/patterns/index.ts +12 -3
- package/src/generators/test-generator/patterns/interaction-patterns.ts +1 -1
- package/src/generators/test-generator/patterns/keyboard-patterns.ts +51 -0
- package/src/generators/test-generator/patterns/scope-patterns.ts +40 -0
- package/src/generators/test-generator/patterns/scroll-patterns.ts +27 -0
- package/src/generators/test-generator/patterns/table-patterns.ts +232 -0
- package/src/generators/test-generator/step-mapper.ts +39 -34
- package/src/generators/test-generator/template-engine.ts +3 -1
- package/src/generators/test-generator/types.ts +7 -112
- package/src/generators/test-generator/utils/selector-resolver.ts +70 -25
- package/src/orchestrator/project-initializer.ts +345 -32
- package/src/orchestrator/screen-manager.ts +61 -233
- package/src/utils/selector-types.ts +3 -0
- package/dist/cli/commands/cache-clear.d.ts +0 -3
- package/dist/cli/commands/cache-clear.d.ts.map +0 -1
- package/dist/cli/commands/cache-clear.js +0 -24
- package/dist/cli/commands/cache-clear.js.map +0 -1
- package/dist/cli/commands/full.d.ts +0 -3
- package/dist/cli/commands/full.d.ts.map +0 -1
- package/dist/cli/commands/full.js +0 -37
- package/dist/cli/commands/full.js.map +0 -1
- package/dist/cli/commands/live-scan.d.ts +0 -3
- package/dist/cli/commands/live-scan.d.ts.map +0 -1
- package/dist/cli/commands/live-scan.js +0 -78
- package/dist/cli/commands/live-scan.js.map +0 -1
- package/dist/cli/commands/map.d.ts +0 -3
- package/dist/cli/commands/map.d.ts.map +0 -1
- package/dist/cli/commands/map.js +0 -93
- package/dist/cli/commands/map.js.map +0 -1
- package/dist/cli/commands/validate.d.ts +0 -3
- package/dist/cli/commands/validate.d.ts.map +0 -1
- package/dist/cli/commands/validate.js +0 -43
- package/dist/cli/commands/validate.js.map +0 -1
- package/dist/cli/utils.d.ts +0 -6
- package/dist/cli/utils.d.ts.map +0 -1
- package/dist/cli/utils.js +0 -101
- package/dist/cli/utils.js.map +0 -1
- package/dist/config/config-loader.d.ts +0 -51
- package/dist/config/config-loader.d.ts.map +0 -1
- package/dist/config/config-loader.js +0 -216
- package/dist/config/config-loader.js.map +0 -1
- package/dist/config/config-schema.d.ts +0 -121
- package/dist/config/config-schema.d.ts.map +0 -1
- package/dist/config/config-schema.js +0 -7
- package/dist/config/config-schema.js.map +0 -1
- package/dist/core/live-scanner/config-reader.d.ts +0 -10
- package/dist/core/live-scanner/config-reader.d.ts.map +0 -1
- package/dist/core/live-scanner/config-reader.js +0 -87
- package/dist/core/live-scanner/config-reader.js.map +0 -1
- package/dist/core/live-scanner/element-finder.d.ts +0 -20
- package/dist/core/live-scanner/element-finder.d.ts.map +0 -1
- package/dist/core/live-scanner/element-finder.js +0 -486
- package/dist/core/live-scanner/element-finder.js.map +0 -1
- package/dist/core/live-scanner/index.d.ts +0 -8
- package/dist/core/live-scanner/index.d.ts.map +0 -1
- package/dist/core/live-scanner/index.js +0 -33
- package/dist/core/live-scanner/index.js.map +0 -1
- package/dist/core/live-scanner/matrix-reader.d.ts +0 -17
- package/dist/core/live-scanner/matrix-reader.d.ts.map +0 -1
- package/dist/core/live-scanner/matrix-reader.js +0 -60
- package/dist/core/live-scanner/matrix-reader.js.map +0 -1
- package/dist/core/live-scanner/matrix-writer.d.ts +0 -7
- package/dist/core/live-scanner/matrix-writer.d.ts.map +0 -1
- package/dist/core/live-scanner/matrix-writer.js +0 -103
- package/dist/core/live-scanner/matrix-writer.js.map +0 -1
- package/dist/core/live-scanner/role-fallback.d.ts +0 -15
- package/dist/core/live-scanner/role-fallback.d.ts.map +0 -1
- package/dist/core/live-scanner/role-fallback.js +0 -45
- package/dist/core/live-scanner/role-fallback.js.map +0 -1
- package/dist/core/live-scanner/scanner.d.ts +0 -13
- package/dist/core/live-scanner/scanner.d.ts.map +0 -1
- package/dist/core/live-scanner/scanner.js +0 -243
- package/dist/core/live-scanner/scanner.js.map +0 -1
- package/dist/core/live-scanner/step-replayer.d.ts +0 -26
- package/dist/core/live-scanner/step-replayer.d.ts.map +0 -1
- package/dist/core/live-scanner/step-replayer.js +0 -286
- package/dist/core/live-scanner/step-replayer.js.map +0 -1
- package/dist/core/live-scanner/types.d.ts +0 -52
- package/dist/core/live-scanner/types.d.ts.map +0 -1
- package/dist/core/live-scanner/types.js +0 -14
- package/dist/core/live-scanner/types.js.map +0 -1
- package/dist/core/selector-base/annotation-handler.d.ts +0 -45
- package/dist/core/selector-base/annotation-handler.d.ts.map +0 -1
- package/dist/core/selector-base/annotation-handler.js +0 -102
- package/dist/core/selector-base/annotation-handler.js.map +0 -1
- package/dist/core/selector-base/base-generator.d.ts +0 -49
- package/dist/core/selector-base/base-generator.d.ts.map +0 -1
- package/dist/core/selector-base/base-generator.js +0 -214
- package/dist/core/selector-base/base-generator.js.map +0 -1
- package/dist/core/selector-base/gherkin-parser.d.ts +0 -24
- package/dist/core/selector-base/gherkin-parser.d.ts.map +0 -1
- package/dist/core/selector-base/gherkin-parser.js +0 -42
- package/dist/core/selector-base/gherkin-parser.js.map +0 -1
- package/dist/core/selector-mapper/priority-mapper.d.ts +0 -74
- package/dist/core/selector-mapper/priority-mapper.d.ts.map +0 -1
- package/dist/core/selector-mapper/priority-mapper.js +0 -477
- package/dist/core/selector-mapper/priority-mapper.js.map +0 -1
- package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts +0 -91
- package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts.map +0 -1
- package/dist/core/ui-scanner/heuristics/base-heuristic.js +0 -175
- package/dist/core/ui-scanner/heuristics/base-heuristic.js.map +0 -1
- package/dist/core/ui-scanner/react-scanner.d.ts +0 -32
- package/dist/core/ui-scanner/react-scanner.d.ts.map +0 -1
- package/dist/core/ui-scanner/react-scanner.js +0 -163
- package/dist/core/ui-scanner/react-scanner.js.map +0 -1
- package/dist/core/ui-scanner/scanner-interface.d.ts +0 -94
- package/dist/core/ui-scanner/scanner-interface.d.ts.map +0 -1
- package/dist/core/ui-scanner/scanner-interface.js +0 -33
- package/dist/core/ui-scanner/scanner-interface.js.map +0 -1
- package/dist/core/ui-scanner/strict-scanner.d.ts +0 -81
- package/dist/core/ui-scanner/strict-scanner.d.ts.map +0 -1
- package/dist/core/ui-scanner/strict-scanner.js +0 -511
- package/dist/core/ui-scanner/strict-scanner.js.map +0 -1
- package/dist/core/validator/data-validator.d.ts +0 -38
- package/dist/core/validator/data-validator.d.ts.map +0 -1
- package/dist/core/validator/data-validator.js +0 -212
- package/dist/core/validator/data-validator.js.map +0 -1
- package/dist/core/validator/feature-validator.d.ts +0 -27
- package/dist/core/validator/feature-validator.d.ts.map +0 -1
- package/dist/core/validator/feature-validator.js +0 -182
- package/dist/core/validator/feature-validator.js.map +0 -1
- package/dist/core/validator/index.d.ts +0 -46
- package/dist/core/validator/index.d.ts.map +0 -1
- package/dist/core/validator/index.js +0 -17
- package/dist/core/validator/index.js.map +0 -1
- package/dist/core/validator/screen-validator.d.ts +0 -35
- package/dist/core/validator/screen-validator.d.ts.map +0 -1
- package/dist/core/validator/screen-validator.js +0 -195
- package/dist/core/validator/screen-validator.js.map +0 -1
- package/dist/core/validator/selector-validator.d.ts +0 -36
- package/dist/core/validator/selector-validator.d.ts.map +0 -1
- package/dist/core/validator/selector-validator.js +0 -210
- package/dist/core/validator/selector-validator.js.map +0 -1
- package/dist/external/ai-provider.d.ts +0 -60
- package/dist/external/ai-provider.d.ts.map +0 -1
- package/dist/external/ai-provider.js +0 -30
- package/dist/external/ai-provider.js.map +0 -1
- package/dist/external/anthropic-provider.d.ts +0 -29
- package/dist/external/anthropic-provider.d.ts.map +0 -1
- package/dist/external/anthropic-provider.js +0 -85
- package/dist/external/anthropic-provider.js.map +0 -1
- package/dist/generators/cache/cache-manager.d.ts +0 -66
- package/dist/generators/cache/cache-manager.d.ts.map +0 -1
- package/dist/generators/cache/cache-manager.js +0 -286
- package/dist/generators/cache/cache-manager.js.map +0 -1
- package/dist/generators/dsl-writer/index.d.ts +0 -33
- package/dist/generators/dsl-writer/index.d.ts.map +0 -1
- package/dist/generators/dsl-writer/index.js +0 -226
- package/dist/generators/dsl-writer/index.js.map +0 -1
- package/dist/generators/scaffold-generator/index.d.ts +0 -162
- package/dist/generators/scaffold-generator/index.d.ts.map +0 -1
- package/dist/generators/scaffold-generator/index.js +0 -868
- package/dist/generators/scaffold-generator/index.js.map +0 -1
- package/dist/generators/selector-mapper/ai-mapper.d.ts +0 -56
- package/dist/generators/selector-mapper/ai-mapper.d.ts.map +0 -1
- package/dist/generators/selector-mapper/ai-mapper.js +0 -457
- package/dist/generators/selector-mapper/ai-mapper.js.map +0 -1
- package/dist/generators/selector-mapper/hybrid-mapper.d.ts +0 -67
- package/dist/generators/selector-mapper/hybrid-mapper.d.ts.map +0 -1
- package/dist/generators/selector-mapper/hybrid-mapper.js +0 -349
- package/dist/generators/selector-mapper/hybrid-mapper.js.map +0 -1
- package/dist/generators/selector-mapper/index.d.ts +0 -8
- package/dist/generators/selector-mapper/index.d.ts.map +0 -1
- package/dist/generators/selector-mapper/index.js +0 -12
- package/dist/generators/selector-mapper/index.js.map +0 -1
- package/dist/generators/selector-mapper/intelligent-mapper.d.ts +0 -125
- package/dist/generators/selector-mapper/intelligent-mapper.d.ts.map +0 -1
- package/dist/generators/selector-mapper/intelligent-mapper.js +0 -391
- package/dist/generators/selector-mapper/intelligent-mapper.js.map +0 -1
- package/dist/generators/test-generator/ai-step-mapper.d.ts +0 -27
- package/dist/generators/test-generator/ai-step-mapper.d.ts.map +0 -1
- package/dist/generators/test-generator/ai-step-mapper.js +0 -204
- package/dist/generators/test-generator/ai-step-mapper.js.map +0 -1
- package/dist/generators/test-generator/auth-setup-generator.d.ts +0 -18
- package/dist/generators/test-generator/auth-setup-generator.d.ts.map +0 -1
- package/dist/generators/test-generator/auth-setup-generator.js +0 -82
- package/dist/generators/test-generator/auth-setup-generator.js.map +0 -1
- package/dist/generators/test-generator/patterns/legacy-patterns.d.ts +0 -7
- package/dist/generators/test-generator/patterns/legacy-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/legacy-patterns.js +0 -98
- package/dist/generators/test-generator/patterns/legacy-patterns.js.map +0 -1
- package/dist/generators/test-generator/templates/auth-setup.ts.hbs +0 -36
- package/dist/generators/ui-model-builder/deep-scanner.d.ts +0 -121
- package/dist/generators/ui-model-builder/deep-scanner.d.ts.map +0 -1
- package/dist/generators/ui-model-builder/deep-scanner.js +0 -1113
- package/dist/generators/ui-model-builder/deep-scanner.js.map +0 -1
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts +0 -110
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts.map +0 -1
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.js +0 -608
- package/dist/generators/ui-model-builder/enhanced-deep-scanner.js.map +0 -1
- package/dist/generators/ui-model-builder/react-scanner.d.ts +0 -107
- package/dist/generators/ui-model-builder/react-scanner.d.ts.map +0 -1
- package/dist/generators/ui-model-builder/react-scanner.js +0 -797
- package/dist/generators/ui-model-builder/react-scanner.js.map +0 -1
- package/dist/orchestrator/cache-manager.d.ts +0 -15
- package/dist/orchestrator/cache-manager.d.ts.map +0 -1
- package/dist/orchestrator/cache-manager.js +0 -62
- package/dist/orchestrator/cache-manager.js.map +0 -1
- package/dist/orchestrator/pipeline.d.ts +0 -56
- package/dist/orchestrator/pipeline.d.ts.map +0 -1
- package/dist/orchestrator/pipeline.js +0 -298
- package/dist/orchestrator/pipeline.js.map +0 -1
- package/dist/orchestrator/reporter.d.ts +0 -15
- package/dist/orchestrator/reporter.d.ts.map +0 -1
- package/dist/orchestrator/reporter.js +0 -30
- package/dist/orchestrator/reporter.js.map +0 -1
- package/src/cli/commands/cache-clear.ts +0 -22
- package/src/cli/commands/full.ts +0 -35
- package/src/cli/commands/live-scan.ts +0 -82
- package/src/cli/commands/map.ts +0 -97
- package/src/cli/commands/validate.ts +0 -43
- package/src/cli/utils.ts +0 -106
- package/src/config/ai-providers.yaml +0 -56
- package/src/config/config-loader.ts +0 -248
- package/src/config/config-schema.ts +0 -148
- package/src/config/default.config.yaml +0 -107
- package/src/config/framework.config.yaml +0 -52
- package/src/config/routes.yaml +0 -31
- package/src/core/live-scanner/config-reader.ts +0 -57
- package/src/core/live-scanner/element-finder.ts +0 -541
- package/src/core/live-scanner/index.ts +0 -7
- package/src/core/live-scanner/matrix-reader.ts +0 -65
- package/src/core/live-scanner/matrix-writer.ts +0 -77
- package/src/core/live-scanner/role-fallback.ts +0 -43
- package/src/core/live-scanner/scanner.ts +0 -254
- package/src/core/live-scanner/step-replayer.ts +0 -306
- package/src/core/live-scanner/types.ts +0 -58
- package/src/core/selector-base/annotation-handler.ts +0 -127
- package/src/core/selector-base/base-generator.ts +0 -234
- package/src/core/selector-base/gherkin-parser.ts +0 -57
- package/src/core/selector-mapper/priority-mapper.ts +0 -607
- package/src/core/ui-scanner/heuristics/base-heuristic.ts +0 -216
- package/src/core/ui-scanner/react-scanner.ts +0 -156
- package/src/core/ui-scanner/scanner-interface.ts +0 -133
- package/src/core/ui-scanner/strict-scanner.ts +0 -629
- package/src/core/validator/data-validator.ts +0 -202
- package/src/core/validator/feature-validator.ts +0 -176
- package/src/core/validator/index.ts +0 -57
- package/src/core/validator/screen-validator.ts +0 -209
- package/src/core/validator/selector-validator.ts +0 -209
- package/src/external/ai-provider.ts +0 -90
- package/src/external/anthropic-provider.ts +0 -114
- package/src/generators/README.md +0 -410
- package/src/generators/cache/cache-manager.ts +0 -322
- package/src/generators/dsl-writer/index.ts +0 -253
- package/src/generators/scaffold-generator/index.ts +0 -1019
- package/src/generators/selector-mapper/ai-mapper.ts +0 -528
- package/src/generators/selector-mapper/hybrid-mapper.ts +0 -427
- package/src/generators/selector-mapper/index.ts +0 -10
- package/src/generators/selector-mapper/intelligent-mapper.ts +0 -530
- package/src/generators/test-generator/ai-step-mapper.ts +0 -224
- package/src/generators/test-generator/auth-setup-generator.ts +0 -59
- package/src/generators/test-generator/patterns/legacy-patterns.ts +0 -104
- package/src/generators/test-generator/templates/auth-setup.ts.hbs +0 -36
- package/src/generators/ui-model-builder/deep-scanner.ts +0 -1244
- package/src/generators/ui-model-builder/enhanced-deep-scanner.ts +0 -731
- package/src/generators/ui-model-builder/react-scanner.ts +0 -959
- package/src/orchestrator/cache-manager.ts +0 -32
- package/src/orchestrator/pipeline.ts +0 -354
- package/src/orchestrator/reporter.ts +0 -36
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Role Fallback Groups
|
|
3
|
-
* Maps Gherkin element type hints to ordered lists of ARIA roles to try.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const ROLE_FALLBACK_GROUPS: Record<string, string[]> = {
|
|
7
|
-
button: ['button', 'link', 'menuitem', 'tab'],
|
|
8
|
-
link: ['link', 'button', 'menuitem'],
|
|
9
|
-
field: ['textbox', 'combobox', 'searchbox', 'spinbutton'],
|
|
10
|
-
dropdown: ['combobox', 'listbox', 'select', 'button'],
|
|
11
|
-
checkbox: ['checkbox', 'switch'],
|
|
12
|
-
text: ['heading', 'paragraph'],
|
|
13
|
-
menuitem: ['menuitem', 'option', 'link'],
|
|
14
|
-
dialog: ['dialog', 'alertdialog'],
|
|
15
|
-
modal: ['dialog', 'alertdialog'],
|
|
16
|
-
tab: ['tab', 'button', 'link'],
|
|
17
|
-
row: ['row', 'listitem', 'option'],
|
|
18
|
-
tag: ['button', 'link', 'option'],
|
|
19
|
-
uploader: ['textbox', 'button'],
|
|
20
|
-
textarea: ['textbox'],
|
|
21
|
-
radio: ['radio'],
|
|
22
|
-
select: ['combobox', 'listbox', 'select'],
|
|
23
|
-
page: [],
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get the ordered list of ARIA roles to try for a given Gherkin element type.
|
|
28
|
-
* Returns empty array if the type is unknown (will fall through to text matching).
|
|
29
|
-
*/
|
|
30
|
-
export function getRoleFallbacks(gherkinType: string): string[] {
|
|
31
|
-
const normalized = gherkinType.toLowerCase().trim();
|
|
32
|
-
return ROLE_FALLBACK_GROUPS[normalized] || [];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Check if a Gherkin type should use getByText as its primary strategy
|
|
37
|
-
* (types that don't map well to ARIA roles).
|
|
38
|
-
*/
|
|
39
|
-
export function isTextOnlyType(gherkinType: string): boolean {
|
|
40
|
-
const normalized = gherkinType.toLowerCase().trim();
|
|
41
|
-
return normalized === 'text' || normalized === 'message' || normalized === 'error'
|
|
42
|
-
|| normalized === 'warning' || normalized === 'success' || normalized === 'alert';
|
|
43
|
-
}
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live Scanner
|
|
3
|
-
* Main orchestrator: parses features, sets up auth, replays steps, collects elements.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import { glob } from 'glob';
|
|
9
|
-
import { LiveScanResult, LiveScanScenario, LiveScanOptions } from './types';
|
|
10
|
-
import { replaySteps } from './step-replayer';
|
|
11
|
-
import { writeMatrix } from './matrix-writer';
|
|
12
|
-
import { readMatrix, mergeMatrixElements } from './matrix-reader';
|
|
13
|
-
import { readBaseUrlFromPlaywrightConfig } from './config-reader';
|
|
14
|
-
|
|
15
|
-
export class LiveScanner {
|
|
16
|
-
private options: LiveScanOptions;
|
|
17
|
-
|
|
18
|
-
constructor(options: LiveScanOptions) {
|
|
19
|
-
this.options = options;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async scan(): Promise<LiveScanResult[]> {
|
|
23
|
-
const { screenName, screensDir } = this.options;
|
|
24
|
-
const screenDir = path.join(screensDir, screenName);
|
|
25
|
-
const featuresDir = path.join(screenDir, 'features');
|
|
26
|
-
const selectorsDir = path.join(screenDir, 'selectors');
|
|
27
|
-
|
|
28
|
-
if (!fs.existsSync(featuresDir)) {
|
|
29
|
-
throw new Error(`Features directory not found: ${featuresDir}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Resolve baseUrl: explicit option > playwright.config.ts
|
|
33
|
-
const baseUrl = this.resolveBaseUrl();
|
|
34
|
-
console.log(`🌐 Base URL: ${baseUrl}`);
|
|
35
|
-
|
|
36
|
-
fs.mkdirSync(selectorsDir, { recursive: true });
|
|
37
|
-
|
|
38
|
-
const featureFiles = glob.sync('**/*.feature', {
|
|
39
|
-
cwd: featuresDir,
|
|
40
|
-
absolute: true,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
if (featureFiles.length === 0) {
|
|
44
|
-
throw new Error(`No .feature files found in ${featuresDir}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Lazy-load playwright to avoid errors when not installed
|
|
48
|
-
let playwright: any;
|
|
49
|
-
try {
|
|
50
|
-
playwright = require('playwright');
|
|
51
|
-
} catch {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'Playwright is required for live-scan. Install it with: npm install playwright'
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const browser = await playwright.chromium.launch({
|
|
58
|
-
headless: !this.options.headed,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const results: LiveScanResult[] = [];
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// Parse all features first
|
|
65
|
-
const { GherkinParser } = require('../../generators/gherkin-parser');
|
|
66
|
-
const parser = new GherkinParser();
|
|
67
|
-
|
|
68
|
-
for (const featureFile of featureFiles) {
|
|
69
|
-
const featureName = path.basename(featureFile, '.feature');
|
|
70
|
-
console.log(`\n📄 Scanning feature: ${featureName}`);
|
|
71
|
-
|
|
72
|
-
// Load existing matrix for incremental scan (non-force mode)
|
|
73
|
-
let existingResult: LiveScanResult | null = null;
|
|
74
|
-
if (!this.options.force) {
|
|
75
|
-
const matrixPath = path.join(selectorsDir, `${featureName}.live-scan.yaml`);
|
|
76
|
-
existingResult = readMatrix(matrixPath);
|
|
77
|
-
if (existingResult) {
|
|
78
|
-
console.log(` 📦 Found existing scan — resolved elements will be skipped`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const parsed = parser.parseFeatureFile(featureFile);
|
|
84
|
-
const result = await this.scanFeature(browser, playwright, parsed, baseUrl, existingResult);
|
|
85
|
-
results.push(result);
|
|
86
|
-
|
|
87
|
-
// Write matrix file
|
|
88
|
-
const matrixPath = path.join(selectorsDir, `${featureName}.live-scan.yaml`);
|
|
89
|
-
writeMatrix(result, matrixPath);
|
|
90
|
-
console.log(` ✅ Matrix written: ${matrixPath}`);
|
|
91
|
-
} catch (error: any) {
|
|
92
|
-
console.error(` ❌ Failed to scan ${featureName}: ${error.message}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} finally {
|
|
96
|
-
await browser.close();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return results;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private resolveBaseUrl(): string {
|
|
103
|
-
// 1. Explicit option (from --url flag)
|
|
104
|
-
if (this.options.baseUrl) {
|
|
105
|
-
return this.options.baseUrl;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 2. Auto-detect from playwright.config.ts
|
|
109
|
-
const detected = readBaseUrlFromPlaywrightConfig(process.cwd());
|
|
110
|
-
if (detected) {
|
|
111
|
-
console.log(`📋 Auto-detected baseURL from playwright.config.ts`);
|
|
112
|
-
return detected;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
throw new Error(
|
|
116
|
-
'Base URL not found. Either:\n' +
|
|
117
|
-
' • Add baseURL to your playwright.config.ts\n' +
|
|
118
|
-
' • Use --url <baseUrl> flag'
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private async scanFeature(
|
|
123
|
-
browser: any,
|
|
124
|
-
playwright: any,
|
|
125
|
-
feature: any,
|
|
126
|
-
baseUrl: string,
|
|
127
|
-
existingResult?: LiveScanResult | null
|
|
128
|
-
): Promise<LiveScanResult> {
|
|
129
|
-
const featurePath = feature.path || '/';
|
|
130
|
-
const scenarios: Record<string, LiveScanScenario> = {};
|
|
131
|
-
|
|
132
|
-
// Build merged existing elements map for skip logic
|
|
133
|
-
const existingElements = existingResult ? mergeMatrixElements(existingResult) : undefined;
|
|
134
|
-
|
|
135
|
-
// Build a map of @steps scenarios for resolution
|
|
136
|
-
const stepsMap = new Map<string, any>();
|
|
137
|
-
for (const scenario of feature.scenarios) {
|
|
138
|
-
if (scenario.stepsName) {
|
|
139
|
-
stepsMap.set(scenario.stepsName, scenario);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
for (const scenario of feature.scenarios) {
|
|
144
|
-
// Skip @steps-only scenarios (they're reusable blocks, not standalone)
|
|
145
|
-
if (scenario.stepsName && !scenario.extendsName) {
|
|
146
|
-
console.log(` ⏭️ Skipping @steps block: ${scenario.name}`);
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const scenarioKey = slugify(scenario.name);
|
|
151
|
-
console.log(` 🎬 Scenario: ${scenario.name}`);
|
|
152
|
-
|
|
153
|
-
// Extract auth tag
|
|
154
|
-
const authTag = scenario.tags.find((t: string) => t.startsWith('@auth:'));
|
|
155
|
-
const authRole = authTag ? authTag.replace('@auth:', '') : null;
|
|
156
|
-
|
|
157
|
-
// Resolve @extend: inline base steps
|
|
158
|
-
let steps = [...scenario.steps];
|
|
159
|
-
if (scenario.extendsName) {
|
|
160
|
-
const baseScenario = stepsMap.get(scenario.extendsName);
|
|
161
|
-
if (baseScenario) {
|
|
162
|
-
steps = [...baseScenario.steps, ...steps];
|
|
163
|
-
console.log(` 🔗 Extended with @steps:${scenario.extendsName} (${baseScenario.steps.length} steps)`);
|
|
164
|
-
} else {
|
|
165
|
-
console.warn(` ⚠️ @extend:${scenario.extendsName} not found`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Set up browser context with auth
|
|
170
|
-
const contextOptions: any = {};
|
|
171
|
-
if (authRole) {
|
|
172
|
-
const authPath = path.join(this.options.authDir || 'specs/.auth', `${authRole}.json`);
|
|
173
|
-
if (fs.existsSync(authPath)) {
|
|
174
|
-
contextOptions.storageState = authPath;
|
|
175
|
-
console.log(` 🔐 Auth: ${authRole}`);
|
|
176
|
-
} else {
|
|
177
|
-
console.warn(` ⚠️ Auth file not found for role '${authRole}', scanning without authentication`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const context = await browser.newContext(contextOptions);
|
|
182
|
-
const page = await context.newPage();
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
const result = await replaySteps(page, steps, featurePath, baseUrl, existingElements);
|
|
186
|
-
|
|
187
|
-
scenarios[scenarioKey] = {
|
|
188
|
-
auth: authRole,
|
|
189
|
-
extends: scenario.extendsName || null,
|
|
190
|
-
path: featurePath,
|
|
191
|
-
elements: result.elements,
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
// Print element summary
|
|
195
|
-
const elementCount = Object.keys(result.elements).length;
|
|
196
|
-
const skipped = Object.values(result.elements).filter(
|
|
197
|
-
(e: any) => e.skipped
|
|
198
|
-
).length;
|
|
199
|
-
const resolved = Object.values(result.elements).filter(
|
|
200
|
-
(e: any) => e.matchMethod !== 'unresolved'
|
|
201
|
-
).length;
|
|
202
|
-
const warnings = Object.values(result.elements).filter(
|
|
203
|
-
(e: any) => e.warning
|
|
204
|
-
).length;
|
|
205
|
-
|
|
206
|
-
const skippedInfo = skipped > 0 ? `, ${skipped} cached` : '';
|
|
207
|
-
console.log(` 📊 Elements: ${resolved}/${elementCount} resolved${skippedInfo}${warnings > 0 ? `, ${warnings} warnings` : ''}`);
|
|
208
|
-
|
|
209
|
-
for (const error of result.errors) {
|
|
210
|
-
console.log(` ⚠️ ${error}`);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Print per-element status
|
|
214
|
-
for (const [key, el] of Object.entries(result.elements) as any) {
|
|
215
|
-
if (el.skipped) {
|
|
216
|
-
// Already printed in replaySteps
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (el.matchMethod === 'unresolved') {
|
|
220
|
-
console.log(` ✗ [${el.gherkinRef}] ${el.gherkinType} → NOT FOUND`);
|
|
221
|
-
} else if (el.warning) {
|
|
222
|
-
console.log(` ⚠ [${el.gherkinRef}] ${el.gherkinType} → ${el.selectorType}(${el.selectorValue || el.name}) (${el.warning})`);
|
|
223
|
-
} else {
|
|
224
|
-
const locatorDesc = el.selectorType
|
|
225
|
-
? `${el.selectorType}${el.role ? `('${el.role}', {name: '${el.name}'})` : `('${el.selectorValue || el.name}')`}`
|
|
226
|
-
: el.gherkinType;
|
|
227
|
-
console.log(` ✓ [${el.gherkinRef}] → getBy${capitalize(el.selectorType || 'text')}${el.role ? `('${el.role}', {name: '${el.name}'})` : `('${el.selectorValue || el.name}')`}`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
} finally {
|
|
231
|
-
await context.close();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return {
|
|
236
|
-
screen: this.options.screenName,
|
|
237
|
-
baseUrl,
|
|
238
|
-
scannedAt: new Date().toISOString(),
|
|
239
|
-
scenarios,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function slugify(text: string): string {
|
|
245
|
-
return text
|
|
246
|
-
.toLowerCase()
|
|
247
|
-
.replace(/[^\w\s-]/g, '')
|
|
248
|
-
.trim()
|
|
249
|
-
.replace(/\s+/g, '-');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function capitalize(str: string): string {
|
|
253
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
254
|
-
}
|
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Step Replayer
|
|
3
|
-
* Replays Gherkin steps on a live Playwright page to reveal dynamic elements.
|
|
4
|
-
* Takes a snapshot and finds elements after each action.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Page } from 'playwright';
|
|
8
|
-
import { findElement } from './element-finder';
|
|
9
|
-
import { LiveElement, ElementContext } from './types';
|
|
10
|
-
import { SelectorResolver } from '../../generators/test-generator/utils/selector-resolver';
|
|
11
|
-
|
|
12
|
-
interface ParsedStepInfo {
|
|
13
|
-
keyword: string;
|
|
14
|
-
text: string;
|
|
15
|
-
selectorRef?: string;
|
|
16
|
-
dataRef?: string;
|
|
17
|
-
elementType?: string;
|
|
18
|
-
nth?: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ReplayResult {
|
|
22
|
-
elements: Record<string, LiveElement>;
|
|
23
|
-
errors: string[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Replay a sequence of Gherkin steps on a Playwright page,
|
|
28
|
-
* finding and interacting with elements along the way.
|
|
29
|
-
*/
|
|
30
|
-
export async function replaySteps(
|
|
31
|
-
page: Page,
|
|
32
|
-
steps: ParsedStepInfo[],
|
|
33
|
-
featurePath: string,
|
|
34
|
-
baseUrl: string,
|
|
35
|
-
existingElements?: Record<string, LiveElement>
|
|
36
|
-
): Promise<ReplayResult> {
|
|
37
|
-
const elements: Record<string, LiveElement> = {};
|
|
38
|
-
const errors: string[] = [];
|
|
39
|
-
let currentContext: ElementContext = 'page';
|
|
40
|
-
|
|
41
|
-
// Pre-scan: detect which base keys have multiple normalized element types
|
|
42
|
-
// so we can append --type suffix (matching scaffold generator behavior)
|
|
43
|
-
// Uses normalizeElementType to match scaffold generator's extractTargetType mapping
|
|
44
|
-
// e.g. modal/dialog both normalize to 'dialog', so they won't create a false conflict
|
|
45
|
-
const typesPerKey = new Map<string, Set<string>>();
|
|
46
|
-
for (const step of steps) {
|
|
47
|
-
if (!step.selectorRef) continue;
|
|
48
|
-
const baseKey = generateElementKey(step.selectorRef, step.elementType, step.nth);
|
|
49
|
-
const action = detectAction(step.text);
|
|
50
|
-
const normalizedType = normalizeElementType(step.elementType || 'button', action);
|
|
51
|
-
if (!typesPerKey.has(baseKey)) {
|
|
52
|
-
typesPerKey.set(baseKey, new Set());
|
|
53
|
-
}
|
|
54
|
-
typesPerKey.get(baseKey)!.add(normalizedType);
|
|
55
|
-
}
|
|
56
|
-
const conflictKeys = new Set<string>();
|
|
57
|
-
for (const [key, types] of typesPerKey) {
|
|
58
|
-
if (types.size > 1) conflictKeys.add(key);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
for (const step of steps) {
|
|
62
|
-
try {
|
|
63
|
-
const action = detectAction(step.text);
|
|
64
|
-
|
|
65
|
-
// Handle navigation (Given User is on [x] page)
|
|
66
|
-
if (action === 'navigate') {
|
|
67
|
-
await page.goto(baseUrl + featurePath, { waitUntil: 'networkidle', timeout: 15000 });
|
|
68
|
-
await settle(page);
|
|
69
|
-
if (step.selectorRef) {
|
|
70
|
-
const baseKey = generateElementKey(step.selectorRef, step.elementType, step.nth);
|
|
71
|
-
const normalizedType = normalizeElementType(step.elementType || 'page', action);
|
|
72
|
-
const key = conflictKeys.has(baseKey) ? `${baseKey}--${normalizedType}` : baseKey;
|
|
73
|
-
// Record the page element but don't search for it on the DOM
|
|
74
|
-
elements[key] = {
|
|
75
|
-
gherkinRef: step.selectorRef,
|
|
76
|
-
gherkinType: step.elementType || 'page',
|
|
77
|
-
selectorType: '',
|
|
78
|
-
selectorValue: featurePath,
|
|
79
|
-
role: '',
|
|
80
|
-
name: step.selectorRef,
|
|
81
|
-
testid: null,
|
|
82
|
-
tag: '',
|
|
83
|
-
contenteditable: false,
|
|
84
|
-
placeholder: null,
|
|
85
|
-
label: null,
|
|
86
|
-
context: 'page',
|
|
87
|
-
matchMethod: 'exact_role',
|
|
88
|
-
exact: false,
|
|
89
|
-
warning: null,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Skip steps without element references
|
|
96
|
-
if (!step.selectorRef) continue;
|
|
97
|
-
|
|
98
|
-
const baseKey = generateElementKey(step.selectorRef, step.elementType, step.nth);
|
|
99
|
-
const elementType = step.elementType || 'button';
|
|
100
|
-
const normalizedType = normalizeElementType(elementType, action);
|
|
101
|
-
const key = conflictKeys.has(baseKey) ? `${baseKey}--${normalizedType}` : baseKey;
|
|
102
|
-
const nth = step.nth || 0;
|
|
103
|
-
|
|
104
|
-
// Skip column-type elements — they use header text matching, no DOM lookup needed
|
|
105
|
-
if (normalizedType === 'column') {
|
|
106
|
-
console.log(` ⏭️ [${step.selectorRef}] column → skipped (table header match)`);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Skip if element already resolved in existing scan (non-force mode)
|
|
111
|
-
if (existingElements && existingElements[key] && existingElements[key].matchMethod !== 'unresolved') {
|
|
112
|
-
elements[key] = existingElements[key];
|
|
113
|
-
(elements[key] as any).skipped = true;
|
|
114
|
-
console.log(` ⏩ [${step.selectorRef}] ${elementType} → cached (${existingElements[key].selectorType})`);
|
|
115
|
-
|
|
116
|
-
// Still execute clicks for cached elements to maintain page state
|
|
117
|
-
if (action === 'click') {
|
|
118
|
-
try {
|
|
119
|
-
await executeClick(page, step.selectorRef, elementType, nth);
|
|
120
|
-
await settle(page);
|
|
121
|
-
currentContext = await detectCurrentContext(page, currentContext, step.text);
|
|
122
|
-
} catch {
|
|
123
|
-
// Click failed for cached element, continue anyway
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Find the element on the page, scoped to current context (dialog/page)
|
|
130
|
-
const element = await findElement(page, step.selectorRef, elementType, nth, currentContext);
|
|
131
|
-
element.context = currentContext;
|
|
132
|
-
|
|
133
|
-
// Record in matrix (first match wins for duplicate keys)
|
|
134
|
-
if (!elements[key]) {
|
|
135
|
-
elements[key] = element;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Execute the action if element was found
|
|
139
|
-
if (element.matchMethod !== 'unresolved') {
|
|
140
|
-
if (action === 'click') {
|
|
141
|
-
await executeClick(page, step.selectorRef, elementType, nth);
|
|
142
|
-
await settle(page);
|
|
143
|
-
currentContext = await detectCurrentContext(page, currentContext, step.text);
|
|
144
|
-
} else if (action === 'fill') {
|
|
145
|
-
// For fill actions during scanning, use a placeholder value
|
|
146
|
-
// We just need to verify the element exists
|
|
147
|
-
}
|
|
148
|
-
// 'see' and 'wait' actions: just record, don't interact
|
|
149
|
-
} else {
|
|
150
|
-
errors.push(`Step "${step.text}": element [${step.selectorRef}] not found`);
|
|
151
|
-
// Continue scanning remaining steps — record what we can even if some elements are missing
|
|
152
|
-
}
|
|
153
|
-
} catch (error: any) {
|
|
154
|
-
errors.push(`Step "${step.text}": ${error.message}`);
|
|
155
|
-
// On navigation or critical errors, stop replaying this scenario
|
|
156
|
-
if (error.message.includes('Navigation') || error.message.includes('net::')) {
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return { elements, errors };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function executeClick(
|
|
166
|
-
page: Page,
|
|
167
|
-
ref: string,
|
|
168
|
-
elementType: string,
|
|
169
|
-
nth: number
|
|
170
|
-
): Promise<void> {
|
|
171
|
-
const namePattern = new RegExp(escapeRegex(ref), 'i');
|
|
172
|
-
|
|
173
|
-
// Try role-based click first
|
|
174
|
-
const { getRoleFallbacks } = require('./role-fallback');
|
|
175
|
-
const roles = getRoleFallbacks(elementType);
|
|
176
|
-
|
|
177
|
-
for (const role of roles) {
|
|
178
|
-
try {
|
|
179
|
-
const locator = page.getByRole(role as any, { name: namePattern });
|
|
180
|
-
const target = nth > 0 ? locator.nth(nth) : locator.first();
|
|
181
|
-
if (await target.isVisible({ timeout: 3000 })) {
|
|
182
|
-
await target.click({ timeout: 5000 });
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
} catch {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Fallback to text click
|
|
191
|
-
try {
|
|
192
|
-
const locator = page.getByText(namePattern);
|
|
193
|
-
const target = nth > 0 ? locator.nth(nth) : locator.first();
|
|
194
|
-
await target.click({ timeout: 5000 });
|
|
195
|
-
} catch {
|
|
196
|
-
// Try testid as last resort
|
|
197
|
-
const normalized = ref.toLowerCase().replace(/[\s_-]+/g, '-');
|
|
198
|
-
await page.locator(`[data-testid*="${normalized}" i]`).first().click({ timeout: 5000 });
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function detectAction(text: string): string {
|
|
203
|
-
const lower = text.toLowerCase();
|
|
204
|
-
if (/\b(is on|navigate|go to|visit|open)\b/.test(lower) && /\bpage\b/.test(lower)) return 'navigate';
|
|
205
|
-
if (/\b(click|press|tap|submit)\b/.test(lower)) return 'click';
|
|
206
|
-
if (/\b(fill|enter|type|input)\b/.test(lower)) return 'fill';
|
|
207
|
-
if (/\b(see|verify|check|expect|visible|displayed)\b/.test(lower)) return 'see';
|
|
208
|
-
if (/\b(select|choose|pick)\b/.test(lower)) return 'select';
|
|
209
|
-
if (/\b(wait)\b/.test(lower)) return 'wait';
|
|
210
|
-
return 'see';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async function detectCurrentContext(
|
|
214
|
-
page: Page,
|
|
215
|
-
previous: ElementContext,
|
|
216
|
-
stepText: string
|
|
217
|
-
): Promise<ElementContext> {
|
|
218
|
-
const lower = stepText.toLowerCase();
|
|
219
|
-
|
|
220
|
-
// Check if we opened a dialog/modal
|
|
221
|
-
if (/\b(dialog|modal|panel)\b/.test(lower)) {
|
|
222
|
-
return 'dialog';
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Check if a dialog is now visible on the page
|
|
226
|
-
try {
|
|
227
|
-
const dialogLocator = page.getByRole('dialog');
|
|
228
|
-
if (await dialogLocator.count() > 0) {
|
|
229
|
-
return 'dialog';
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
// ignore
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Check for menu
|
|
236
|
-
if (/\b(menu|dropdown)\b/.test(lower)) {
|
|
237
|
-
return 'menu';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
const menuLocator = page.getByRole('menu');
|
|
242
|
-
if (await menuLocator.count() > 0) {
|
|
243
|
-
return 'menu';
|
|
244
|
-
}
|
|
245
|
-
} catch {
|
|
246
|
-
// ignore
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return previous;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function settle(page: Page): Promise<void> {
|
|
253
|
-
try {
|
|
254
|
-
await page.waitForLoadState('networkidle', { timeout: 5000 });
|
|
255
|
-
} catch {
|
|
256
|
-
// Timeout is acceptable — page may have long-polling connections
|
|
257
|
-
}
|
|
258
|
-
// Small extra wait for animations/transitions
|
|
259
|
-
await page.waitForTimeout(300);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function generateElementKey(ref: string, elementType?: string, nth?: number): string {
|
|
263
|
-
// Use the same key generation as the scaffold generator
|
|
264
|
-
// so live-scan keys match scaffold keys in the map command.
|
|
265
|
-
// Append --N suffix for nth > 0, consistent with scaffold generator format.
|
|
266
|
-
const baseKey = SelectorResolver.generateKey(ref);
|
|
267
|
-
return nth && nth > 0 ? `${baseKey}--${nth}` : baseKey;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Normalize element type to match scaffold generator's extractTargetType() mapping.
|
|
272
|
-
* e.g. modal→dialog, img/icon/logo→img, input→field, select→dropdown, etc.
|
|
273
|
-
*/
|
|
274
|
-
function normalizeElementType(elementType: string, action: string): string {
|
|
275
|
-
const t = elementType.toLowerCase();
|
|
276
|
-
if (t === 'page') return 'page';
|
|
277
|
-
if (t === 'link') return 'link';
|
|
278
|
-
if (t === 'button') return 'button';
|
|
279
|
-
if (t === 'radio') return 'radio';
|
|
280
|
-
if (t === 'dropdown' || t === 'select') return 'dropdown';
|
|
281
|
-
if (t === 'checkbox') return 'checkbox';
|
|
282
|
-
if (t === 'field' || t === 'input') return 'field';
|
|
283
|
-
if (t === 'textbox') return 'textbox';
|
|
284
|
-
if (t === 'textarea') return 'textarea';
|
|
285
|
-
if (t === 'text') return 'text';
|
|
286
|
-
if (t === 'label') return 'label';
|
|
287
|
-
if (t === 'uploader') return 'uploader';
|
|
288
|
-
if (t === 'element') return 'element';
|
|
289
|
-
if (t === 'column' || t === 'columnheader') return 'column';
|
|
290
|
-
if (t === 'logo' || t === 'image' || t === 'img' || t === 'icon') return 'img';
|
|
291
|
-
if (t === 'dialog' || t === 'modal') return 'dialog';
|
|
292
|
-
if (t === 'heading' || t === 'header') return 'heading';
|
|
293
|
-
if (t === 'title' || t === 'caption' || t === 'message') return 'text';
|
|
294
|
-
// Default based on action (matches scaffold generator)
|
|
295
|
-
switch (action) {
|
|
296
|
-
case 'click': return 'text';
|
|
297
|
-
case 'fill': return 'field';
|
|
298
|
-
case 'see': return 'text';
|
|
299
|
-
case 'select': return 'dropdown';
|
|
300
|
-
default: return 'element';
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function escapeRegex(str: string): string {
|
|
305
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
306
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live Scanner Types
|
|
3
|
-
* Defines the data structures for live DOM scanning and matrix file output.
|
|
4
|
-
*
|
|
5
|
-
* Selector types map directly to Playwright locator strategies:
|
|
6
|
-
* testid → page.getByTestId(value)
|
|
7
|
-
* role → page.getByRole(value, { name })
|
|
8
|
-
* label → page.getByLabel(value)
|
|
9
|
-
* placeholder → page.getByPlaceholder(value)
|
|
10
|
-
* text → page.getByText(value)
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export type MatchMethod = 'testid' | 'exact_role' | 'role_fallback' | 'label' | 'placeholder' | 'text_only' | 'contenteditable' | 'unresolved';
|
|
14
|
-
|
|
15
|
-
export type SelectorType = 'testid' | 'role' | 'label' | 'placeholder' | 'text' | 'locator';
|
|
16
|
-
|
|
17
|
-
export type ElementContext = 'page' | 'dialog' | 'menu' | 'modal' | 'popup';
|
|
18
|
-
|
|
19
|
-
export interface LiveElement {
|
|
20
|
-
gherkinRef: string;
|
|
21
|
-
gherkinType: string;
|
|
22
|
-
selectorType: SelectorType | '';
|
|
23
|
-
selectorValue: string;
|
|
24
|
-
role: string;
|
|
25
|
-
name: string;
|
|
26
|
-
testid: string | null;
|
|
27
|
-
tag: string;
|
|
28
|
-
contenteditable: boolean;
|
|
29
|
-
placeholder: string | null;
|
|
30
|
-
label: string | null;
|
|
31
|
-
context: ElementContext;
|
|
32
|
-
matchMethod: MatchMethod;
|
|
33
|
-
exact: boolean;
|
|
34
|
-
warning: string | null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface LiveScanScenario {
|
|
38
|
-
auth: string | null;
|
|
39
|
-
extends: string | null;
|
|
40
|
-
path: string;
|
|
41
|
-
elements: Record<string, LiveElement>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface LiveScanResult {
|
|
45
|
-
screen: string;
|
|
46
|
-
baseUrl: string;
|
|
47
|
-
scannedAt: string;
|
|
48
|
-
scenarios: Record<string, LiveScanScenario>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface LiveScanOptions {
|
|
52
|
-
baseUrl?: string;
|
|
53
|
-
screenName: string;
|
|
54
|
-
screensDir: string;
|
|
55
|
-
headed?: boolean;
|
|
56
|
-
authDir?: string;
|
|
57
|
-
force?: boolean;
|
|
58
|
-
}
|