@sun-asterisk/sungen 1.0.19 → 1.0.21
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 +3 -3
- package/dist/cli/commands/add.d.ts +3 -0
- package/dist/cli/commands/add.d.ts.map +1 -0
- package/dist/cli/commands/add.js +27 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/cache-clear.d.ts +3 -0
- package/dist/cli/commands/cache-clear.d.ts.map +1 -0
- package/dist/cli/commands/cache-clear.js +24 -0
- package/dist/cli/commands/cache-clear.js.map +1 -0
- package/dist/cli/commands/full.d.ts +3 -0
- package/dist/cli/commands/full.d.ts.map +1 -0
- package/dist/cli/commands/full.js +37 -0
- package/dist/cli/commands/full.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +3 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +53 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +20 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/live-scan.d.ts +3 -0
- package/dist/cli/commands/live-scan.d.ts.map +1 -0
- package/dist/cli/commands/{live-scan-command.js → live-scan.js} +22 -16
- package/dist/cli/commands/live-scan.js.map +1 -0
- package/dist/cli/commands/makeauth.d.ts +3 -0
- package/dist/cli/commands/makeauth.d.ts.map +1 -0
- package/dist/cli/commands/makeauth.js +76 -0
- package/dist/cli/commands/makeauth.js.map +1 -0
- package/dist/cli/commands/map.d.ts +3 -0
- package/dist/cli/commands/map.d.ts.map +1 -0
- package/dist/cli/commands/map.js +93 -0
- package/dist/cli/commands/map.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +3 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +43 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.js +29 -442
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/types.d.ts +9 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +7 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/utils.d.ts +6 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +101 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/core/live-scanner/matrix-reader.d.ts.map +1 -1
- package/dist/core/live-scanner/matrix-reader.js +2 -40
- package/dist/core/live-scanner/matrix-reader.js.map +1 -1
- package/dist/core/live-scanner/scanner.d.ts.map +1 -1
- package/dist/core/live-scanner/scanner.js +22 -4
- package/dist/core/live-scanner/scanner.js.map +1 -1
- package/dist/core/live-scanner/step-replayer.d.ts +1 -1
- package/dist/core/live-scanner/step-replayer.d.ts.map +1 -1
- package/dist/core/live-scanner/step-replayer.js +107 -6
- package/dist/core/live-scanner/step-replayer.js.map +1 -1
- package/dist/core/live-scanner/types.d.ts +1 -0
- package/dist/core/live-scanner/types.d.ts.map +1 -1
- package/dist/core/validator/selector-validator.d.ts.map +1 -1
- package/dist/core/validator/selector-validator.js +2 -1
- package/dist/core/validator/selector-validator.js.map +1 -1
- package/dist/generators/scaffold-generator/index.d.ts +2 -1
- package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
- package/dist/generators/scaffold-generator/index.js +21 -1
- package/dist/generators/scaffold-generator/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +95 -58
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -2
- package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.js +34 -47
- package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +3 -1
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +20 -3
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +44 -85
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/legacy-patterns.d.ts +7 -0
- package/dist/generators/test-generator/patterns/legacy-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/legacy-patterns.js +98 -0
- package/dist/generators/test-generator/patterns/legacy-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -2
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js +14 -42
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -1
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/setup-patterns.js +23 -35
- package/dist/generators/test-generator/patterns/setup-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +18 -3
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +0 -15
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +4 -106
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/{executor/test-generator.d.ts → generators/test-generator/types.d.ts} +4 -25
- package/dist/generators/test-generator/types.d.ts.map +1 -0
- package/dist/generators/test-generator/types.js +106 -0
- package/dist/generators/test-generator/types.js.map +1 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +8 -17
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +10 -18
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/orchestrator/cache-manager.d.ts +1 -23
- package/dist/orchestrator/cache-manager.d.ts.map +1 -1
- package/dist/orchestrator/cache-manager.js +1 -87
- package/dist/orchestrator/cache-manager.js.map +1 -1
- package/dist/orchestrator/pipeline.d.ts +11 -28
- package/dist/orchestrator/pipeline.d.ts.map +1 -1
- package/dist/orchestrator/pipeline.js +52 -371
- package/dist/orchestrator/pipeline.js.map +1 -1
- package/dist/orchestrator/reporter.d.ts +1 -1
- package/dist/orchestrator/reporter.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +1 -1
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/utils/feature-finder.d.ts +9 -0
- package/dist/utils/feature-finder.d.ts.map +1 -0
- package/dist/utils/feature-finder.js +67 -0
- package/dist/utils/feature-finder.js.map +1 -0
- package/dist/utils/screen-paths.d.ts +10 -0
- package/dist/utils/screen-paths.d.ts.map +1 -0
- package/dist/utils/screen-paths.js +73 -0
- package/dist/utils/screen-paths.js.map +1 -0
- package/dist/utils/selector-loader.d.ts +6 -0
- package/dist/utils/selector-loader.d.ts.map +1 -0
- package/dist/utils/selector-loader.js +20 -0
- package/dist/utils/selector-loader.js.map +1 -0
- package/dist/utils/selector-types.d.ts +7 -0
- package/dist/utils/selector-types.d.ts.map +1 -0
- package/dist/utils/selector-types.js +19 -0
- package/dist/utils/selector-types.js.map +1 -0
- package/dist/utils/test-data-loader.d.ts +6 -0
- package/dist/utils/test-data-loader.d.ts.map +1 -0
- package/dist/utils/test-data-loader.js +20 -0
- package/dist/utils/test-data-loader.js.map +1 -0
- package/dist/utils/yaml-io.d.ts +14 -0
- package/dist/utils/yaml-io.d.ts.map +1 -0
- package/dist/utils/yaml-io.js +72 -0
- package/dist/utils/yaml-io.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/commands/add.ts +25 -0
- package/src/cli/commands/cache-clear.ts +22 -0
- package/src/cli/commands/full.ts +35 -0
- package/src/cli/commands/generate.ts +55 -0
- package/src/cli/commands/init.ts +17 -0
- package/src/cli/commands/{live-scan-command.ts → live-scan.ts} +21 -18
- package/src/cli/commands/makeauth.ts +77 -0
- package/src/cli/commands/map.ts +97 -0
- package/src/cli/commands/validate.ts +43 -0
- package/src/cli/index.ts +32 -473
- package/src/cli/types.ts +9 -0
- package/src/cli/utils.ts +106 -0
- package/src/core/live-scanner/matrix-reader.ts +2 -8
- package/src/core/live-scanner/scanner.ts +27 -4
- package/src/core/live-scanner/step-replayer.ts +92 -6
- package/src/core/live-scanner/types.ts +1 -0
- package/src/core/validator/selector-validator.ts +3 -2
- package/src/generators/scaffold-generator/index.ts +23 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
- package/src/generators/test-generator/patterns/assertion-patterns.ts +102 -62
- package/src/generators/test-generator/patterns/form-patterns.ts +38 -60
- package/src/generators/test-generator/patterns/index.ts +22 -4
- package/src/generators/test-generator/patterns/interaction-patterns.ts +47 -107
- package/src/generators/test-generator/patterns/legacy-patterns.ts +104 -0
- package/src/generators/test-generator/patterns/navigation-patterns.ts +27 -69
- package/src/generators/test-generator/patterns/setup-patterns.ts +23 -41
- package/src/generators/test-generator/patterns/types.ts +26 -9
- package/src/generators/test-generator/step-mapper.ts +4 -124
- package/src/generators/test-generator/types.ts +131 -0
- package/src/generators/test-generator/utils/data-resolver.ts +8 -13
- package/src/generators/test-generator/utils/selector-resolver.ts +15 -17
- package/src/orchestrator/cache-manager.ts +1 -107
- package/src/orchestrator/pipeline.ts +58 -433
- package/src/orchestrator/reporter.ts +1 -1
- package/src/orchestrator/screen-manager.ts +1 -1
- package/src/utils/feature-finder.ts +33 -0
- package/src/utils/screen-paths.ts +37 -0
- package/src/utils/selector-loader.ts +23 -0
- package/src/utils/selector-types.ts +17 -0
- package/src/utils/test-data-loader.ts +23 -0
- package/src/utils/yaml-io.ts +33 -0
- package/dist/cli/commands/auto-tag-command.d.ts +0 -8
- package/dist/cli/commands/auto-tag-command.d.ts.map +0 -1
- package/dist/cli/commands/auto-tag-command.js +0 -104
- package/dist/cli/commands/auto-tag-command.js.map +0 -1
- package/dist/cli/commands/live-scan-command.d.ts +0 -9
- package/dist/cli/commands/live-scan-command.d.ts.map +0 -1
- package/dist/cli/commands/live-scan-command.js.map +0 -1
- package/dist/executor/playwright/playwright-generator.d.ts +0 -33
- package/dist/executor/playwright/playwright-generator.d.ts.map +0 -1
- package/dist/executor/playwright/playwright-generator.js +0 -136
- package/dist/executor/playwright/playwright-generator.js.map +0 -1
- package/dist/executor/test-generator.d.ts.map +0 -1
- package/dist/executor/test-generator.js +0 -30
- package/dist/executor/test-generator.js.map +0 -1
- package/dist/generators/cli.d.ts +0 -7
- package/dist/generators/cli.d.ts.map +0 -1
- package/dist/generators/cli.js +0 -570
- package/dist/generators/cli.js.map +0 -1
- package/dist/input/cli-adapter.d.ts +0 -75
- package/dist/input/cli-adapter.d.ts.map +0 -1
- package/dist/input/cli-adapter.js +0 -218
- package/dist/input/cli-adapter.js.map +0 -1
- package/dist/input/config-adapter.d.ts +0 -25
- package/dist/input/config-adapter.d.ts.map +0 -1
- package/dist/input/config-adapter.js +0 -70
- package/dist/input/config-adapter.js.map +0 -1
- package/dist/input/input-adapter.d.ts +0 -28
- package/dist/input/input-adapter.d.ts.map +0 -1
- package/dist/input/input-adapter.js +0 -17
- package/dist/input/input-adapter.js.map +0 -1
- package/dist/input/vscode-adapter.d.ts +0 -62
- package/dist/input/vscode-adapter.d.ts.map +0 -1
- package/dist/input/vscode-adapter.js +0 -64
- package/dist/input/vscode-adapter.js.map +0 -1
- package/dist/tools/auto-tagger.d.ts +0 -107
- package/dist/tools/auto-tagger.d.ts.map +0 -1
- package/dist/tools/auto-tagger.js +0 -502
- package/dist/tools/auto-tagger.js.map +0 -1
- package/src/cli/commands/auto-tag-command.ts +0 -80
- package/src/executor/playwright/playwright-generator.ts +0 -125
- package/src/executor/test-generator.ts +0 -90
- package/src/generators/cli.ts +0 -640
- package/src/input/cli-adapter.ts +0 -233
- package/src/input/config-adapter.ts +0 -71
- package/src/input/input-adapter.ts +0 -32
- package/src/input/vscode-adapter.ts +0 -90
- package/src/tools/auto-tagger.ts +0 -572
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pipeline Orchestrator
|
|
3
|
-
* Coordinates
|
|
3
|
+
* Coordinates test generation: Live-Scan → Scaffold → Code Generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import * as fs from 'fs';
|
|
8
|
-
import yaml from 'yaml';
|
|
9
8
|
import { RuntimeConfig } from '../config/config-schema';
|
|
10
|
-
import {
|
|
11
|
-
import { SelectorBaseGenerator, SelectorBase } from '../core/selector-base/base-generator';
|
|
12
|
-
import { AIProviderFactory } from '../external/ai-provider';
|
|
13
|
-
import { TestGeneratorFactory, GeneratedTest } from '../executor/test-generator';
|
|
9
|
+
import { TestGeneratorFactory, GeneratedTest } from '../generators/test-generator/types';
|
|
14
10
|
import { CacheManager } from './cache-manager';
|
|
15
11
|
import { Reporter } from './reporter';
|
|
16
12
|
|
|
@@ -25,334 +21,6 @@ export class Pipeline {
|
|
|
25
21
|
this.reporter = new Reporter(config);
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
/**
|
|
29
|
-
* Run UI discovery phase
|
|
30
|
-
* Scans React components and generates UI model
|
|
31
|
-
*/
|
|
32
|
-
async runDiscovery(screenName: string): Promise<UIModel> {
|
|
33
|
-
const strictMode = this.config.scanner.strictMode || false;
|
|
34
|
-
const mode = strictMode ? 'strict' : 'standard';
|
|
35
|
-
|
|
36
|
-
// Debug logging
|
|
37
|
-
if (this.config.verbose) {
|
|
38
|
-
console.log(`[DEBUG] strictMode value: ${this.config.scanner.strictMode}`);
|
|
39
|
-
console.log(`[DEBUG] scanner config:`, JSON.stringify(this.config.scanner, null, 2));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log(`\n🔍 Discovering UI elements for screen: ${screenName} (${mode} mode)\n`);
|
|
43
|
-
|
|
44
|
-
// Check cache
|
|
45
|
-
if (!this.config.force && this.config.cache.enabled) {
|
|
46
|
-
const cached = await this.cacheManager.getUIModel(screenName);
|
|
47
|
-
if (cached) {
|
|
48
|
-
console.log(`✓ Loaded from cache (${cached.elements.length} elements)\n`);
|
|
49
|
-
return cached;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let uiModel: UIModel;
|
|
54
|
-
|
|
55
|
-
// Use StrictScanner if strict mode enabled (v3.1)
|
|
56
|
-
if (strictMode) {
|
|
57
|
-
const { StrictScanner } = require('../core/ui-scanner/strict-scanner');
|
|
58
|
-
|
|
59
|
-
const strictConfig = {
|
|
60
|
-
sourceRoot: this.config.paths.sourceRoot,
|
|
61
|
-
projectRoot: process.cwd(),
|
|
62
|
-
strictMode: true,
|
|
63
|
-
filterRules: this.config.scanner.filterRules || {
|
|
64
|
-
allowedTags: ['input', 'button', 'a', 'select', 'textarea'],
|
|
65
|
-
requireAttributes: ['data-testid', 'role'],
|
|
66
|
-
allowGenericWithId: true,
|
|
67
|
-
},
|
|
68
|
-
verbose: this.config.verbose || false,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const strictScanner = new StrictScanner(strictConfig);
|
|
72
|
-
const strictResult = await strictScanner.scan(screenName);
|
|
73
|
-
|
|
74
|
-
// Convert StrictScanResult to UIModel format
|
|
75
|
-
uiModel = {
|
|
76
|
-
screen: strictResult.screen,
|
|
77
|
-
timestamp: strictResult.timestamp,
|
|
78
|
-
framework: strictResult.framework,
|
|
79
|
-
sourceFiles: strictResult.sourceFiles,
|
|
80
|
-
elements: strictResult.elements.map(el => ({
|
|
81
|
-
id: el.logicalName,
|
|
82
|
-
type: el.tag as any,
|
|
83
|
-
selector: el.primaryLocator,
|
|
84
|
-
label: el.label,
|
|
85
|
-
placeholder: el.placeholder,
|
|
86
|
-
role: el.role,
|
|
87
|
-
testId: el.props['data-testid'],
|
|
88
|
-
context: {
|
|
89
|
-
component: path.basename(el.sourceFile),
|
|
90
|
-
},
|
|
91
|
-
})),
|
|
92
|
-
stats: {
|
|
93
|
-
totalComponents: 1,
|
|
94
|
-
totalElements: strictResult.stats.totalIncluded,
|
|
95
|
-
maxDepth: 0,
|
|
96
|
-
scanDuration: strictResult.stats.scanDuration,
|
|
97
|
-
filesScanned: strictResult.sourceFiles.length,
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
console.log(` Filtered: ${strictResult.stats.totalFiltered} elements (${strictResult.stats.filterRate.toFixed(1)}%)`);
|
|
102
|
-
console.log(` Direct ID: ${strictResult.stats.directIdMatches} elements`);
|
|
103
|
-
} else {
|
|
104
|
-
// Use standard scanner (old approach)
|
|
105
|
-
const scanner = ScannerFactory.create(this.config.scanner.framework, this.config);
|
|
106
|
-
uiModel = await scanner.scan(screenName, this.config);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Save UI model
|
|
110
|
-
await this.saveUIModel(uiModel);
|
|
111
|
-
|
|
112
|
-
// Cache UI model
|
|
113
|
-
if (this.config.cache.enabled) {
|
|
114
|
-
await this.cacheManager.saveUIModel(screenName, uiModel);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
console.log(`✓ Discovered ${uiModel.elements.length} elements in ${uiModel.stats.scanDuration}ms\n`);
|
|
118
|
-
|
|
119
|
-
return uiModel;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Run mapping for single screen or all screens
|
|
124
|
-
*/
|
|
125
|
-
async runMapping(screenName?: string): Promise<void> {
|
|
126
|
-
if (screenName) {
|
|
127
|
-
// Map specific screen
|
|
128
|
-
await this.runSelectorMapping(screenName);
|
|
129
|
-
} else {
|
|
130
|
-
// Map all screens discovered from Gherkin
|
|
131
|
-
const screens = await this.discoverScreensFromGherkin();
|
|
132
|
-
|
|
133
|
-
if (screens.length === 0) {
|
|
134
|
-
console.warn('⚠️ No screens found in Gherkin features');
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log(`📋 Found ${screens.length} screen(s): ${screens.join(', ')}\n`);
|
|
139
|
-
|
|
140
|
-
for (const screen of screens) {
|
|
141
|
-
await this.runSelectorMapping(screen);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Run selector mapping phase
|
|
148
|
-
* Generates selector base and maps with AI or Hybrid Mapper
|
|
149
|
-
*/
|
|
150
|
-
async runSelectorMapping(screenName: string): Promise<SelectorBase> {
|
|
151
|
-
const mode = this.config.selectorMapping.mode || 'hybrid';
|
|
152
|
-
console.log(`\n🎯 Mapping selectors for screen: ${screenName} (mode: ${mode})\n`);
|
|
153
|
-
|
|
154
|
-
// Check cache
|
|
155
|
-
if (!this.config.force && this.config.cache.enabled) {
|
|
156
|
-
const cached = await this.cacheManager.getSelectors(screenName);
|
|
157
|
-
if (cached) {
|
|
158
|
-
console.log(`✓ Loaded from cache (${Object.keys(cached.elements).length} selectors)\n`);
|
|
159
|
-
return cached;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Generate selector base from Gherkin
|
|
164
|
-
const baseGenerator = new SelectorBaseGenerator(this.config);
|
|
165
|
-
const selectorBase = await baseGenerator.generate(screenName);
|
|
166
|
-
|
|
167
|
-
console.log(` Generated selector base with ${selectorBase.metadata.totalElements} elements`);
|
|
168
|
-
|
|
169
|
-
// Load UI model (needed for mapping)
|
|
170
|
-
const uiModel = await this.loadUIModel(screenName);
|
|
171
|
-
if (!uiModel) {
|
|
172
|
-
throw new Error(`UI model not found for screen: ${screenName}. Run discovery first.`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Map selectors based on mode/strategy
|
|
176
|
-
let mappedSelectors: SelectorBase;
|
|
177
|
-
|
|
178
|
-
// v3.1: Use PriorityMapper if strategy is priority-waterfall
|
|
179
|
-
const strategy = this.config.selectorMapping.strategy;
|
|
180
|
-
if (strategy === 'priority-waterfall') {
|
|
181
|
-
const { PriorityMapper } = require('../core/selector-mapper/priority-mapper');
|
|
182
|
-
|
|
183
|
-
const mapperConfig = {
|
|
184
|
-
strategy: 'priority-waterfall' as const,
|
|
185
|
-
priorities: this.config.selectorMapping.priorities || ['direct-id', 'accessibility', 'heuristic-ai'],
|
|
186
|
-
confidenceThreshold: this.config.selectorMapping.confidenceThreshold || 0.5,
|
|
187
|
-
aiProvider: this.config.selectorMapping.aiProvider,
|
|
188
|
-
model: this.config.selectorMapping.model,
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const priorityMapper = new PriorityMapper(mapperConfig);
|
|
192
|
-
|
|
193
|
-
// Extract element IDs from selector base
|
|
194
|
-
const elementIds = Object.keys(selectorBase.elements);
|
|
195
|
-
|
|
196
|
-
// Convert UI model to StrictScanResult format (compatible)
|
|
197
|
-
const strictUIModel = {
|
|
198
|
-
screen: uiModel.screen,
|
|
199
|
-
timestamp: uiModel.timestamp,
|
|
200
|
-
framework: uiModel.framework,
|
|
201
|
-
sourceFiles: uiModel.sourceFiles,
|
|
202
|
-
elements: uiModel.elements.map(el => {
|
|
203
|
-
// Check if selector contains data-testid
|
|
204
|
-
const hasTestId = el.selector.includes('data-testid');
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
logicalName: el.id,
|
|
208
|
-
primaryLocator: el.selector,
|
|
209
|
-
fallbackStrategy: el.selector,
|
|
210
|
-
tag: el.type,
|
|
211
|
-
elementType: el.type,
|
|
212
|
-
discoveryMethod: hasTestId ? 'direct-id' : 'heuristic' as const,
|
|
213
|
-
confidence: hasTestId ? 0.95 : 0.5,
|
|
214
|
-
sourceFile: uiModel.sourceFiles[0] || '',
|
|
215
|
-
label: el.label,
|
|
216
|
-
placeholder: el.placeholder,
|
|
217
|
-
ariaLabel: undefined,
|
|
218
|
-
role: el.role,
|
|
219
|
-
text: undefined,
|
|
220
|
-
props: {},
|
|
221
|
-
};
|
|
222
|
-
}),
|
|
223
|
-
stats: {
|
|
224
|
-
totalScanned: uiModel.stats.totalElements,
|
|
225
|
-
totalFiltered: 0,
|
|
226
|
-
totalIncluded: uiModel.stats.totalElements,
|
|
227
|
-
filterRate: 0,
|
|
228
|
-
directIdMatches: uiModel.elements.filter(el => el.selector.includes('data-testid')).length,
|
|
229
|
-
accessibilityMatches: 0,
|
|
230
|
-
heuristicMatches: uiModel.elements.filter(el => !el.selector.includes('data-testid')).length,
|
|
231
|
-
scanDuration: uiModel.stats.scanDuration,
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
// Map elements using priority waterfall
|
|
236
|
-
const mappingRequests = elementIds.map(id => ({
|
|
237
|
-
elementId: `[${screenName}.${id}]`,
|
|
238
|
-
screenName,
|
|
239
|
-
uiModel: strictUIModel,
|
|
240
|
-
}));
|
|
241
|
-
|
|
242
|
-
const results = await priorityMapper.mapElements(mappingRequests);
|
|
243
|
-
|
|
244
|
-
// Convert results back to SelectorBase format
|
|
245
|
-
mappedSelectors = { ...selectorBase };
|
|
246
|
-
results.forEach(result => {
|
|
247
|
-
// result.elementId is already just the element name (e.g., "email-input")
|
|
248
|
-
const elementId = result.elementId;
|
|
249
|
-
|
|
250
|
-
if (this.config.verbose) {
|
|
251
|
-
console.log(`[DEBUG] Mapping result: ${result.elementId} → ${result.selector} (confidence: ${result.confidence})`);
|
|
252
|
-
console.log(`[DEBUG] Extracted elementId: ${elementId}`);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (elementId && result.confidence > 0 && selectorBase.elements[elementId]) {
|
|
256
|
-
mappedSelectors.elements[elementId] = {
|
|
257
|
-
...selectorBase.elements[elementId],
|
|
258
|
-
selector: result.selector,
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
if (this.config.verbose) {
|
|
262
|
-
console.log(`[DEBUG] Updated selector for ${elementId}: ${mappedSelectors.elements[elementId].selector}`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Show statistics
|
|
268
|
-
const stats = priorityMapper.getStats();
|
|
269
|
-
console.log(`\n📊 Priority Mapping Statistics:`);
|
|
270
|
-
console.log(` Total elements: ${stats.totalMappings}`);
|
|
271
|
-
console.log(` Successful: ${stats.successfulMappings} (${((stats.successfulMappings / stats.totalMappings) * 100).toFixed(1)}%)`);
|
|
272
|
-
console.log(` Direct ID: ${stats.directIdMatches} (${((stats.directIdMatches / stats.totalMappings) * 100).toFixed(1)}%)`);
|
|
273
|
-
console.log(` Accessibility: ${stats.accessibilityMatches}`);
|
|
274
|
-
console.log(` Heuristic: ${stats.heuristicMatches}`);
|
|
275
|
-
console.log(` AI: ${stats.aiMatches}`);
|
|
276
|
-
console.log(` Avg time: ${stats.averageMappingTime.toFixed(2)}ms`);
|
|
277
|
-
console.log(` Cost: $${stats.estimatedCost.toFixed(4)}\n`);
|
|
278
|
-
} else if (mode === 'hybrid') {
|
|
279
|
-
// Use Hybrid Mapper (heuristics + AI fallback)
|
|
280
|
-
const { HybridMapper } = require('../generators/selector-mapper/hybrid-mapper');
|
|
281
|
-
const hybridMapper = new HybridMapper({
|
|
282
|
-
provider: this.config.selectorMapping.aiProvider,
|
|
283
|
-
model: this.config.selectorMapping.model,
|
|
284
|
-
apiKey: process.env[this.config.apis.anthropic.apiKeyEnv],
|
|
285
|
-
heuristicFirst: this.config.selectorMapping.heuristicFirst,
|
|
286
|
-
minHeuristicConfidence: this.config.selectorMapping.minHeuristicConfidence,
|
|
287
|
-
verbose: this.config.verbose
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Extract element IDs from selector base
|
|
291
|
-
const elementIds = Object.keys(selectorBase.elements).map(id => `[${screenName}.${id}]`);
|
|
292
|
-
|
|
293
|
-
// Map using hybrid approach
|
|
294
|
-
const selectorDSL = await hybridMapper.mapSelectors(uiModel, elementIds);
|
|
295
|
-
|
|
296
|
-
// Convert SelectorDSL back to SelectorBase format
|
|
297
|
-
// selectorDSL is a flat object { elementId: SelectorEntry }
|
|
298
|
-
mappedSelectors = {
|
|
299
|
-
...selectorBase,
|
|
300
|
-
elements: selectorDSL
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// Show statistics if verbose
|
|
304
|
-
if (this.config.verbose) {
|
|
305
|
-
const stats = hybridMapper.getStats();
|
|
306
|
-
console.log(`\n📊 Mapping Statistics:`);
|
|
307
|
-
console.log(` Total elements: ${stats.total}`);
|
|
308
|
-
console.log(` Heuristic matches: ${stats.heuristicMatches} (${stats.heuristicSuccessRate.toFixed(1)}%)`);
|
|
309
|
-
console.log(` AI fallback: ${stats.aiMatches} (${(100 - stats.heuristicSuccessRate).toFixed(1)}%)`);
|
|
310
|
-
console.log(` Estimated cost: $${stats.estimatedCost.toFixed(4)}`);
|
|
311
|
-
console.log(` Estimated time: ${stats.estimatedTime}ms`);
|
|
312
|
-
console.log(` Savings vs AI-only: ~${(90 * stats.heuristicSuccessRate / 100).toFixed(0)}%\n`);
|
|
313
|
-
}
|
|
314
|
-
} else if (mode === 'ai-only') {
|
|
315
|
-
// Use AI mapper only (legacy behavior)
|
|
316
|
-
const aiProvider = AIProviderFactory.create(this.config.selectorMapping.aiProvider, this.config);
|
|
317
|
-
mappedSelectors = await aiProvider.mapSelectors(uiModel, selectorBase, this.config);
|
|
318
|
-
} else if (mode === 'heuristic-only') {
|
|
319
|
-
// Use heuristics only (no AI fallback)
|
|
320
|
-
const { HeuristicMatcher } = require('../generators/selector-mapper/hybrid-mapper');
|
|
321
|
-
|
|
322
|
-
mappedSelectors = { ...selectorBase };
|
|
323
|
-
|
|
324
|
-
for (const [elementId, element] of Object.entries(selectorBase.elements)) {
|
|
325
|
-
const match = HeuristicMatcher.match(elementId, uiModel.elements);
|
|
326
|
-
|
|
327
|
-
if (match.confidence > 0) {
|
|
328
|
-
mappedSelectors.elements[elementId] = {
|
|
329
|
-
...element,
|
|
330
|
-
selector: match.selector
|
|
331
|
-
};
|
|
332
|
-
} else {
|
|
333
|
-
console.warn(` ⚠️ No heuristic match for: ${elementId}`);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
} else {
|
|
337
|
-
throw new Error(`Invalid selector mapping mode: ${mode}`);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Apply overrides
|
|
341
|
-
const finalSelectors = await this.applyOverrides(screenName, mappedSelectors);
|
|
342
|
-
|
|
343
|
-
// Save selectors
|
|
344
|
-
await baseGenerator.save(finalSelectors);
|
|
345
|
-
|
|
346
|
-
// Cache selectors
|
|
347
|
-
if (this.config.cache.enabled) {
|
|
348
|
-
await this.cacheManager.saveSelectors(screenName, finalSelectors);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
console.log(`✓ Mapped ${Object.keys(finalSelectors.elements).length} selectors\n`);
|
|
352
|
-
|
|
353
|
-
return finalSelectors;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
24
|
/**
|
|
357
25
|
* Run test generation phase
|
|
358
26
|
* Generates Playwright test scripts from Gherkin
|
|
@@ -362,10 +30,10 @@ export class Pipeline {
|
|
|
362
30
|
console.log(`\n${header}\n`);
|
|
363
31
|
|
|
364
32
|
// Find all feature files from both old and new structure
|
|
365
|
-
const featureFiles = screenName
|
|
33
|
+
const featureFiles = screenName
|
|
366
34
|
? this.findFeatureFilesForScreen(screenName)
|
|
367
35
|
: this.findAllFeatureFiles();
|
|
368
|
-
|
|
36
|
+
|
|
369
37
|
if (screenName && featureFiles.length === 0) {
|
|
370
38
|
throw new Error(
|
|
371
39
|
`No feature files found for screen: ${screenName}\n` +
|
|
@@ -384,7 +52,7 @@ export class Pipeline {
|
|
|
384
52
|
if (screenName) {
|
|
385
53
|
const validation = this.validateSelectorFiles(screenName, features);
|
|
386
54
|
const missing = validation.filter(v => !v.selectorExists);
|
|
387
|
-
|
|
55
|
+
|
|
388
56
|
if (missing.length > 0) {
|
|
389
57
|
const missingList = missing.map(v => ` ✗ ${v.featureName}.feature → selector file not found`).join('\n');
|
|
390
58
|
throw new Error(
|
|
@@ -412,7 +80,7 @@ export class Pipeline {
|
|
|
412
80
|
const fileName = path.basename(result.outputPath);
|
|
413
81
|
const relativePath = path.relative(process.cwd(), result.outputPath);
|
|
414
82
|
const stepInfo = result.stats?.totalSteps ? ` (${result.stats.totalSteps} steps)` : '';
|
|
415
|
-
|
|
83
|
+
|
|
416
84
|
console.log(`✓ Generated ${fileName}`);
|
|
417
85
|
console.log(` → ${relativePath}${stepInfo}\n`);
|
|
418
86
|
} catch (error) {
|
|
@@ -455,11 +123,11 @@ export class Pipeline {
|
|
|
455
123
|
selectorExists: boolean;
|
|
456
124
|
selectorPath: string | null;
|
|
457
125
|
}> = [];
|
|
458
|
-
|
|
126
|
+
|
|
459
127
|
for (const feature of features) {
|
|
460
128
|
const featureName = this.extractFeatureName(feature);
|
|
461
129
|
const selectorPath = this.findSelectorFile(screenName, featureName);
|
|
462
|
-
|
|
130
|
+
|
|
463
131
|
results.push({
|
|
464
132
|
featureName,
|
|
465
133
|
sourceFile: feature.sourceFile,
|
|
@@ -467,7 +135,7 @@ export class Pipeline {
|
|
|
467
135
|
selectorPath,
|
|
468
136
|
});
|
|
469
137
|
}
|
|
470
|
-
|
|
138
|
+
|
|
471
139
|
return results;
|
|
472
140
|
}
|
|
473
141
|
|
|
@@ -484,7 +152,6 @@ export class Pipeline {
|
|
|
484
152
|
|
|
485
153
|
/**
|
|
486
154
|
* Find selector file path for a feature
|
|
487
|
-
* Searches both old and new directory structures
|
|
488
155
|
*/
|
|
489
156
|
private findSelectorFile(screenName: string, featureName: string): string | null {
|
|
490
157
|
const possiblePaths: string[] = [];
|
|
@@ -510,7 +177,6 @@ export class Pipeline {
|
|
|
510
177
|
);
|
|
511
178
|
possiblePaths.push(oldStructurePath);
|
|
512
179
|
|
|
513
|
-
// Find first existing file
|
|
514
180
|
for (const p of possiblePaths) {
|
|
515
181
|
if (fs.existsSync(p)) {
|
|
516
182
|
return p;
|
|
@@ -521,10 +187,12 @@ export class Pipeline {
|
|
|
521
187
|
}
|
|
522
188
|
|
|
523
189
|
/**
|
|
524
|
-
* Run full pipeline
|
|
525
|
-
* Discovery → Selector Mapping → Test Generation
|
|
190
|
+
* Run full pipeline: Live-Scan → Scaffold → Test Generation
|
|
526
191
|
*/
|
|
527
|
-
async runFull(
|
|
192
|
+
async runFull(
|
|
193
|
+
screens?: string[],
|
|
194
|
+
options?: { headed?: boolean; authDir?: string; skipLiveScan?: boolean; force?: boolean }
|
|
195
|
+
): Promise<void> {
|
|
528
196
|
console.log('\n🚀 Running full pipeline\n');
|
|
529
197
|
|
|
530
198
|
// Discover screens if not specified
|
|
@@ -533,20 +201,43 @@ export class Pipeline {
|
|
|
533
201
|
console.log(` Found ${screens.length} screen(s) in Gherkin: ${screens.join(', ')}\n`);
|
|
534
202
|
}
|
|
535
203
|
|
|
536
|
-
|
|
537
|
-
console.log('📍 Phase 1: UI Discovery\n');
|
|
538
|
-
for (const screen of screens) {
|
|
539
|
-
await this.runDiscovery(screen);
|
|
540
|
-
}
|
|
204
|
+
const screensDir = 'qa/screens';
|
|
541
205
|
|
|
542
|
-
// Phase 2: Selector Mapping
|
|
543
|
-
console.log('📍 Phase 2: Selector Mapping\n');
|
|
544
206
|
for (const screen of screens) {
|
|
545
|
-
|
|
207
|
+
// Phase 1: Live-scan
|
|
208
|
+
if (!options?.skipLiveScan) {
|
|
209
|
+
console.log(`📍 Phase 1: Live-Scan — ${screen}\n`);
|
|
210
|
+
const { LiveScanner } = require('../core/live-scanner');
|
|
211
|
+
const scanner = new LiveScanner({
|
|
212
|
+
screenName: screen,
|
|
213
|
+
screensDir,
|
|
214
|
+
headed: options?.headed,
|
|
215
|
+
authDir: options?.authDir || 'specs/.auth',
|
|
216
|
+
force: options?.force,
|
|
217
|
+
});
|
|
218
|
+
await scanner.scan();
|
|
219
|
+
console.log('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Phase 2: Scaffold (map)
|
|
223
|
+
console.log(`📍 Phase 2: Map — ${screen}\n`);
|
|
224
|
+
const { ScaffoldGenerator } = require('../generators/scaffold-generator');
|
|
225
|
+
const generator = new ScaffoldGenerator();
|
|
226
|
+
const results = generator.processScreen(screen, screensDir, options?.force || false);
|
|
227
|
+
|
|
228
|
+
for (const result of results) {
|
|
229
|
+
console.log(`✓ Mapped ${result.featureFile}.feature`);
|
|
230
|
+
if (result.selectorsSkipped) {
|
|
231
|
+
console.log(` ⊘ ${path.basename(result.selectorsPath)} (skipped - already exists)`);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(` → ${path.basename(result.selectorsPath)} (${result.elementCount} elements)`);
|
|
234
|
+
}
|
|
235
|
+
console.log('');
|
|
236
|
+
}
|
|
546
237
|
}
|
|
547
238
|
|
|
548
239
|
// Phase 3: Test Generation
|
|
549
|
-
console.log('📍 Phase 3:
|
|
240
|
+
console.log('📍 Phase 3: Generate Tests\n');
|
|
550
241
|
await this.runTestGeneration();
|
|
551
242
|
|
|
552
243
|
console.log('✅ Full pipeline completed!\n');
|
|
@@ -564,65 +255,6 @@ export class Pipeline {
|
|
|
564
255
|
// Helper Methods
|
|
565
256
|
// ============================================================================
|
|
566
257
|
|
|
567
|
-
private async saveUIModel(uiModel: UIModel): Promise<void> {
|
|
568
|
-
const outputDir = path.join(this.config.paths.uiModelsDir, 'screens');
|
|
569
|
-
if (!fs.existsSync(outputDir)) {
|
|
570
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const outputPath = path.join(outputDir, `${uiModel.screen}.json`);
|
|
574
|
-
fs.writeFileSync(outputPath, JSON.stringify(uiModel, null, 2), 'utf-8');
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
private async loadUIModel(screenName: string): Promise<UIModel | null> {
|
|
578
|
-
const filePath = path.join(this.config.paths.uiModelsDir, 'screens', `${screenName}.json`);
|
|
579
|
-
|
|
580
|
-
if (!fs.existsSync(filePath)) {
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
try {
|
|
585
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
586
|
-
return JSON.parse(content);
|
|
587
|
-
} catch {
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
private async applyOverrides(screenName: string, selectors: SelectorBase): Promise<SelectorBase> {
|
|
593
|
-
const overridePath = path.join(
|
|
594
|
-
this.config.paths.selectorsDir,
|
|
595
|
-
'screens',
|
|
596
|
-
`${screenName}.override.yaml`
|
|
597
|
-
);
|
|
598
|
-
|
|
599
|
-
if (!fs.existsSync(overridePath)) {
|
|
600
|
-
return selectors;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
try {
|
|
604
|
-
const content = fs.readFileSync(overridePath, 'utf-8');
|
|
605
|
-
const overrides = yaml.parse(content);
|
|
606
|
-
|
|
607
|
-
// Merge overrides (override values replace AI values)
|
|
608
|
-
if (overrides.elements) {
|
|
609
|
-
for (const [elementId, overrideValue] of Object.entries(overrides.elements)) {
|
|
610
|
-
if (selectors.elements[elementId]) {
|
|
611
|
-
Object.assign(selectors.elements[elementId], overrideValue);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (this.config.verbose) {
|
|
617
|
-
console.log(` Applied overrides from ${path.basename(overridePath)}`);
|
|
618
|
-
}
|
|
619
|
-
} catch (error) {
|
|
620
|
-
console.warn(` Warning: Failed to apply overrides: ${error}`);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
return selectors;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
258
|
private findFeatureFiles(dir: string): string[] {
|
|
627
259
|
const files: string[] = [];
|
|
628
260
|
|
|
@@ -677,8 +309,6 @@ export class Pipeline {
|
|
|
677
309
|
|
|
678
310
|
/**
|
|
679
311
|
* Find all feature files from both old and new folder structures
|
|
680
|
-
* Old: qa/features/*.feature
|
|
681
|
-
* New: qa/screens/*/features/*.feature
|
|
682
312
|
*/
|
|
683
313
|
private findAllFeatureFiles(): string[] {
|
|
684
314
|
const allFiles: string[] = [];
|
|
@@ -704,26 +334,21 @@ export class Pipeline {
|
|
|
704
334
|
}
|
|
705
335
|
|
|
706
336
|
/**
|
|
707
|
-
* Discover all unique screens
|
|
337
|
+
* Discover all unique screens from qa/screens/ directory
|
|
708
338
|
*/
|
|
709
339
|
private async discoverScreensFromGherkin(): Promise<string[]> {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
// Regex to match [screen.element] pattern
|
|
714
|
-
const selectorPattern = /\[([a-z0-9-]+)\.([a-z0-9-]+)\]/gi;
|
|
715
|
-
|
|
716
|
-
for (const featureFile of featureFiles) {
|
|
717
|
-
const content = fs.readFileSync(featureFile, 'utf-8');
|
|
718
|
-
|
|
719
|
-
// Extract all [screen.element] references
|
|
720
|
-
let match;
|
|
721
|
-
while ((match = selectorPattern.exec(content)) !== null) {
|
|
722
|
-
const screenName = match[1];
|
|
723
|
-
screens.add(screenName);
|
|
724
|
-
}
|
|
340
|
+
const screensDir = path.join(process.cwd(), 'qa', 'screens');
|
|
341
|
+
if (!fs.existsSync(screensDir)) {
|
|
342
|
+
return [];
|
|
725
343
|
}
|
|
726
344
|
|
|
727
|
-
return
|
|
345
|
+
return fs.readdirSync(screensDir, { withFileTypes: true })
|
|
346
|
+
.filter(entry => entry.isDirectory())
|
|
347
|
+
.filter(entry => {
|
|
348
|
+
const featuresDir = path.join(screensDir, entry.name, 'features');
|
|
349
|
+
return fs.existsSync(featuresDir);
|
|
350
|
+
})
|
|
351
|
+
.map(entry => entry.name)
|
|
352
|
+
.sort();
|
|
728
353
|
}
|
|
729
354
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { RuntimeConfig } from '../config/config-schema';
|
|
9
|
-
import { GeneratedTest } from '../
|
|
9
|
+
import { GeneratedTest } from '../generators/test-generator/types';
|
|
10
10
|
|
|
11
11
|
export class Reporter {
|
|
12
12
|
private config: RuntimeConfig;
|
|
@@ -182,7 +182,7 @@ export class ScreenManager {
|
|
|
182
182
|
|
|
183
183
|
@high
|
|
184
184
|
Scenario: Sample scenario for ${options.name}
|
|
185
|
-
Given User
|
|
185
|
+
Given User is on [${options.name}] page
|
|
186
186
|
When User click [element] button
|
|
187
187
|
Then User see [result] text with {{success}}
|
|
188
188
|
`;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { getFeaturesDir } from './screen-paths';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Find all .feature files for a specific screen.
|
|
7
|
+
*/
|
|
8
|
+
export function findFeatureFiles(screensRoot: string, screenName: string): string[] {
|
|
9
|
+
const featuresDir = getFeaturesDir(screensRoot, screenName);
|
|
10
|
+
if (!fs.existsSync(featuresDir)) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return fs.readdirSync(featuresDir)
|
|
14
|
+
.filter(f => f.endsWith('.feature'))
|
|
15
|
+
.map(f => path.join(featuresDir, f));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Find all .feature files across all screens.
|
|
20
|
+
*/
|
|
21
|
+
export function findAllFeatureFiles(screensRoot: string): string[] {
|
|
22
|
+
if (!fs.existsSync(screensRoot)) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const files: string[] = [];
|
|
26
|
+
const screens = fs.readdirSync(screensRoot).filter(d =>
|
|
27
|
+
fs.statSync(path.join(screensRoot, d)).isDirectory()
|
|
28
|
+
);
|
|
29
|
+
for (const screen of screens) {
|
|
30
|
+
files.push(...findFeatureFiles(screensRoot, screen));
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
|
|
3
|
+
export function getScreenDir(screensRoot: string, screenName: string): string {
|
|
4
|
+
return path.join(screensRoot, screenName);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getFeaturesDir(screensRoot: string, screenName: string): string {
|
|
8
|
+
return path.join(screensRoot, screenName, 'features');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getSelectorsDir(screensRoot: string, screenName: string): string {
|
|
12
|
+
return path.join(screensRoot, screenName, 'selectors');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getTestDataDir(screensRoot: string, screenName: string): string {
|
|
16
|
+
return path.join(screensRoot, screenName, 'test-data');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getSelectorBasePath(screensRoot: string, screenName: string, featureName: string): string {
|
|
20
|
+
return path.join(screensRoot, screenName, 'selectors', `${featureName}.yaml`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getSelectorOverridePath(screensRoot: string, screenName: string, featureName: string): string {
|
|
24
|
+
return path.join(screensRoot, screenName, 'selectors', `${featureName}.override.yaml`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getTestDataBasePath(screensRoot: string, screenName: string, featureName: string): string {
|
|
28
|
+
return path.join(screensRoot, screenName, 'test-data', `${featureName}.yaml`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getTestDataOverridePath(screensRoot: string, screenName: string, featureName: string): string {
|
|
32
|
+
return path.join(screensRoot, screenName, 'test-data', `${featureName}.override.yaml`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getLiveScanPath(screensRoot: string, screenName: string, featureName: string): string {
|
|
36
|
+
return path.join(screensRoot, screenName, 'selectors', `${featureName}.live-scan.yaml`);
|
|
37
|
+
}
|