@sun-asterisk/sungen 1.0.14 → 1.0.16

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 (125) hide show
  1. package/dist/generators/cli.js +1 -1
  2. package/dist/generators/gherkin-parser/index.d.ts +3 -0
  3. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  4. package/dist/generators/gherkin-parser/index.js +14 -1
  5. package/dist/generators/gherkin-parser/index.js.map +1 -1
  6. package/dist/generators/scaffold-generator/index.d.ts +4 -1
  7. package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
  8. package/dist/generators/scaffold-generator/index.js +46 -18
  9. package/dist/generators/scaffold-generator/index.js.map +1 -1
  10. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-element-with-text.hbs +1 -1
  11. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -1
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs +1 -0
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +1 -2
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +5 -0
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +1 -2
  19. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +1 -2
  20. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
  21. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +5 -0
  22. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
  23. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  24. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -1
  25. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
  26. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +5 -1
  27. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  28. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  29. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
  30. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
  31. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
  32. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
  33. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
  34. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
  35. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
  36. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
  37. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
  38. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
  39. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
  40. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
  41. package/dist/generators/test-generator/code-generator.d.ts +3 -1
  42. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  43. package/dist/generators/test-generator/code-generator.js +66 -14
  44. package/dist/generators/test-generator/code-generator.js.map +1 -1
  45. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  46. package/dist/generators/test-generator/patterns/assertion-patterns.js +148 -73
  47. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  48. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  49. package/dist/generators/test-generator/patterns/form-patterns.js +37 -5
  50. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  51. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  52. package/dist/generators/test-generator/patterns/interaction-patterns.js +116 -20
  53. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  54. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
  55. package/dist/generators/test-generator/patterns/navigation-patterns.js +1 -1
  56. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  57. package/dist/generators/test-generator/step-mapper.d.ts +1 -0
  58. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  59. package/dist/generators/test-generator/step-mapper.js +15 -0
  60. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  61. package/dist/generators/test-generator/template-engine.d.ts +5 -0
  62. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  63. package/dist/generators/test-generator/template-engine.js +67 -3
  64. package/dist/generators/test-generator/template-engine.js.map +1 -1
  65. package/dist/generators/test-generator/utils/selector-resolver.d.ts +14 -1
  66. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  67. package/dist/generators/test-generator/utils/selector-resolver.js +32 -5
  68. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  69. package/dist/input/cli-adapter.js +1 -1
  70. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  71. package/dist/orchestrator/project-initializer.js +2 -1
  72. package/dist/orchestrator/project-initializer.js.map +1 -1
  73. package/package.json +1 -1
  74. package/src/generators/cli.ts +1 -1
  75. package/src/generators/gherkin-parser/index.ts +18 -1
  76. package/src/generators/scaffold-generator/index.ts +50 -22
  77. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-element-with-text.hbs +1 -1
  78. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -1
  79. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -0
  80. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs +1 -0
  81. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +1 -2
  82. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
  83. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +5 -0
  84. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
  85. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +1 -2
  86. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +1 -2
  87. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
  88. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +5 -0
  89. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
  90. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  91. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -1
  92. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
  93. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +5 -1
  94. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  95. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  96. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
  97. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
  98. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
  99. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
  100. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
  101. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
  102. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
  103. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
  104. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
  105. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
  106. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
  107. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
  108. package/src/generators/test-generator/code-generator.ts +70 -15
  109. package/src/generators/test-generator/patterns/assertion-patterns.ts +171 -78
  110. package/src/generators/test-generator/patterns/form-patterns.ts +39 -5
  111. package/src/generators/test-generator/patterns/interaction-patterns.ts +126 -24
  112. package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -1
  113. package/src/generators/test-generator/step-mapper.ts +18 -2
  114. package/src/generators/test-generator/template-engine.ts +74 -3
  115. package/src/generators/test-generator/utils/selector-resolver.ts +34 -5
  116. package/src/input/cli-adapter.ts +1 -1
  117. package/src/orchestrator/project-initializer.ts +2 -1
  118. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/target-element-action.hbs +0 -1
  119. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +0 -1
  120. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +0 -1
  121. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/page-navigation.hbs +0 -1
  122. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/target-element-action.hbs +0 -1
  123. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +0 -1
  124. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +0 -1
  125. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/page-navigation.hbs +0 -1
@@ -23,7 +23,8 @@ export const assertionPatterns: StepPattern[] = [
23
23
  const resolved = context.selectorResolver.resolveSelector(
24
24
  step.selectorRef,
25
25
  context.featureName,
26
- step.elementType
26
+ step.elementType,
27
+ step.nth
27
28
  );
28
29
  // Use selector's value as the path for URL assertion
29
30
  path = resolved.value || path;
@@ -42,6 +43,134 @@ export const assertionPatterns: StepPattern[] = [
42
43
  };
43
44
  },
44
45
  priority: 13, // Higher priority than general visibility
46
+ },
47
+ // Pattern: Then User see [panel] dialog with {{kudo.title}} hidden -> toBeHidden()
48
+ {
49
+ name: 'see-with-variable-hidden',
50
+ matcher: (step: ParsedStep) =>
51
+ (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
52
+ !!step.selectorRef &&
53
+ !!step.dataRef &&
54
+ step.text.includes('with') &&
55
+ /\bis hidden\b/.test(step.text),
56
+ generator: (step, context) => {
57
+ let resolved: any = {};
58
+ try {
59
+ resolved = context.selectorResolver.resolveSelector(
60
+ step.selectorRef!,
61
+ context.featureName,
62
+ step.elementType,
63
+ step.nth
64
+ );
65
+ } catch (error) {}
66
+
67
+ let dataValue: string;
68
+ try {
69
+ dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
70
+ } catch (error) {
71
+ dataValue = `\${${step.dataRef}}`;
72
+ }
73
+
74
+ const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
75
+
76
+ if (resolved.strategy === 'locator') {
77
+ const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
78
+ ...resolved, dataValue, nth: resolved.nth || 0,
79
+ });
80
+ return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
81
+ }
82
+
83
+ if (resolved.strategy === 'role' && resolved.role) {
84
+ const code = context.templateEngine.renderStep('hidden-with-role-variable-assertion', {
85
+ role: resolved.role,
86
+ name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
87
+ dataValue,
88
+ nth: resolved.nth || 0,
89
+ });
90
+ return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
91
+ }
92
+
93
+ const selectorValue = resolved.value || '';
94
+ if (selectorValue && selectorValue.trim()) {
95
+ const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
96
+ ...resolved, dataValue, nth: resolved.nth || 0,
97
+ });
98
+ return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
99
+ }
100
+
101
+ const code = context.templateEngine.renderStep('hidden-with-variable-assertion', {
102
+ selectorRef: step.selectorRef,
103
+ selectorValue: resolved.value || '',
104
+ dataValue,
105
+ nth: resolved.nth || 0,
106
+ });
107
+ return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
108
+ },
109
+ priority: 14,
110
+ },
111
+ // Pattern: Then User see [submit] button with {{label}} disabled -> toBeDisabled()
112
+ {
113
+ name: 'see-with-variable-disabled',
114
+ matcher: (step: ParsedStep) =>
115
+ (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
116
+ !!step.selectorRef &&
117
+ !!step.dataRef &&
118
+ step.text.includes('with') &&
119
+ /\bis disabled\b/.test(step.text),
120
+ generator: (step, context) => {
121
+ let resolved: any = {};
122
+ try {
123
+ resolved = context.selectorResolver.resolveSelector(
124
+ step.selectorRef!,
125
+ context.featureName,
126
+ step.elementType,
127
+ step.nth
128
+ );
129
+ } catch (error) {}
130
+
131
+ let dataValue: string;
132
+ try {
133
+ dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
134
+ } catch (error) {
135
+ dataValue = `\${${step.dataRef}}`;
136
+ }
137
+
138
+ const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
139
+
140
+ if (resolved.strategy === 'locator') {
141
+ const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
142
+ ...resolved, dataValue, nth: resolved.nth || 0,
143
+ });
144
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
145
+ }
146
+
147
+ if (resolved.strategy === 'role' && resolved.role) {
148
+ const code = context.templateEngine.renderStep('disabled-with-role-variable-assertion', {
149
+ role: resolved.role,
150
+ name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
151
+ dataValue,
152
+ nth: resolved.nth || 0,
153
+ });
154
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
155
+ }
156
+
157
+ const selectorValue = resolved.value || '';
158
+ if (selectorValue && selectorValue.trim()) {
159
+ const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
160
+ ...resolved, dataValue, nth: resolved.nth || 0,
161
+ });
162
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
163
+ }
164
+
165
+ const code = context.templateEngine.renderStep('disabled-with-variable-assertion', {
166
+ selectorRef: step.selectorRef,
167
+ selectorValue: resolved.value || '',
168
+ dataValue,
169
+ nth: resolved.nth || 0,
170
+ });
171
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
172
+ },
173
+ priority: 14,
45
174
  },
46
175
  // NEW: Assertion with variable (handles both empty selector and static + variable)
47
176
  // Pattern: Then User see [error] message with {{fail_message}}
@@ -61,8 +190,9 @@ export const assertionPatterns: StepPattern[] = [
61
190
  try {
62
191
  resolved = context.selectorResolver.resolveSelector(
63
192
  step.selectorRef!,
64
- context.featureName, // Pass feature context from step context
65
- step.elementType
193
+ context.featureName,
194
+ step.elementType,
195
+ step.nth
66
196
  );
67
197
  } catch (error) {
68
198
  // Selector not in YAML or context issue - will use variable-only
@@ -124,7 +254,10 @@ export const assertionPatterns: StepPattern[] = [
124
254
  } else {
125
255
  // Empty selector - use variable only
126
256
  const code = context.templateEngine.renderStep('visible-with-variable-assertion', {
127
- variable: escapedVariable
257
+ selectorRef: step.selectorRef,
258
+ selectorValue: resolved.value || '',
259
+ dataValue,
260
+ nth: resolved.nth || 0,
128
261
  });
129
262
  return {
130
263
  code,
@@ -132,46 +265,7 @@ export const assertionPatterns: StepPattern[] = [
132
265
  };
133
266
  }
134
267
  },
135
- priority: 13, // HIGHEST priority - match before should-be-visible-with-value
136
- },
137
- {
138
- name: 'should-be-visible-with-value',
139
- matcher: (step: ParsedStep) =>
140
- (step.text.includes('should see') ||
141
- step.text.match(/\b(see|sees)\s+\[/)) &&
142
- !!step.selectorRef &&
143
- !!step.dataRef &&
144
- step.text.includes('with'),
145
- generator: (step, context) => {
146
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
147
-
148
- // Resolve the data value
149
- let dataValue: string;
150
- try {
151
- dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
152
- } catch (error) {
153
- dataValue = `\${${step.dataRef}}`;
154
- }
155
-
156
- // Container types (row, list-item/listitem) use .filter({ hasText }) instead of regex
157
- const filterTypes = ['row', 'listitem', 'list-item'];
158
- const normalizedType = step.elementType || '';
159
- const useFilter = filterTypes.includes(normalizedType) || filterTypes.includes(resolved.role || '');
160
-
161
- // When using .filter({ hasText }), name is redundant — data value identifies the element
162
- const templateData = useFilter
163
- ? { ...(() => { const { name: _n, ...rest } = resolved; return rest; })(), dataValue, dataRef: step.dataRef }
164
- : { ...resolved, dataValue, dataRef: step.dataRef };
165
-
166
- const templateName = useFilter ? 'visible-with-filter-assertion' : 'visible-with-value-assertion';
167
- const code = context.templateEngine.renderStep(templateName, templateData);
168
-
169
- return {
170
- code,
171
- comment: `Assert ${step.selectorRef} with value ${step.dataRef}`,
172
- };
173
- },
174
- priority: 11, // Lower than see-with-variable (13) so variable-only pattern matches first
268
+ priority: 13,
175
269
  },
176
270
  {
177
271
  name: 'should-be-visible',
@@ -182,7 +276,7 @@ export const assertionPatterns: StepPattern[] = [
182
276
  step.text.match(/\b(see|sees)\s+\[/)) &&
183
277
  !!step.selectorRef,
184
278
  generator: (step, context) => {
185
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
279
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
186
280
  const code = context.templateEngine.renderStep('visible-assertion', { ...resolved, selectorRef: step.selectorRef });
187
281
  return {
188
282
  code,
@@ -192,15 +286,14 @@ export const assertionPatterns: StepPattern[] = [
192
286
  priority: 10,
193
287
  },
194
288
  {
195
- name: 'should-not-be-visible',
289
+ name: 'is-hidden',
290
+ // "see [target] is hidden" — no variable version; with-variable handled by see-with-variable-hidden
196
291
  matcher: (step: ParsedStep) =>
197
- (step.text.includes('should not be visible') ||
198
- step.text.includes('should be hidden') ||
199
- step.text.includes('should not see')) &&
200
- !!step.selectorRef,
292
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis hidden\b/.test(step.text) &&
293
+ !step.dataRef && !!step.selectorRef,
201
294
  generator: (step, context) => {
202
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
203
- const code = context.templateEngine.renderStep('not-visible-assertion', { ...resolved, selectorRef: step.selectorRef });
295
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
296
+ const code = context.templateEngine.renderStep('is-hidden-assertion', { ...resolved, selectorRef: step.selectorRef });
204
297
  return {
205
298
  code,
206
299
  comment: `Assert ${step.selectorRef} is not visible`,
@@ -211,37 +304,37 @@ export const assertionPatterns: StepPattern[] = [
211
304
  {
212
305
  name: 'should-be-enabled',
213
306
  matcher: (step: ParsedStep) =>
214
- step.text.includes('should be enabled') && !!step.selectorRef,
307
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis enabled\b/.test(step.text) && !!step.selectorRef,
215
308
  generator: (step, context) => {
216
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
309
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
217
310
  const code = context.templateEngine.renderStep('enabled-assertion', { ...resolved, selectorRef: step.selectorRef });
218
311
  return {
219
312
  code,
220
313
  comment: `Assert ${step.selectorRef} is enabled`,
221
314
  };
222
315
  },
223
- priority: 9,
316
+ priority: 11,
224
317
  },
225
318
  {
226
319
  name: 'should-be-disabled',
227
320
  matcher: (step: ParsedStep) =>
228
- step.text.includes('should be disabled') && !!step.selectorRef,
321
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis disabled\b/.test(step.text) && !!step.selectorRef,
229
322
  generator: (step, context) => {
230
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
323
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
231
324
  const code = context.templateEngine.renderStep('disabled-assertion', { ...resolved, selectorRef: step.selectorRef });
232
325
  return {
233
326
  code,
234
327
  comment: `Assert ${step.selectorRef} is disabled`,
235
328
  };
236
329
  },
237
- priority: 9,
330
+ priority: 11,
238
331
  },
239
332
  {
240
333
  name: 'should-contain-text',
241
334
  matcher: (step: ParsedStep) =>
242
335
  step.text.includes('should contain') && !!step.selectorRef && !!(step.value || step.dataRef),
243
336
  generator: (step, context) => {
244
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
337
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
245
338
  const expectedText = step.value || `\${${step.dataRef}}`;
246
339
  const code = context.templateEngine.renderStep('contain-text-assertion', {
247
340
  ...resolved,
@@ -259,7 +352,7 @@ export const assertionPatterns: StepPattern[] = [
259
352
  matcher: (step: ParsedStep) =>
260
353
  step.text.includes('should have text') && !!step.selectorRef && !!(step.value || step.dataRef),
261
354
  generator: (step, context) => {
262
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
355
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
263
356
  const expectedText = step.value || `\${${step.dataRef}}`;
264
357
  const code = context.templateEngine.renderStep('have-text-assertion', {
265
358
  ...resolved,
@@ -273,60 +366,60 @@ export const assertionPatterns: StepPattern[] = [
273
366
  priority: 10,
274
367
  },
275
368
  {
276
- name: 'should-be-empty',
369
+ name: 'is-empty',
277
370
  matcher: (step: ParsedStep) =>
278
- step.text.includes('should be empty') && !!step.selectorRef,
371
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis empty\b/.test(step.text) && !!step.selectorRef,
279
372
  generator: (step, context) => {
280
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
373
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
281
374
  const code = context.templateEngine.renderStep('empty-assertion', { ...resolved, selectorRef: step.selectorRef });
282
375
  return {
283
376
  code,
284
377
  comment: `Assert ${step.selectorRef} is empty`,
285
378
  };
286
379
  },
287
- priority: 8,
380
+ priority: 11,
288
381
  },
289
382
  {
290
- name: 'should-be-checked',
383
+ name: 'is-checked',
291
384
  matcher: (step: ParsedStep) =>
292
- step.text.includes('should be checked') && !!step.selectorRef,
385
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis checked\b/.test(step.text) && !!step.selectorRef,
293
386
  generator: (step, context) => {
294
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
387
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
295
388
  const code = context.templateEngine.renderStep('checked-assertion', { ...resolved, selectorRef: step.selectorRef });
296
389
  return {
297
390
  code,
298
391
  comment: `Assert ${step.selectorRef} is checked`,
299
392
  };
300
393
  },
301
- priority: 8,
394
+ priority: 11,
302
395
  },
303
396
  {
304
- name: 'should-be-unchecked',
397
+ name: 'is-unchecked',
305
398
  matcher: (step: ParsedStep) =>
306
- step.text.includes('should be unchecked') && !!step.selectorRef,
399
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis unchecked\b/.test(step.text) && !!step.selectorRef,
307
400
  generator: (step, context) => {
308
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
401
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
309
402
  const code = context.templateEngine.renderStep('not-checked-assertion', { ...resolved, selectorRef: step.selectorRef });
310
403
  return {
311
404
  code,
312
405
  comment: `Assert ${step.selectorRef} is unchecked`,
313
406
  };
314
407
  },
315
- priority: 8,
408
+ priority: 11,
316
409
  },
317
410
  {
318
- name: 'should-be-focused',
411
+ name: 'is-focused',
319
412
  matcher: (step: ParsedStep) =>
320
- step.text.includes('should be focused') && !!step.selectorRef,
413
+ /\b(see|sees)\s+\[/.test(step.text) && /\bis focused\b/.test(step.text) && !!step.selectorRef,
321
414
  generator: (step, context) => {
322
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
415
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
323
416
  const code = context.templateEngine.renderStep('focused-assertion', { ...resolved, selectorRef: step.selectorRef });
324
417
  return {
325
418
  code,
326
419
  comment: `Assert ${step.selectorRef} is focused`,
327
420
  };
328
421
  },
329
- priority: 7,
422
+ priority: 11,
330
423
  },
331
424
  {
332
425
  name: 'see-data-text',
@@ -379,7 +472,7 @@ export const assertionPatterns: StepPattern[] = [
379
472
  }
380
473
  }
381
474
 
382
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
475
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
383
476
  const code = context.templateEngine.renderStep('list-item-count-assertion', {
384
477
  ...resolved,
385
478
  expectedCount,
@@ -413,7 +506,7 @@ export const assertionPatterns: StepPattern[] = [
413
506
  }
414
507
  }
415
508
 
416
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
509
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
417
510
  const code = context.templateEngine.renderStep('count-assertion', {
418
511
  ...resolved,
419
512
  expectedCount,
@@ -7,12 +7,46 @@ import { StepPattern } from './types';
7
7
  * Resolvers return metadata, templates handle framework syntax
8
8
  */
9
9
  export const formPatterns: StepPattern[] = [
10
+ {
11
+ name: 'upload-file',
12
+ matcher: (step: ParsedStep) =>
13
+ (step.text.includes('fill') || step.text.includes('fills')) &&
14
+ step.elementType === 'uploader' &&
15
+ !!step.selectorRef &&
16
+ !!(step.dataRef || step.value),
17
+ generator: (step, context) => {
18
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
19
+
20
+ let fileName: string;
21
+ if (step.dataRef) {
22
+ try {
23
+ fileName = context.dataResolver.resolveData(step.dataRef, context.featureName);
24
+ } catch (error) {
25
+ fileName = `\${${step.dataRef}}`;
26
+ }
27
+ } else {
28
+ fileName = step.value!;
29
+ }
30
+
31
+ const code = context.templateEngine.renderStep('upload-action', {
32
+ ...resolved,
33
+ selectorRef: step.selectorRef,
34
+ fileName,
35
+ });
36
+
37
+ return {
38
+ code,
39
+ comment: `Upload file ${fileName} via ${step.selectorRef}`,
40
+ };
41
+ },
42
+ priority: 12,
43
+ },
10
44
  {
11
45
  name: 'fill-input',
12
46
  matcher: (step: ParsedStep) =>
13
47
  (step.text.includes('fills') || step.text.includes('fill') || step.text.includes('inputs') || step.text.includes('input')) && !!step.selectorRef && !!(step.dataRef || step.value),
14
48
  generator: (step, context) => {
15
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
49
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
16
50
 
17
51
  // Resolve data reference to actual value
18
52
  let value: string;
@@ -46,7 +80,7 @@ export const formPatterns: StepPattern[] = [
46
80
  matcher: (step: ParsedStep) =>
47
81
  step.text.includes('checks') && !!step.selectorRef,
48
82
  generator: (step, context) => {
49
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
83
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
50
84
 
51
85
  const code = context.templateEngine.renderStep('check-action', { ...resolved, selectorRef: step.selectorRef });
52
86
 
@@ -62,7 +96,7 @@ export const formPatterns: StepPattern[] = [
62
96
  matcher: (step: ParsedStep) =>
63
97
  step.text.includes('unchecks') && !!step.selectorRef,
64
98
  generator: (step, context) => {
65
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
99
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
66
100
 
67
101
  const code = context.templateEngine.renderStep('uncheck-action', { ...resolved, selectorRef: step.selectorRef });
68
102
 
@@ -80,7 +114,7 @@ export const formPatterns: StepPattern[] = [
80
114
  !!step.selectorRef &&
81
115
  !!(step.dataRef || step.value),
82
116
  generator: (step, context) => {
83
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
117
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
84
118
 
85
119
  // Resolve data reference to actual value
86
120
  let value: string;
@@ -130,7 +164,7 @@ export const formPatterns: StepPattern[] = [
130
164
  matcher: (step: ParsedStep) =>
131
165
  step.text.includes('clears') && !!step.selectorRef,
132
166
  generator: (step, context) => {
133
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
167
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
134
168
 
135
169
  const code = context.templateEngine.renderStep('clear-action', { ...resolved, selectorRef: step.selectorRef });
136
170