@sun-asterisk/sungen 1.0.20 → 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/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} +8 -15
- 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/step-replayer.d.ts.map +1 -1
- package/dist/core/live-scanner/step-replayer.js +7 -0
- package/dist/core/live-scanner/step-replayer.js.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} +8 -17
- 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/step-replayer.ts +7 -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
package/src/generators/cli.ts
DELETED
|
@@ -1,640 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CLI for Selector DSL Generator
|
|
4
|
-
* Commands: discover, generate, validate, cache-clear, info
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { config as loadEnv } from 'dotenv';
|
|
8
|
-
import { Command } from 'commander';
|
|
9
|
-
import chalk from 'chalk';
|
|
10
|
-
import ora from 'ora';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as yaml from 'yaml';
|
|
14
|
-
|
|
15
|
-
// Load environment variables
|
|
16
|
-
loadEnv({ path: '.env.local' });
|
|
17
|
-
loadEnv(); // Also load .env if exists
|
|
18
|
-
|
|
19
|
-
// Import generators
|
|
20
|
-
import { ReactScanner, buildUIModel, discoverScreens } from './ui-model-builder/react-scanner';
|
|
21
|
-
import { AIMapper, mapSelectors } from './selector-mapper/ai-mapper';
|
|
22
|
-
import { writeDSL, loadDSL, validateDSL, createOverrideTemplate } from './dsl-writer';
|
|
23
|
-
import { CacheManager, createCacheManager } from './cache/cache-manager';
|
|
24
|
-
import { UIModel } from './types';
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Configuration
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
const PROJECT_ROOT = process.cwd();
|
|
31
|
-
const SOURCE_ROOT = path.join(PROJECT_ROOT, 'app'); // Next.js app directory
|
|
32
|
-
const UI_MODEL_PATH = path.join(PROJECT_ROOT, 'qa/ui-models');
|
|
33
|
-
const SELECTOR_PATH = path.join(PROJECT_ROOT, 'qa/selectors');
|
|
34
|
-
const CACHE_PATH = path.join(PROJECT_ROOT, 'qa/selectors/.cache');
|
|
35
|
-
|
|
36
|
-
const DEFAULT_CONFIG = {
|
|
37
|
-
sourceRoot: SOURCE_ROOT,
|
|
38
|
-
uiModelPath: UI_MODEL_PATH,
|
|
39
|
-
selectorPath: SELECTOR_PATH,
|
|
40
|
-
cachePath: CACHE_PATH,
|
|
41
|
-
framework: 'react-nextjs' as const,
|
|
42
|
-
aiProvider: (process.env.AI_PROVIDER || 'google') as 'anthropic' | 'google',
|
|
43
|
-
enableCache: true
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// ============================================================================
|
|
47
|
-
// CLI Program
|
|
48
|
-
// ============================================================================
|
|
49
|
-
|
|
50
|
-
const program = new Command();
|
|
51
|
-
|
|
52
|
-
program
|
|
53
|
-
.name('qa-generator')
|
|
54
|
-
.description('AI-Native QA Selector DSL Generator')
|
|
55
|
-
.version('1.0.20');
|
|
56
|
-
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// Command: discover
|
|
59
|
-
// ============================================================================
|
|
60
|
-
|
|
61
|
-
program
|
|
62
|
-
.command('discover')
|
|
63
|
-
.description('Discover screens and generate UI models')
|
|
64
|
-
.option('-s, --screen <id>', 'Specific screen to discover')
|
|
65
|
-
.option('-a, --all', 'Discover all screens')
|
|
66
|
-
.option('-f, --force', 'Force re-scan even if cached')
|
|
67
|
-
.option('-v, --verbose', 'Verbose output')
|
|
68
|
-
.option('-d, --depth <number>', 'Component scan depth (default: unlimited)', '999')
|
|
69
|
-
.action(async (options) => {
|
|
70
|
-
const spinner = ora('Discovering screens...').start();
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const maxDepth = parseInt(options.depth, 10);
|
|
74
|
-
const scanner = new ReactScanner({
|
|
75
|
-
sourceRoot: SOURCE_ROOT,
|
|
76
|
-
framework: 'react-nextjs',
|
|
77
|
-
verbose: options.verbose,
|
|
78
|
-
maxDepth: maxDepth
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Discover all screens
|
|
82
|
-
const screens = await scanner.discoverScreens();
|
|
83
|
-
spinner.succeed(`Found ${screens.length} screens`);
|
|
84
|
-
|
|
85
|
-
console.log(chalk.cyan('\nScreens found:'));
|
|
86
|
-
screens.forEach(screen => console.log(chalk.gray(` - ${screen}`)));
|
|
87
|
-
|
|
88
|
-
// Build UI models
|
|
89
|
-
const screensToScan = options.screen ? [options.screen] : (options.all ? screens : []);
|
|
90
|
-
|
|
91
|
-
if (screensToScan.length === 0) {
|
|
92
|
-
console.log(chalk.yellow('\nNo screens selected. Use --screen <id> or --all'));
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
fs.mkdirSync(UI_MODEL_PATH, { recursive: true });
|
|
97
|
-
|
|
98
|
-
for (const screenId of screensToScan) {
|
|
99
|
-
const screenSpinner = ora(`Scanning ${screenId}...`).start();
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const uiModel = await scanner.buildUIModel(screenId);
|
|
103
|
-
|
|
104
|
-
// Save UI model
|
|
105
|
-
const outputPath = path.join(UI_MODEL_PATH, `${screenId}.json`);
|
|
106
|
-
fs.writeFileSync(outputPath, JSON.stringify(uiModel, null, 2), 'utf-8');
|
|
107
|
-
|
|
108
|
-
screenSpinner.succeed(`${screenId}: ${uiModel.elements.length} elements`);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
screenSpinner.fail(`${screenId}: ${(error as Error).message}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
console.log(chalk.green('\n✓ Discovery complete'));
|
|
115
|
-
} catch (error) {
|
|
116
|
-
spinner.fail('Discovery failed');
|
|
117
|
-
console.error(chalk.red((error as Error).message));
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// ============================================================================
|
|
123
|
-
// Command: generate
|
|
124
|
-
// ============================================================================
|
|
125
|
-
|
|
126
|
-
program
|
|
127
|
-
.command('generate')
|
|
128
|
-
.description('Generate selectors from UI models')
|
|
129
|
-
.option('-s, --screen <id>', 'Specific screen to generate')
|
|
130
|
-
.option('-a, --all', 'Generate all screens')
|
|
131
|
-
.option('-f, --force', 'Force re-generation (skip cache)')
|
|
132
|
-
.option('--provider <provider>', 'AI provider (anthropic|google)', DEFAULT_CONFIG.aiProvider)
|
|
133
|
-
.option('--model <model>', 'Specific AI model to use')
|
|
134
|
-
.option('-v, --verbose', 'Verbose output')
|
|
135
|
-
.action(async (options) => {
|
|
136
|
-
const spinner = ora('Generating selectors...').start();
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
// Extract selectors from Gherkin first
|
|
140
|
-
const { GherkinSelectorExtractor } = require('./gherkin-parser/selector-extractor');
|
|
141
|
-
const extractor = new GherkinSelectorExtractor();
|
|
142
|
-
const featuresDir = path.join(PROJECT_ROOT, 'qa/features');
|
|
143
|
-
|
|
144
|
-
const selectorsByScreen = extractor.extractSelectors(featuresDir);
|
|
145
|
-
spinner.succeed(`Extracted selectors from Gherkin for ${Object.keys(selectorsByScreen).length} screen(s)`);
|
|
146
|
-
|
|
147
|
-
// Initialize cache manager
|
|
148
|
-
const cacheManager = createCacheManager({
|
|
149
|
-
cachePath: CACHE_PATH,
|
|
150
|
-
enabled: !options.force
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Get screens to generate
|
|
154
|
-
const availableScreens = Object.keys(selectorsByScreen);
|
|
155
|
-
|
|
156
|
-
const screensToGenerate = options.screen
|
|
157
|
-
? [options.screen]
|
|
158
|
-
: (options.all ? availableScreens : []);
|
|
159
|
-
|
|
160
|
-
if (screensToGenerate.length === 0) {
|
|
161
|
-
console.log(chalk.yellow('No screens selected. Use --screen <id> or --all'));
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let totalGenerated = 0;
|
|
166
|
-
let totalCached = 0;
|
|
167
|
-
|
|
168
|
-
for (const screenId of screensToGenerate) {
|
|
169
|
-
const screenSpinner = ora(`Generating ${screenId}...`).start();
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
// Load UI model
|
|
173
|
-
const uiModelPath = path.join(UI_MODEL_PATH, `${screenId}.json`);
|
|
174
|
-
if (!fs.existsSync(uiModelPath)) {
|
|
175
|
-
screenSpinner.fail(`${screenId}: UI model not found`);
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const uiModel: UIModel = JSON.parse(fs.readFileSync(uiModelPath, 'utf-8'));
|
|
180
|
-
|
|
181
|
-
// Get element IDs from Gherkin (not from UI model!)
|
|
182
|
-
const screenSelectors = selectorsByScreen[screenId];
|
|
183
|
-
if (!screenSelectors || Object.keys(screenSelectors).length === 0) {
|
|
184
|
-
screenSpinner.warn(`${screenId}: No selectors referenced in Gherkin`);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const elementIds = Object.keys(screenSelectors).map(elementId =>
|
|
189
|
-
screenSelectors[elementId].fullRef
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// Check cache
|
|
193
|
-
const cacheKey = cacheManager.generateCacheKey(screenId, uiModel, elementIds);
|
|
194
|
-
const cached = cacheManager.get(screenId, cacheKey);
|
|
195
|
-
|
|
196
|
-
if (cached && !options.force) {
|
|
197
|
-
// Use cached selectors
|
|
198
|
-
await writeDSL(cached.selectors, {
|
|
199
|
-
outputPath: SELECTOR_PATH,
|
|
200
|
-
screenId
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
screenSpinner.succeed(`${screenId}: ${Object.keys(cached.selectors).length} selectors (cached)`);
|
|
204
|
-
totalCached++;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Generate with AI
|
|
209
|
-
const result = await mapSelectors(uiModel, elementIds, {
|
|
210
|
-
provider: options.provider,
|
|
211
|
-
model: options.model,
|
|
212
|
-
verbose: options.verbose
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Write DSL
|
|
216
|
-
await writeDSL(result.selectors, {
|
|
217
|
-
outputPath: SELECTOR_PATH,
|
|
218
|
-
screenId
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Cache result
|
|
222
|
-
cacheManager.set(screenId, cacheKey, result.selectors, {
|
|
223
|
-
aiModel: result.metadata.aiModel,
|
|
224
|
-
tokenCost: result.metadata.tokenCost,
|
|
225
|
-
framework: uiModel.framework
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const tokenInfo = result.metadata.tokenCost
|
|
229
|
-
? chalk.gray(` (${result.metadata.tokenCost} tokens)`)
|
|
230
|
-
: '';
|
|
231
|
-
|
|
232
|
-
screenSpinner.succeed(`${screenId}: ${Object.keys(result.selectors).length} selectors${tokenInfo}`);
|
|
233
|
-
totalGenerated++;
|
|
234
|
-
} catch (error) {
|
|
235
|
-
screenSpinner.fail(`${screenId}: ${(error as Error).message}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
console.log(chalk.green(`\n✓ Generated: ${totalGenerated}, Cached: ${totalCached}`));
|
|
240
|
-
} catch (error) {
|
|
241
|
-
spinner.fail('Generation failed');
|
|
242
|
-
console.error(chalk.red((error as Error).message));
|
|
243
|
-
process.exit(1);
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// ============================================================================
|
|
248
|
-
// Command: validate
|
|
249
|
-
// ============================================================================
|
|
250
|
-
|
|
251
|
-
program
|
|
252
|
-
.command('validate')
|
|
253
|
-
.description('Validate selector DSL files')
|
|
254
|
-
.option('-s, --screen <id>', 'Specific screen to validate')
|
|
255
|
-
.option('-a, --all', 'Validate all screens')
|
|
256
|
-
.action(async (options) => {
|
|
257
|
-
const spinner = ora('Validating selectors...').start();
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
const screensDir = path.join(SELECTOR_PATH, 'screens');
|
|
261
|
-
if (!fs.existsSync(screensDir)) {
|
|
262
|
-
spinner.fail('No selector files found');
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const selectorFiles = fs.readdirSync(screensDir).filter(f => f.endsWith('.yaml') && !f.includes('.override.'));
|
|
267
|
-
const availableScreens = selectorFiles.map(f => f.replace('.yaml', ''));
|
|
268
|
-
|
|
269
|
-
const screensToValidate = options.screen
|
|
270
|
-
? [options.screen]
|
|
271
|
-
: (options.all ? availableScreens : []);
|
|
272
|
-
|
|
273
|
-
if (screensToValidate.length === 0) {
|
|
274
|
-
spinner.fail('No screens selected');
|
|
275
|
-
console.log(chalk.yellow('Use --screen <id> or --all'));
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let totalValid = 0;
|
|
280
|
-
let totalInvalid = 0;
|
|
281
|
-
|
|
282
|
-
for (const screenId of screensToValidate) {
|
|
283
|
-
const result = await validateDSL({
|
|
284
|
-
selectorPath: SELECTOR_PATH,
|
|
285
|
-
screenId
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
if (result.valid) {
|
|
289
|
-
console.log(chalk.green(`✓ ${screenId}: Valid`));
|
|
290
|
-
totalValid++;
|
|
291
|
-
} else {
|
|
292
|
-
console.log(chalk.red(`✗ ${screenId}: Invalid`));
|
|
293
|
-
result.errors.forEach(error => {
|
|
294
|
-
console.log(chalk.gray(` - ${error}`));
|
|
295
|
-
});
|
|
296
|
-
totalInvalid++;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
spinner.stop();
|
|
301
|
-
console.log(chalk.cyan(`\nTotal: ${totalValid} valid, ${totalInvalid} invalid`));
|
|
302
|
-
|
|
303
|
-
if (totalInvalid > 0) {
|
|
304
|
-
process.exit(1);
|
|
305
|
-
}
|
|
306
|
-
} catch (error) {
|
|
307
|
-
spinner.fail('Validation failed');
|
|
308
|
-
console.error(chalk.red((error as Error).message));
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// ============================================================================
|
|
314
|
-
// Command: cache-clear
|
|
315
|
-
// ============================================================================
|
|
316
|
-
|
|
317
|
-
program
|
|
318
|
-
.command('cache-clear')
|
|
319
|
-
.description('Clear selector cache')
|
|
320
|
-
.option('-s, --screen <id>', 'Clear cache for specific screen')
|
|
321
|
-
.option('-a, --all', 'Clear all cache')
|
|
322
|
-
.action(async (options) => {
|
|
323
|
-
const cacheManager = createCacheManager({
|
|
324
|
-
cachePath: CACHE_PATH,
|
|
325
|
-
enabled: true
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
if (options.screen) {
|
|
329
|
-
const count = cacheManager.invalidate(options.screen);
|
|
330
|
-
console.log(chalk.green(`✓ Cleared ${count} cache entries for ${options.screen}`));
|
|
331
|
-
} else if (options.all) {
|
|
332
|
-
const count = cacheManager.clearAll();
|
|
333
|
-
console.log(chalk.green(`✓ Cleared ${count} cache entries`));
|
|
334
|
-
} else {
|
|
335
|
-
console.log(chalk.yellow('Use --screen <id> or --all'));
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// ============================================================================
|
|
340
|
-
// Command: info
|
|
341
|
-
// ============================================================================
|
|
342
|
-
|
|
343
|
-
program
|
|
344
|
-
.command('info')
|
|
345
|
-
.description('Show cache and selector statistics')
|
|
346
|
-
.action(async () => {
|
|
347
|
-
const cacheManager = createCacheManager({
|
|
348
|
-
cachePath: CACHE_PATH,
|
|
349
|
-
enabled: true
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const stats = cacheManager.getStats();
|
|
353
|
-
|
|
354
|
-
console.log(chalk.cyan('\n📊 Cache Statistics:'));
|
|
355
|
-
console.log(chalk.gray(` Screens cached: ${stats.totalScreens}`));
|
|
356
|
-
console.log(chalk.gray(` Cache entries: ${stats.totalEntries}`));
|
|
357
|
-
console.log(chalk.gray(` Total size: ${(stats.totalSize / 1024).toFixed(2)} KB`));
|
|
358
|
-
|
|
359
|
-
if (stats.oldestEntry) {
|
|
360
|
-
console.log(chalk.gray(` Oldest entry: ${stats.oldestEntry}`));
|
|
361
|
-
}
|
|
362
|
-
if (stats.newestEntry) {
|
|
363
|
-
console.log(chalk.gray(` Newest entry: ${stats.newestEntry}`));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Selector statistics
|
|
367
|
-
const screensDir = path.join(SELECTOR_PATH, 'screens');
|
|
368
|
-
if (fs.existsSync(screensDir)) {
|
|
369
|
-
const selectorFiles = fs.readdirSync(screensDir).filter(f => f.endsWith('.yaml') && !f.includes('.override.'));
|
|
370
|
-
|
|
371
|
-
console.log(chalk.cyan('\n📁 Selector Files:'));
|
|
372
|
-
console.log(chalk.gray(` Total screens: ${selectorFiles.length}`));
|
|
373
|
-
|
|
374
|
-
for (const file of selectorFiles) {
|
|
375
|
-
const screenId = file.replace('.yaml', '');
|
|
376
|
-
const content = fs.readFileSync(path.join(screensDir, file), 'utf-8');
|
|
377
|
-
const selectors = yaml.parse(content);
|
|
378
|
-
const count = Object.keys(selectors || {}).length;
|
|
379
|
-
|
|
380
|
-
console.log(chalk.gray(` - ${screenId}: ${count} selectors`));
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
console.log('');
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// ============================================================================
|
|
388
|
-
// Command: generate-tests
|
|
389
|
-
// ============================================================================
|
|
390
|
-
|
|
391
|
-
program
|
|
392
|
-
.command('generate-tests')
|
|
393
|
-
.description('Generate Playwright tests from Gherkin feature files')
|
|
394
|
-
.option('-f, --feature <file>', 'Specific feature file to generate')
|
|
395
|
-
.option('-a, --all', 'Generate tests for all feature files')
|
|
396
|
-
.option('-o, --output <dir>', 'Output directory for generated tests', 'specs/generated')
|
|
397
|
-
.option('--ai-mapper', 'Enable AI fallback for unknown Gherkin patterns')
|
|
398
|
-
.option('--framework <name>', 'Test framework (playwright, cypress, etc.)')
|
|
399
|
-
.option('-v, --verbose', 'Verbose output')
|
|
400
|
-
.action(async (options) => {
|
|
401
|
-
const spinner = ora('Generating tests from Gherkin...').start();
|
|
402
|
-
|
|
403
|
-
try {
|
|
404
|
-
const { GherkinParser } = require('./gherkin-parser');
|
|
405
|
-
const { CodeGenerator } = require('./test-generator/code-generator');
|
|
406
|
-
const { ConfigLoader } = require('../config/config-loader');
|
|
407
|
-
|
|
408
|
-
// Load configuration
|
|
409
|
-
const configLoader = new ConfigLoader();
|
|
410
|
-
const config = configLoader.load(options);
|
|
411
|
-
|
|
412
|
-
const featuresDir = path.join(PROJECT_ROOT, 'qa/features');
|
|
413
|
-
const outputDir = path.join(PROJECT_ROOT, options.output);
|
|
414
|
-
|
|
415
|
-
// Discover feature files
|
|
416
|
-
const featureFiles = options.feature
|
|
417
|
-
? [path.join(featuresDir, options.feature)]
|
|
418
|
-
: GherkinParser.discoverFeatureFiles(featuresDir);
|
|
419
|
-
|
|
420
|
-
if (featureFiles.length === 0) {
|
|
421
|
-
spinner.fail('No feature files found');
|
|
422
|
-
console.log(chalk.yellow('Create .feature files in qa/features/'));
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
spinner.succeed(`Found ${featureFiles.length} feature file(s)`);
|
|
427
|
-
|
|
428
|
-
// Get framework from CLI option, config file, or default
|
|
429
|
-
const framework = options.framework || config.testGenerator.framework || 'playwright';
|
|
430
|
-
|
|
431
|
-
// Get baseURL from config (null if not set)
|
|
432
|
-
const baseURL = config.app?.baseURL || null;
|
|
433
|
-
|
|
434
|
-
// Generate tests
|
|
435
|
-
const generator = new CodeGenerator({
|
|
436
|
-
useAI: options.aiMapper,
|
|
437
|
-
verbose: options.verbose,
|
|
438
|
-
framework,
|
|
439
|
-
baseURL
|
|
440
|
-
});
|
|
441
|
-
const results = await generator.generateAllTests(featuresDir, outputDir, featureFiles);
|
|
442
|
-
|
|
443
|
-
if (options.aiMapper && options.verbose) {
|
|
444
|
-
console.log(chalk.blue('\n ℹ AI-based step mapping enabled for unknown patterns'));
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
console.log(chalk.green(`\n✓ Generated ${results.length} test file(s) in ${options.output}`));
|
|
448
|
-
|
|
449
|
-
results.forEach(result => {
|
|
450
|
-
const relativePath = path.relative(PROJECT_ROOT, result.filePath);
|
|
451
|
-
console.log(chalk.gray(` - ${relativePath}`));
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
} catch (error) {
|
|
455
|
-
spinner.fail('Test generation failed');
|
|
456
|
-
console.error(chalk.red((error as Error).message));
|
|
457
|
-
if (options.verbose) {
|
|
458
|
-
console.error(error);
|
|
459
|
-
}
|
|
460
|
-
process.exit(1);
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// ============================================================================
|
|
465
|
-
// Command: full (run complete workflow)
|
|
466
|
-
// ============================================================================
|
|
467
|
-
|
|
468
|
-
program
|
|
469
|
-
.command('full')
|
|
470
|
-
.description('Run complete workflow: discover → generate → gen-tests')
|
|
471
|
-
.option('-p, --provider <provider>', 'AI provider (anthropic|google)', DEFAULT_CONFIG.aiProvider)
|
|
472
|
-
.option('--model <model>', 'Specific AI model to use')
|
|
473
|
-
.option('--ai-mapper', 'Enable AI fallback for unknown Gherkin patterns')
|
|
474
|
-
.option('--framework <name>', 'Test framework (playwright, cypress, etc.)')
|
|
475
|
-
.option('-v, --verbose', 'Verbose output')
|
|
476
|
-
.action(async (options) => {
|
|
477
|
-
console.log(chalk.cyan.bold('\n🚀 Running Full QA Workflow\n'));
|
|
478
|
-
|
|
479
|
-
// Load configuration
|
|
480
|
-
const { ConfigLoader } = require('../config/config-loader');
|
|
481
|
-
const configLoader = new ConfigLoader();
|
|
482
|
-
const config = configLoader.load(options);
|
|
483
|
-
|
|
484
|
-
// Step 1: Discover
|
|
485
|
-
console.log(chalk.cyan('Step 1/3: Discovering screens...'));
|
|
486
|
-
const discoverSpinner = ora('Scanning source code').start();
|
|
487
|
-
|
|
488
|
-
try {
|
|
489
|
-
const scanner = new ReactScanner({
|
|
490
|
-
sourceRoot: SOURCE_ROOT,
|
|
491
|
-
framework: 'react-nextjs',
|
|
492
|
-
verbose: options.verbose
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
const screens = await scanner.discoverScreens();
|
|
496
|
-
|
|
497
|
-
fs.mkdirSync(UI_MODEL_PATH, { recursive: true });
|
|
498
|
-
|
|
499
|
-
for (const screenId of screens) {
|
|
500
|
-
const uiModel = await scanner.buildUIModel(screenId);
|
|
501
|
-
const outputPath = path.join(UI_MODEL_PATH, `${screenId}.json`);
|
|
502
|
-
fs.writeFileSync(outputPath, JSON.stringify(uiModel, null, 2), 'utf-8');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
discoverSpinner.succeed(`Discovered ${screens.length} screens`);
|
|
506
|
-
} catch (error) {
|
|
507
|
-
discoverSpinner.fail('Discovery failed');
|
|
508
|
-
console.error(chalk.red((error as Error).message));
|
|
509
|
-
process.exit(1);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Step 2: Generate Selectors
|
|
513
|
-
console.log(chalk.cyan('\nStep 2/3: Generating selectors with AI...'));
|
|
514
|
-
|
|
515
|
-
// First, extract selectors from Gherkin
|
|
516
|
-
const extractSpinner = ora('Extracting selectors from Gherkin').start();
|
|
517
|
-
|
|
518
|
-
try {
|
|
519
|
-
const { GherkinSelectorExtractor } = require('./gherkin-parser/selector-extractor');
|
|
520
|
-
const extractor = new GherkinSelectorExtractor();
|
|
521
|
-
const featuresDir = path.join(PROJECT_ROOT, 'qa/features');
|
|
522
|
-
|
|
523
|
-
const selectorsByScreen = extractor.extractSelectors(featuresDir);
|
|
524
|
-
extractSpinner.succeed(`Extracted selectors from Gherkin for ${Object.keys(selectorsByScreen).length} screen(s)`);
|
|
525
|
-
|
|
526
|
-
// Now generate selectors with AI
|
|
527
|
-
const generateSpinner = ora('Calling AI to generate selectors').start();
|
|
528
|
-
|
|
529
|
-
const cacheManager = createCacheManager({
|
|
530
|
-
cachePath: CACHE_PATH,
|
|
531
|
-
enabled: true
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
const uiModelFiles = fs.readdirSync(UI_MODEL_PATH).filter(f => f.endsWith('.json'));
|
|
535
|
-
let totalGenerated = 0;
|
|
536
|
-
let totalCached = 0;
|
|
537
|
-
|
|
538
|
-
for (const file of uiModelFiles) {
|
|
539
|
-
const screenId = file.replace('.json', '');
|
|
540
|
-
const uiModelPath = path.join(UI_MODEL_PATH, file);
|
|
541
|
-
const uiModel: UIModel = JSON.parse(fs.readFileSync(uiModelPath, 'utf-8'));
|
|
542
|
-
|
|
543
|
-
// Get element IDs from Gherkin (not from UI model!)
|
|
544
|
-
const screenSelectors = selectorsByScreen[screenId];
|
|
545
|
-
if (!screenSelectors || Object.keys(screenSelectors).length === 0) {
|
|
546
|
-
// Skip screens with no Gherkin references
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const elementIds = Object.keys(screenSelectors).map(elementId =>
|
|
551
|
-
screenSelectors[elementId].fullRef
|
|
552
|
-
);
|
|
553
|
-
|
|
554
|
-
const cacheKey = cacheManager.generateCacheKey(screenId, uiModel, elementIds);
|
|
555
|
-
const cached = cacheManager.get(screenId, cacheKey);
|
|
556
|
-
|
|
557
|
-
if (cached) {
|
|
558
|
-
await writeDSL(cached.selectors, {
|
|
559
|
-
outputPath: SELECTOR_PATH,
|
|
560
|
-
screenId
|
|
561
|
-
});
|
|
562
|
-
totalCached++;
|
|
563
|
-
} else {
|
|
564
|
-
const result = await mapSelectors(uiModel, elementIds, {
|
|
565
|
-
provider: options.provider,
|
|
566
|
-
model: options.model,
|
|
567
|
-
verbose: options.verbose
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
await writeDSL(result.selectors, {
|
|
571
|
-
outputPath: SELECTOR_PATH,
|
|
572
|
-
screenId
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
cacheManager.set(screenId, cacheKey, result.selectors, {
|
|
576
|
-
aiModel: result.metadata.aiModel,
|
|
577
|
-
tokenCost: result.metadata.tokenCost,
|
|
578
|
-
framework: uiModel.framework
|
|
579
|
-
});
|
|
580
|
-
totalGenerated++;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
generateSpinner.succeed(`Generated selectors: ${totalGenerated} new, ${totalCached} cached`);
|
|
585
|
-
} catch (error) {
|
|
586
|
-
extractSpinner.fail('Selector generation failed');
|
|
587
|
-
console.error(chalk.red((error as Error).message));
|
|
588
|
-
process.exit(1);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// Step 3: Generate Tests
|
|
592
|
-
console.log(chalk.cyan('\nStep 3/3: Generating Playwright tests from Gherkin...'));
|
|
593
|
-
const testGenSpinner = ora('Parsing Gherkin and generating tests').start();
|
|
594
|
-
|
|
595
|
-
try {
|
|
596
|
-
const { GherkinParser } = require('./gherkin-parser');
|
|
597
|
-
const { CodeGenerator } = require('./test-generator/code-generator');
|
|
598
|
-
|
|
599
|
-
const featuresDir = path.join(PROJECT_ROOT, 'qa/features');
|
|
600
|
-
const outputDir = path.join(PROJECT_ROOT, 'specs/generated');
|
|
601
|
-
|
|
602
|
-
const featureFiles = GherkinParser.discoverFeatureFiles(featuresDir);
|
|
603
|
-
|
|
604
|
-
if (featureFiles.length === 0) {
|
|
605
|
-
testGenSpinner.warn('No feature files found');
|
|
606
|
-
} else {
|
|
607
|
-
// Get framework from CLI option, config file, or default
|
|
608
|
-
const framework = options.framework || config.testGenerator.framework || 'playwright';
|
|
609
|
-
|
|
610
|
-
// Get baseURL from config (null if not set)
|
|
611
|
-
const baseURL = config.app?.baseURL || null;
|
|
612
|
-
|
|
613
|
-
const generator = new CodeGenerator({
|
|
614
|
-
useAI: options.aiMapper,
|
|
615
|
-
verbose: options.verbose,
|
|
616
|
-
framework,
|
|
617
|
-
baseURL
|
|
618
|
-
});
|
|
619
|
-
const results = await generator.generateAllTests(featuresDir, outputDir, featureFiles);
|
|
620
|
-
testGenSpinner.succeed(`Generated ${results.length} test file(s)`);
|
|
621
|
-
}
|
|
622
|
-
} catch (error) {
|
|
623
|
-
testGenSpinner.fail('Test generation failed');
|
|
624
|
-
console.error(chalk.red((error as Error).message));
|
|
625
|
-
process.exit(1);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Summary
|
|
629
|
-
console.log(chalk.green.bold('\n✅ Full workflow completed!\n'));
|
|
630
|
-
console.log(chalk.cyan('Next steps:'));
|
|
631
|
-
console.log(chalk.gray(' 1. Review generated selectors in qa/selectors/screens/'));
|
|
632
|
-
console.log(chalk.gray(' 2. Create override files (*.override.yaml) if needed'));
|
|
633
|
-
console.log(chalk.gray(' 3. Run tests: npm run test:qa\n'));
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// ============================================================================
|
|
637
|
-
// Parse and Execute
|
|
638
|
-
// ============================================================================
|
|
639
|
-
|
|
640
|
-
program.parse();
|