@sun-asterisk/sungen 1.0.17 → 1.0.19

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.
Files changed (108) hide show
  1. package/README.md +101 -601
  2. package/dist/cli/commands/live-scan-command.d.ts +9 -0
  3. package/dist/cli/commands/live-scan-command.d.ts.map +1 -0
  4. package/dist/cli/commands/live-scan-command.js +72 -0
  5. package/dist/cli/commands/live-scan-command.js.map +1 -0
  6. package/dist/cli/index.js +34 -5
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/core/live-scanner/config-reader.d.ts +10 -0
  9. package/dist/core/live-scanner/config-reader.d.ts.map +1 -0
  10. package/dist/core/live-scanner/config-reader.js +87 -0
  11. package/dist/core/live-scanner/config-reader.js.map +1 -0
  12. package/dist/core/live-scanner/element-finder.d.ts +20 -0
  13. package/dist/core/live-scanner/element-finder.d.ts.map +1 -0
  14. package/dist/core/live-scanner/element-finder.js +289 -0
  15. package/dist/core/live-scanner/element-finder.js.map +1 -0
  16. package/dist/core/live-scanner/index.d.ts +8 -0
  17. package/dist/core/live-scanner/index.d.ts.map +1 -0
  18. package/dist/core/live-scanner/index.js +33 -0
  19. package/dist/core/live-scanner/index.js.map +1 -0
  20. package/dist/core/live-scanner/matrix-reader.d.ts +17 -0
  21. package/dist/core/live-scanner/matrix-reader.d.ts.map +1 -0
  22. package/dist/core/live-scanner/matrix-reader.js +97 -0
  23. package/dist/core/live-scanner/matrix-reader.js.map +1 -0
  24. package/dist/core/live-scanner/matrix-writer.d.ts +7 -0
  25. package/dist/core/live-scanner/matrix-writer.d.ts.map +1 -0
  26. package/dist/core/live-scanner/matrix-writer.js +92 -0
  27. package/dist/core/live-scanner/matrix-writer.js.map +1 -0
  28. package/dist/core/live-scanner/role-fallback.d.ts +15 -0
  29. package/dist/core/live-scanner/role-fallback.d.ts.map +1 -0
  30. package/dist/core/live-scanner/role-fallback.js +45 -0
  31. package/dist/core/live-scanner/role-fallback.js.map +1 -0
  32. package/dist/core/live-scanner/scanner.d.ts +13 -0
  33. package/dist/core/live-scanner/scanner.d.ts.map +1 -0
  34. package/dist/core/live-scanner/scanner.js +225 -0
  35. package/dist/core/live-scanner/scanner.js.map +1 -0
  36. package/dist/core/live-scanner/step-replayer.d.ts +26 -0
  37. package/dist/core/live-scanner/step-replayer.d.ts.map +1 -0
  38. package/dist/core/live-scanner/step-replayer.js +184 -0
  39. package/dist/core/live-scanner/step-replayer.js.map +1 -0
  40. package/dist/core/live-scanner/types.d.ts +50 -0
  41. package/dist/core/live-scanner/types.d.ts.map +1 -0
  42. package/dist/core/live-scanner/types.js +14 -0
  43. package/dist/core/live-scanner/types.js.map +1 -0
  44. package/dist/generators/cli.js +1 -1
  45. package/dist/generators/scaffold-generator/index.d.ts +20 -2
  46. package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
  47. package/dist/generators/scaffold-generator/index.js +170 -10
  48. package/dist/generators/scaffold-generator/index.js.map +1 -1
  49. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -1
  50. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
  51. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
  52. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +6 -2
  53. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  54. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -1
  55. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -1
  56. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -1
  57. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -1
  58. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  59. package/dist/generators/test-generator/patterns/assertion-patterns.js +6 -3
  60. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  61. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  62. package/dist/generators/test-generator/template-engine.js +3 -5
  63. package/dist/generators/test-generator/template-engine.js.map +1 -1
  64. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  65. package/dist/generators/test-generator/utils/data-resolver.js +18 -7
  66. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  67. package/dist/generators/test-generator/utils/selector-resolver.d.ts +1 -0
  68. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  69. package/dist/generators/test-generator/utils/selector-resolver.js +6 -0
  70. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  71. package/dist/input/cli-adapter.d.ts +4 -0
  72. package/dist/input/cli-adapter.d.ts.map +1 -1
  73. package/dist/input/cli-adapter.js +18 -3
  74. package/dist/input/cli-adapter.js.map +1 -1
  75. package/dist/orchestrator/project-initializer.d.ts +6 -1
  76. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  77. package/dist/orchestrator/project-initializer.js +34 -8
  78. package/dist/orchestrator/project-initializer.js.map +1 -1
  79. package/package.json +1 -1
  80. package/src/cli/commands/live-scan-command.ts +79 -0
  81. package/src/cli/index.ts +40 -7
  82. package/src/config/default.config.yaml +6 -0
  83. package/src/core/live-scanner/config-reader.ts +57 -0
  84. package/src/core/live-scanner/element-finder.ts +333 -0
  85. package/src/core/live-scanner/index.ts +7 -0
  86. package/src/core/live-scanner/matrix-reader.ts +70 -0
  87. package/src/core/live-scanner/matrix-writer.ts +66 -0
  88. package/src/core/live-scanner/role-fallback.ts +43 -0
  89. package/src/core/live-scanner/scanner.ts +231 -0
  90. package/src/core/live-scanner/step-replayer.ts +219 -0
  91. package/src/core/live-scanner/types.ts +56 -0
  92. package/src/generators/cli.ts +1 -1
  93. package/src/generators/scaffold-generator/index.ts +181 -14
  94. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -1
  95. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
  96. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
  97. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +6 -2
  98. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  99. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -1
  100. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -1
  101. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -1
  102. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -1
  103. package/src/generators/test-generator/patterns/assertion-patterns.ts +6 -3
  104. package/src/generators/test-generator/template-engine.ts +3 -4
  105. package/src/generators/test-generator/utils/data-resolver.ts +20 -9
  106. package/src/generators/test-generator/utils/selector-resolver.ts +8 -0
  107. package/src/input/cli-adapter.ts +19 -3
  108. package/src/orchestrator/project-initializer.ts +37 -8
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Step Replayer
3
+ * Replays Gherkin steps on a live Playwright page to reveal dynamic elements.
4
+ * Takes a snapshot and finds elements after each action.
5
+ */
6
+ import type { Page } from 'playwright';
7
+ import { LiveElement } from './types';
8
+ interface ParsedStepInfo {
9
+ keyword: string;
10
+ text: string;
11
+ selectorRef?: string;
12
+ dataRef?: string;
13
+ elementType?: string;
14
+ nth?: number;
15
+ }
16
+ interface ReplayResult {
17
+ elements: Record<string, LiveElement>;
18
+ errors: string[];
19
+ }
20
+ /**
21
+ * Replay a sequence of Gherkin steps on a Playwright page,
22
+ * finding and interacting with elements along the way.
23
+ */
24
+ export declare function replaySteps(page: Page, steps: ParsedStepInfo[], featurePath: string, baseUrl: string): Promise<ReplayResult>;
25
+ export {};
26
+ //# sourceMappingURL=step-replayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-replayer.d.ts","sourceRoot":"","sources":["../../../src/core/live-scanner/step-replayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,WAAW,EAAkB,MAAM,SAAS,CAAC;AAGtD,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,cAAc,EAAE,EACvB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA6EvB"}
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ /**
3
+ * Step Replayer
4
+ * Replays Gherkin steps on a live Playwright page to reveal dynamic elements.
5
+ * Takes a snapshot and finds elements after each action.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.replaySteps = replaySteps;
9
+ const element_finder_1 = require("./element-finder");
10
+ const selector_resolver_1 = require("../../generators/test-generator/utils/selector-resolver");
11
+ /**
12
+ * Replay a sequence of Gherkin steps on a Playwright page,
13
+ * finding and interacting with elements along the way.
14
+ */
15
+ async function replaySteps(page, steps, featurePath, baseUrl) {
16
+ const elements = {};
17
+ const errors = [];
18
+ let currentContext = 'page';
19
+ for (const step of steps) {
20
+ try {
21
+ const action = detectAction(step.text);
22
+ // Handle navigation (Given User is on [x] page)
23
+ if (action === 'navigate') {
24
+ await page.goto(baseUrl + featurePath, { waitUntil: 'networkidle', timeout: 15000 });
25
+ await settle(page);
26
+ if (step.selectorRef) {
27
+ const key = generateElementKey(step.selectorRef, step.elementType);
28
+ // Record the page element but don't search for it on the DOM
29
+ elements[key] = {
30
+ gherkinRef: step.selectorRef,
31
+ gherkinType: step.elementType || 'page',
32
+ selectorType: '',
33
+ selectorValue: featurePath,
34
+ role: '',
35
+ name: step.selectorRef,
36
+ testid: null,
37
+ tag: '',
38
+ placeholder: null,
39
+ label: null,
40
+ context: 'page',
41
+ matchMethod: 'exact_role',
42
+ exact: false,
43
+ warning: null,
44
+ };
45
+ }
46
+ continue;
47
+ }
48
+ // Skip steps without element references
49
+ if (!step.selectorRef)
50
+ continue;
51
+ const key = generateElementKey(step.selectorRef, step.elementType);
52
+ const nth = step.nth || 0;
53
+ const elementType = step.elementType || 'button';
54
+ // Find the element on the page
55
+ const element = await (0, element_finder_1.findElement)(page, step.selectorRef, elementType, nth);
56
+ element.context = currentContext;
57
+ // Record in matrix (first match wins for duplicate keys)
58
+ if (!elements[key]) {
59
+ elements[key] = element;
60
+ }
61
+ // Execute the action if element was found
62
+ if (element.matchMethod !== 'unresolved') {
63
+ if (action === 'click') {
64
+ await executeClick(page, step.selectorRef, elementType, nth);
65
+ await settle(page);
66
+ currentContext = await detectCurrentContext(page, currentContext, step.text);
67
+ }
68
+ else if (action === 'fill') {
69
+ // For fill actions during scanning, use a placeholder value
70
+ // We just need to verify the element exists
71
+ }
72
+ // 'see' and 'wait' actions: just record, don't interact
73
+ }
74
+ else {
75
+ errors.push(`Step "${step.text}": element [${step.selectorRef}] not found`);
76
+ // Continue scanning remaining steps — record what we can even if some elements are missing
77
+ }
78
+ }
79
+ catch (error) {
80
+ errors.push(`Step "${step.text}": ${error.message}`);
81
+ // On navigation or critical errors, stop replaying this scenario
82
+ if (error.message.includes('Navigation') || error.message.includes('net::')) {
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ return { elements, errors };
88
+ }
89
+ async function executeClick(page, ref, elementType, nth) {
90
+ const namePattern = new RegExp(escapeRegex(ref), 'i');
91
+ // Try role-based click first
92
+ const { getRoleFallbacks } = require('./role-fallback');
93
+ const roles = getRoleFallbacks(elementType);
94
+ for (const role of roles) {
95
+ try {
96
+ const locator = page.getByRole(role, { name: namePattern });
97
+ const target = nth > 0 ? locator.nth(nth) : locator.first();
98
+ if (await target.isVisible({ timeout: 3000 })) {
99
+ await target.click({ timeout: 5000 });
100
+ return;
101
+ }
102
+ }
103
+ catch {
104
+ continue;
105
+ }
106
+ }
107
+ // Fallback to text click
108
+ try {
109
+ const locator = page.getByText(namePattern);
110
+ const target = nth > 0 ? locator.nth(nth) : locator.first();
111
+ await target.click({ timeout: 5000 });
112
+ }
113
+ catch {
114
+ // Try testid as last resort
115
+ const normalized = ref.toLowerCase().replace(/[\s_-]+/g, '-');
116
+ await page.locator(`[data-testid*="${normalized}" i]`).first().click({ timeout: 5000 });
117
+ }
118
+ }
119
+ function detectAction(text) {
120
+ const lower = text.toLowerCase();
121
+ if (/\b(is on|navigate|go to|visit|open)\b/.test(lower) && /\bpage\b/.test(lower))
122
+ return 'navigate';
123
+ if (/\b(click|press|tap|submit)\b/.test(lower))
124
+ return 'click';
125
+ if (/\b(fill|enter|type|input)\b/.test(lower))
126
+ return 'fill';
127
+ if (/\b(see|verify|check|expect|visible|displayed)\b/.test(lower))
128
+ return 'see';
129
+ if (/\b(select|choose|pick)\b/.test(lower))
130
+ return 'select';
131
+ if (/\b(wait)\b/.test(lower))
132
+ return 'wait';
133
+ return 'see';
134
+ }
135
+ async function detectCurrentContext(page, previous, stepText) {
136
+ const lower = stepText.toLowerCase();
137
+ // Check if we opened a dialog/modal
138
+ if (/\b(dialog|modal|panel)\b/.test(lower)) {
139
+ return 'dialog';
140
+ }
141
+ // Check if a dialog is now visible on the page
142
+ try {
143
+ const dialogLocator = page.getByRole('dialog');
144
+ if (await dialogLocator.count() > 0) {
145
+ return 'dialog';
146
+ }
147
+ }
148
+ catch {
149
+ // ignore
150
+ }
151
+ // Check for menu
152
+ if (/\b(menu|dropdown)\b/.test(lower)) {
153
+ return 'menu';
154
+ }
155
+ try {
156
+ const menuLocator = page.getByRole('menu');
157
+ if (await menuLocator.count() > 0) {
158
+ return 'menu';
159
+ }
160
+ }
161
+ catch {
162
+ // ignore
163
+ }
164
+ return previous;
165
+ }
166
+ async function settle(page) {
167
+ try {
168
+ await page.waitForLoadState('networkidle', { timeout: 5000 });
169
+ }
170
+ catch {
171
+ // Timeout is acceptable — page may have long-polling connections
172
+ }
173
+ // Small extra wait for animations/transitions
174
+ await page.waitForTimeout(300);
175
+ }
176
+ function generateElementKey(ref, elementType) {
177
+ // Use the same key generation as the scaffold generator
178
+ // so live-scan keys match scaffold keys in the map command.
179
+ return selector_resolver_1.SelectorResolver.generateKey(ref);
180
+ }
181
+ function escapeRegex(str) {
182
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
183
+ }
184
+ //# sourceMappingURL=step-replayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-replayer.js","sourceRoot":"","sources":["../../../src/core/live-scanner/step-replayer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Live Scanner Types
3
+ * Defines the data structures for live DOM scanning and matrix file output.
4
+ *
5
+ * Selector types map directly to Playwright locator strategies:
6
+ * testid → page.getByTestId(value)
7
+ * role → page.getByRole(value, { name })
8
+ * label → page.getByLabel(value)
9
+ * placeholder → page.getByPlaceholder(value)
10
+ * text → page.getByText(value)
11
+ */
12
+ export type MatchMethod = 'testid' | 'exact_role' | 'role_fallback' | 'label' | 'placeholder' | 'text_only' | 'unresolved';
13
+ export type SelectorType = 'testid' | 'role' | 'label' | 'placeholder' | 'text';
14
+ export type ElementContext = 'page' | 'dialog' | 'menu' | 'modal' | 'popup';
15
+ export interface LiveElement {
16
+ gherkinRef: string;
17
+ gherkinType: string;
18
+ selectorType: SelectorType | '';
19
+ selectorValue: string;
20
+ role: string;
21
+ name: string;
22
+ testid: string | null;
23
+ tag: string;
24
+ placeholder: string | null;
25
+ label: string | null;
26
+ context: ElementContext;
27
+ matchMethod: MatchMethod;
28
+ exact: boolean;
29
+ warning: string | null;
30
+ }
31
+ export interface LiveScanScenario {
32
+ auth: string | null;
33
+ extends: string | null;
34
+ path: string;
35
+ elements: Record<string, LiveElement>;
36
+ }
37
+ export interface LiveScanResult {
38
+ screen: string;
39
+ baseUrl: string;
40
+ scannedAt: string;
41
+ scenarios: Record<string, LiveScanScenario>;
42
+ }
43
+ export interface LiveScanOptions {
44
+ baseUrl?: string;
45
+ screenName: string;
46
+ screensDir: string;
47
+ headed?: boolean;
48
+ authDir?: string;
49
+ }
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/live-scanner/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,eAAe,GAAG,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAE3H,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,CAAC;AAEhF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,GAAG,EAAE,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Live Scanner Types
4
+ * Defines the data structures for live DOM scanning and matrix file output.
5
+ *
6
+ * Selector types map directly to Playwright locator strategies:
7
+ * testid → page.getByTestId(value)
8
+ * role → page.getByRole(value, { name })
9
+ * label → page.getByLabel(value)
10
+ * placeholder → page.getByPlaceholder(value)
11
+ * text → page.getByText(value)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/live-scanner/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG"}
@@ -80,7 +80,7 @@ const program = new commander_1.Command();
80
80
  program
81
81
  .name('qa-generator')
82
82
  .description('AI-Native QA Selector DSL Generator')
83
- .version('1.0.17');
83
+ .version('1.0.19');
84
84
  // ============================================================================
85
85
  // Command: discover
86
86
  // ============================================================================
@@ -4,10 +4,11 @@
4
4
  */
5
5
  export interface ScaffoldElement {
6
6
  locator: string;
7
- type: 'placeholder' | 'role' | 'text' | 'label' | 'page';
7
+ type: 'placeholder' | 'role' | 'text' | 'label' | 'page' | 'testid';
8
8
  value: string;
9
9
  name?: string;
10
10
  nth: number;
11
+ exact?: boolean;
11
12
  }
12
13
  export interface ScaffoldResult {
13
14
  [key: string]: ScaffoldElement;
@@ -22,7 +23,7 @@ export interface MapResult {
22
23
  elementCount: number;
23
24
  dataRefCount: number;
24
25
  selectorsSkipped?: boolean;
25
- testDataSkipped?: boolean;
26
+ testDataMerged?: boolean;
26
27
  }
27
28
  export declare class ScaffoldGenerator {
28
29
  /**
@@ -112,6 +113,16 @@ export declare class ScaffoldGenerator {
112
113
  outputPath: string;
113
114
  elementCount: number;
114
115
  }>;
116
+ /**
117
+ * Enrich scaffold with live-scan data if a .live-scan.yaml file exists.
118
+ * Uses the Playwright locator strategy detected by live-scan:
119
+ * testid → type: testid, value: <testid>
120
+ * role → type: role, value: <aria-role>, name: <accessible-name>
121
+ * label → type: label, value: <label-text>
122
+ * placeholder → type: placeholder, value: <placeholder-text>
123
+ * text → type: text, value: <visible-text>
124
+ */
125
+ private enrichWithLiveScan;
115
126
  /**
116
127
  * Helper to capitalize first letter
117
128
  */
@@ -134,6 +145,13 @@ export declare class ScaffoldGenerator {
134
145
  * Write selectors YAML file
135
146
  */
136
147
  private writeSelectorsYaml;
148
+ /**
149
+ * Smart-merge test-data objects:
150
+ * - Keys present in both: keep the existing value (user may have filled it in)
151
+ * - Keys only in fresh (new refs in features): add with empty value
152
+ * - Keys only in existing (removed from features): drop them
153
+ */
154
+ private mergeTestData;
137
155
  /**
138
156
  * Write test-data YAML file
139
157
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generators/scaffold-generator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CAAC;CACxC;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAiBD,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,cAAc;IAKzD;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc;IAQpD;;OAEG;IACH,OAAO,CAAC,eAAe;IAqDvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAsEzB;;;OAGG;IACH;;OAEG;IACH,OAAO,CAAC,YAAY;IA4BpB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;;;OAIG;IACH;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;OAEG;IACH,OAAO,CAAC,aAAa;IA2CrB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqH7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAuBpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM;IAyB1E;;OAEG;IACH,aAAa,CACX,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI;IAUP;;OAEG;IACH,kBAAkB,CAChB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,OAAe,GAC9B;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAsB/C;;OAEG;IACH,kBAAkB,CAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAmBpE;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,GAAE,MAAqB,EAAE,cAAc,GAAE,OAAe,GAAG,SAAS,EAAE;IAyElH;;OAEG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IA0BrB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwB1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAmB1B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generators/scaffold-generator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CAAC;CACxC;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAiBD,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,cAAc;IAKzD;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc;IAQpD;;OAEG;IACH,OAAO,CAAC,eAAe;IAqDvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAyEzB;;;OAGG;IACH;;OAEG;IACH,OAAO,CAAC,YAAY;IA4BpB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;;;OAIG;IACH;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;OAEG;IACH,OAAO,CAAC,aAAa;IA2CrB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqH7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAyBpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM;IAyB1E;;OAEG;IACH,aAAa,CACX,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI;IAUP;;OAEG;IACH,kBAAkB,CAChB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,OAAe,GAC9B;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAsB/C;;OAEG;IACH,kBAAkB,CAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAmBpE;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IA6G1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,GAAE,MAAqB,EAAE,cAAc,GAAE,OAAe,GAAG,SAAS,EAAE;IAoFlH;;OAEG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IA0BrB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwB1B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAwBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAqB1B"}
@@ -125,6 +125,9 @@ class ScaffoldGenerator {
125
125
  if (/^(logo|image|img|icon)\b/.test(lowerText) || /^\d*\s*(logo|image|img|icon)\b/.test(lowerText)) {
126
126
  return 'img';
127
127
  }
128
+ if (/^(dialog|modal)\b/.test(lowerText) || /^\d*\s*(dialog|modal)\b/.test(lowerText)) {
129
+ return 'dialog';
130
+ }
128
131
  if (/^(heading|header)\b/.test(lowerText) || /^\d*\s*(heading|header)\b/.test(lowerText)) {
129
132
  return 'heading';
130
133
  }
@@ -317,7 +320,7 @@ class ScaffoldGenerator {
317
320
  case 'see':
318
321
  // For assertions - respect targetType for role-based elements
319
322
  if (targetType === 'img' || targetType === 'button' || targetType === 'link' ||
320
- targetType === 'checkbox' || targetType === 'radio' || targetType === 'heading') {
323
+ targetType === 'checkbox' || targetType === 'radio' || targetType === 'heading' || targetType === 'dialog') {
321
324
  const seeRoleValue = this.getRoleValue(targetType);
322
325
  return {
323
326
  locator: '',
@@ -383,6 +386,8 @@ class ScaffoldGenerator {
383
386
  return 'img';
384
387
  case 'heading':
385
388
  return 'heading';
389
+ case 'dialog':
390
+ return 'dialog';
386
391
  default:
387
392
  return 'button'; // Default to button
388
393
  }
@@ -495,6 +500,119 @@ class ScaffoldGenerator {
495
500
  }
496
501
  return results;
497
502
  }
503
+ /**
504
+ * Enrich scaffold with live-scan data if a .live-scan.yaml file exists.
505
+ * Uses the Playwright locator strategy detected by live-scan:
506
+ * testid → type: testid, value: <testid>
507
+ * role → type: role, value: <aria-role>, name: <accessible-name>
508
+ * label → type: label, value: <label-text>
509
+ * placeholder → type: placeholder, value: <placeholder-text>
510
+ * text → type: text, value: <visible-text>
511
+ */
512
+ enrichWithLiveScan(scaffold, liveScanPath) {
513
+ if (!fs_1.default.existsSync(liveScanPath)) {
514
+ return scaffold;
515
+ }
516
+ let matrixData;
517
+ try {
518
+ const { readMatrix, mergeMatrixElements } = require('../../core/live-scanner/matrix-reader');
519
+ matrixData = readMatrix(liveScanPath);
520
+ if (!matrixData)
521
+ return scaffold;
522
+ const elements = mergeMatrixElements(matrixData);
523
+ if (!elements || Object.keys(elements).length === 0)
524
+ return scaffold;
525
+ console.log(` 🔗 Using live-scan data from ${path_1.default.basename(liveScanPath)}`);
526
+ let enrichedCount = 0;
527
+ let warningCount = 0;
528
+ const enriched = { ...scaffold };
529
+ for (const [key, scaffoldElement] of Object.entries(enriched)) {
530
+ // Try exact key first, then base key without --N suffix or --type suffix
531
+ let liveElement = elements[key];
532
+ if (!liveElement && key.includes('--')) {
533
+ const baseKey = key.replace(/--.*$/, '');
534
+ liveElement = elements[baseKey];
535
+ }
536
+ if (!liveElement) {
537
+ continue;
538
+ }
539
+ // If live-scan couldn't find the element, clear the identifying field so user must fill manually
540
+ if (liveElement.matchMethod === 'unresolved') {
541
+ if (scaffoldElement.name !== undefined) {
542
+ enriched[key] = { ...scaffoldElement, name: '' };
543
+ }
544
+ else {
545
+ enriched[key] = { ...scaffoldElement, value: '' };
546
+ }
547
+ continue;
548
+ }
549
+ // Use the Playwright locator strategy that live-scan detected
550
+ const selectorType = liveElement.selectorType;
551
+ // Only set exact when true (omit from YAML when false for cleaner output)
552
+ const exactField = liveElement.exact ? { exact: true } : {};
553
+ if (selectorType === 'testid' && liveElement.testid) {
554
+ // page.getByTestId('value')
555
+ enriched[key] = {
556
+ ...scaffoldElement,
557
+ type: 'testid',
558
+ value: liveElement.testid,
559
+ };
560
+ }
561
+ else if (selectorType === 'role' && liveElement.role) {
562
+ // page.getByRole('button', { name: 'Submit' })
563
+ enriched[key] = {
564
+ ...scaffoldElement,
565
+ type: 'role',
566
+ value: liveElement.role,
567
+ name: liveElement.name || liveElement.gherkinRef,
568
+ ...exactField,
569
+ };
570
+ }
571
+ else if (selectorType === 'label' && liveElement.label) {
572
+ // page.getByLabel('Username')
573
+ enriched[key] = {
574
+ ...scaffoldElement,
575
+ type: 'label',
576
+ value: liveElement.label,
577
+ ...exactField,
578
+ };
579
+ }
580
+ else if (selectorType === 'placeholder' && liveElement.placeholder) {
581
+ // page.getByPlaceholder('Enter email')
582
+ enriched[key] = {
583
+ ...scaffoldElement,
584
+ type: 'placeholder',
585
+ value: liveElement.placeholder,
586
+ ...exactField,
587
+ };
588
+ }
589
+ else if (selectorType === 'text') {
590
+ // page.getByText('Welcome')
591
+ enriched[key] = {
592
+ ...scaffoldElement,
593
+ type: 'text',
594
+ value: liveElement.name || liveElement.gherkinRef,
595
+ ...exactField,
596
+ };
597
+ }
598
+ else {
599
+ continue;
600
+ }
601
+ enrichedCount++;
602
+ if (liveElement.warning) {
603
+ warningCount++;
604
+ console.log(` ⚠️ ${key}: ${liveElement.warning}`);
605
+ }
606
+ }
607
+ const textOnlyCount = Object.keys(enriched).length - enrichedCount;
608
+ console.log(` 📊 Live-scan: ${enrichedCount} enriched, ${textOnlyCount} text-inferred${warningCount > 0 ? `, ${warningCount} warnings` : ''}`);
609
+ return enriched;
610
+ }
611
+ catch (error) {
612
+ console.warn(` ⚠️ Failed to read live-scan data: ${error.message}`);
613
+ return scaffold;
614
+ }
615
+ }
498
616
  /**
499
617
  * Helper to capitalize first letter
500
618
  */
@@ -534,6 +652,9 @@ class ScaffoldGenerator {
534
652
  // Build scaffolds from this file
535
653
  const selectorScaffold = this.buildScaffold(elements);
536
654
  const testDataScaffold = this.buildTestData(dataRefs);
655
+ // Enrich scaffold with live-scan data if available
656
+ const liveScanPath = path_1.default.join(selectorsDir, `${filename}.live-scan.yaml`);
657
+ const liveScanEnriched = this.enrichWithLiveScan(selectorScaffold, liveScanPath);
537
658
  // Check if files exist
538
659
  const selectorsPath = path_1.default.join(selectorsDir, `${filename}.yaml`);
539
660
  const selectorsOverridePath = path_1.default.join(selectorsDir, `${filename}.override.yaml`);
@@ -543,11 +664,19 @@ class ScaffoldGenerator {
543
664
  const testDataExists = fs_1.default.existsSync(testDataPath);
544
665
  // Write selectors YAML (BASE ONLY - never touch override files)
545
666
  if (!selectorsExists || forceOverwrite) {
546
- this.writeSelectorsYaml(selectorScaffold, selectorsPath, filename);
667
+ this.writeSelectorsYaml(liveScanEnriched, selectorsPath, filename);
547
668
  }
548
669
  // Override file is NEVER touched by map command
549
- // Write test-data YAML (BASE ONLY - never touch override files)
550
- if (!testDataExists || forceOverwrite) {
670
+ // Write test-data YAML always smart-merge regardless of --force:
671
+ // existing keys keep their current value
672
+ // • new keys from features are added (empty string)
673
+ // • keys no longer referenced in features are removed
674
+ if (testDataExists) {
675
+ const existingRaw = yaml_1.default.parse(fs_1.default.readFileSync(testDataPath, 'utf-8')) || {};
676
+ const merged = this.mergeTestData(existingRaw, testDataScaffold);
677
+ this.writeTestDataYaml(merged, testDataPath, filename);
678
+ }
679
+ else {
551
680
  this.writeTestDataYaml(testDataScaffold, testDataPath, filename);
552
681
  }
553
682
  // Override file is NEVER touched by map command
@@ -555,10 +684,10 @@ class ScaffoldGenerator {
555
684
  featureFile: filename,
556
685
  selectorsPath,
557
686
  testDataPath,
558
- elementCount: Object.keys(selectorScaffold).length,
687
+ elementCount: Object.keys(liveScanEnriched).length,
559
688
  dataRefCount: dataRefs.length,
560
689
  selectorsSkipped: selectorsExists && !forceOverwrite,
561
- testDataSkipped: testDataExists && !forceOverwrite,
690
+ testDataMerged: testDataExists,
562
691
  });
563
692
  }
564
693
  return results;
@@ -644,14 +773,45 @@ class ScaffoldGenerator {
644
773
  });
645
774
  fs_1.default.writeFileSync(outputPath, header + yamlContent, 'utf-8');
646
775
  }
776
+ /**
777
+ * Smart-merge test-data objects:
778
+ * - Keys present in both: keep the existing value (user may have filled it in)
779
+ * - Keys only in fresh (new refs in features): add with empty value
780
+ * - Keys only in existing (removed from features): drop them
781
+ */
782
+ mergeTestData(existing, fresh) {
783
+ const merged = {};
784
+ for (const key of Object.keys(fresh)) {
785
+ if (key in existing) {
786
+ const existingVal = existing[key];
787
+ const freshVal = fresh[key];
788
+ if (typeof freshVal === 'object' && typeof existingVal === 'object') {
789
+ // Both are nested — recurse
790
+ merged[key] = this.mergeTestData(existingVal, freshVal);
791
+ }
792
+ else {
793
+ // Keep the existing (user-supplied) value
794
+ merged[key] = existingVal;
795
+ }
796
+ }
797
+ else {
798
+ // New key — add with the scaffold default (empty string or nested object)
799
+ merged[key] = fresh[key];
800
+ }
801
+ // Keys present in existing but absent from fresh are intentionally dropped (no longer referenced)
802
+ }
803
+ return merged;
804
+ }
647
805
  /**
648
806
  * Write test-data YAML file
649
807
  */
650
808
  writeTestDataYaml(testData, outputPath, screenName) {
651
- const header = `# ${this.capitalizeFirst(screenName)} Screen Test Data (Auto-generated)
652
- # ⚠️ AUTO-GENERATED - Consider editing only the override file
653
- # To override values, copy to ${screenName}.override.yaml
654
- # Manual edits to this file will be lost on regeneration
809
+ const header = `# ${this.capitalizeFirst(screenName)} Screen Test Data
810
+ # Managed by: sungen map (smart-merge)
811
+ # New {{variable}} refs are added automatically (empty value)
812
+ # Removed refs are dropped on the next sungen map run
813
+ # • Your existing values are never overwritten
814
+ # To fix a value permanently, copy the key to ${screenName}.override.yaml
655
815
  #
656
816
  # Reference in features using {{variable}} syntax
657
817