@sun-asterisk/sungen 1.0.14 → 1.0.15

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 (94) hide show
  1. package/dist/generators/cli.js +1 -1
  2. package/dist/generators/gherkin-parser/index.d.ts +1 -0
  3. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  4. package/dist/generators/gherkin-parser/index.js +8 -0
  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 +39 -16
  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/upload-action.hbs +1 -0
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -0
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -0
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +1 -1
  19. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
  20. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
  21. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
  22. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
  23. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
  24. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
  25. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
  26. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
  27. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
  28. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
  29. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
  30. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
  31. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
  32. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  33. package/dist/generators/test-generator/patterns/assertion-patterns.js +119 -16
  34. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  35. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  36. package/dist/generators/test-generator/patterns/form-patterns.js +37 -5
  37. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  38. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  39. package/dist/generators/test-generator/patterns/interaction-patterns.js +108 -9
  40. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  41. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
  42. package/dist/generators/test-generator/patterns/navigation-patterns.js +1 -1
  43. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  44. package/dist/generators/test-generator/step-mapper.d.ts +1 -0
  45. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  46. package/dist/generators/test-generator/step-mapper.js +15 -0
  47. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  48. package/dist/generators/test-generator/template-engine.d.ts +5 -0
  49. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  50. package/dist/generators/test-generator/template-engine.js +61 -1
  51. package/dist/generators/test-generator/template-engine.js.map +1 -1
  52. package/dist/generators/test-generator/utils/selector-resolver.d.ts +14 -1
  53. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  54. package/dist/generators/test-generator/utils/selector-resolver.js +28 -3
  55. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  56. package/dist/input/cli-adapter.js +1 -1
  57. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  58. package/dist/orchestrator/project-initializer.js +2 -1
  59. package/dist/orchestrator/project-initializer.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/generators/cli.ts +1 -1
  62. package/src/generators/gherkin-parser/index.ts +10 -0
  63. package/src/generators/scaffold-generator/index.ts +43 -20
  64. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-element-with-text.hbs +1 -1
  65. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs +1 -0
  66. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
  67. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -0
  68. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
  69. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
  70. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -0
  71. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
  72. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +1 -1
  73. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
  74. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
  75. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
  76. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
  77. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
  78. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
  79. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
  80. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
  81. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
  82. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
  83. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
  84. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
  85. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
  86. package/src/generators/test-generator/patterns/assertion-patterns.ts +142 -16
  87. package/src/generators/test-generator/patterns/form-patterns.ts +39 -5
  88. package/src/generators/test-generator/patterns/interaction-patterns.ts +118 -11
  89. package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -1
  90. package/src/generators/test-generator/step-mapper.ts +18 -2
  91. package/src/generators/test-generator/template-engine.ts +70 -1
  92. package/src/generators/test-generator/utils/selector-resolver.ts +30 -3
  93. package/src/input/cli-adapter.ts +1 -1
  94. package/src/orchestrator/project-initializer.ts +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"project-initializer.js","sourceRoot":"","sources":["../../src/orchestrator/project-initializer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project-initializer.js","sourceRoot":"","sources":["../../src/orchestrator/project-initializer.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,2CAA6B;AAE7B,MAAa,kBAAkB;IAK7B;QAHQ,iBAAY,GAAa,EAAE,CAAC;QAC5B,iBAAY,GAAa,EAAE,CAAC;QAGlC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QAEnD,qBAAqB;QACrB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,4CAA4C;QAC5C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAE9B,2BAA2B;QAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,gBAAgB;QAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,kBAAkB;QAClB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,MAAM,IAAI,GAAG;YACX,IAAI;YACJ,YAAY;YACZ,OAAO;YACP,iBAAiB;YACjB,eAAe;SAChB,CAAC;QAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC5C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,sBAAsB;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;QAE/D,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACzD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACxD,MAAM,aAAa,GAAG;YACpB,0BAA0B;YAC1B,kBAAkB;YAClB,cAAc;YACd,eAAe;YACf,oBAAoB;SACrB,CAAC;QAEF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,8CAA8C;YAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,oBAAoB,EAAE,GAAG,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YACrF,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,+CAA+C;YAC/C,IAAI,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YACtD,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;YAE1D,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO,IAAI,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBAClD,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAClD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,YAAY;QAClB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAEpD,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACpC,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QAEpD,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxB,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,oCAAoC;QACpC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAC;QAC9F,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;IACrG,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,EAAE,GAAG,WAAW,CAAC,YAAY,EAAE,GAAG,WAAW,CAAC,eAAe,EAAE,CAAC;YAE7E,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,2BAA2B;QACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgFV,CAAC;IACA,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,OAAO;;;;;;;;;;;;;;;;CAgBV,CAAC;IACA,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDV,CAAC;IACA,CAAC;CACF;AApVD,gDAoVC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "AI-Native E2E Test Generator - Generate Playwright tests from Gherkin features using AI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -52,7 +52,7 @@ const program = new Command();
52
52
  program
53
53
  .name('qa-generator')
54
54
  .description('AI-Native QA Selector DSL Generator')
55
- .version('1.0.14');
55
+ .version('1.0.15');
56
56
 
57
57
  // ============================================================================
58
58
  // Command: discover
@@ -3,6 +3,7 @@ import * as Gherkin from '@cucumber/gherkin';
3
3
  import * as Messages from '@cucumber/messages';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
+ import { SelectorResolver } from '../test-generator/utils/selector-resolver';
6
7
 
7
8
  export interface ParsedStep {
8
9
  keyword: string; // Given, When, Then, And, But
@@ -11,6 +12,7 @@ export interface ParsedStep {
11
12
  dataRef?: string; // Extracted data reference like <valid_user.email>
12
13
  value?: string; // Static value if any
13
14
  elementType?: string; // Element type after [Selector] e.g., "button", "text", "link", "field"
15
+ nth?: number; // Positional index from "[Element] field 3" → 3 (0 = not specified)
14
16
  featurePath?: string; // NEW: Feature path for page type (e.g., "/", "/dashboard")
15
17
  }
16
18
 
@@ -141,6 +143,13 @@ export class GherkinParser {
141
143
  elementType = finalWordMatch ? finalWordMatch[1].toLowerCase() : undefined;
142
144
  }
143
145
 
146
+ // Extract positional index from "[Element] field 3", "[Element] button 2", etc.
147
+ let nth = 0;
148
+ if (selectorRef && selectorMatch) {
149
+ const afterElement = text.slice(selectorMatch.index + selectorMatch[0].length);
150
+ nth = SelectorResolver.extractNthFromStep(afterElement);
151
+ }
152
+
144
153
  return {
145
154
  keyword: step.keyword.trim(),
146
155
  text,
@@ -148,6 +157,7 @@ export class GherkinParser {
148
157
  dataRef,
149
158
  value,
150
159
  elementType,
160
+ nth,
151
161
  };
152
162
  }
153
163
 
@@ -95,14 +95,18 @@ export class ScaffoldGenerator {
95
95
  let match: RegExpExecArray | null;
96
96
  while ((match = elementRegex.exec(trimmedLine)) !== null) {
97
97
  const rawText = match[1];
98
- const key = SelectorResolver.generateKey(rawText);
98
+ const baseKey = SelectorResolver.generateKey(rawText);
99
99
 
100
100
  // Extract nth index and target type from text after the element reference
101
101
  // Matches patterns like: [Email] field 1, [Submit] button 2, [Name] 3
102
102
  const afterElement = trimmedLine.slice(match.index + match[0].length);
103
- const nth = this.extractNthIndex(afterElement);
103
+ const nth = SelectorResolver.extractNthFromStep(afterElement);
104
104
  const targetType = this.extractTargetType(afterElement, action);
105
105
 
106
+ // Append nth to key so elements at different positions get distinct keys
107
+ // e.g., [Hãy gửi lời cảm ơn] field 3 → "hay.gui.loi.cam.on--3"
108
+ const key = nth > 0 ? `${baseKey}--${nth}` : baseKey;
109
+
106
110
  // Check if element starts with "target" (semantic indicator for dynamic content)
107
111
  // e.g., [target order], [Target Item] → element should have empty value
108
112
  const isTargetElement = /^target\s/i.test(rawText);
@@ -153,12 +157,21 @@ export class ScaffoldGenerator {
153
157
  if (/^field\b/.test(lowerText) || /^\d*\s*field\b/.test(lowerText) || /^input\b/.test(lowerText)) {
154
158
  return 'field';
155
159
  }
160
+ if (/^textbox\b/.test(lowerText) || /^\d*\s*textbox\b/.test(lowerText)) {
161
+ return 'textbox';
162
+ }
163
+ if (/^textarea\b/.test(lowerText) || /^\d*\s*textarea\b/.test(lowerText)) {
164
+ return 'textarea';
165
+ }
156
166
  if (/^text\b/.test(lowerText) || /^\d*\s*text\b/.test(lowerText)) {
157
167
  return 'text';
158
168
  }
159
169
  if (/^label\b/.test(lowerText) || /^\d*\s*label\b/.test(lowerText)) {
160
170
  return 'label';
161
171
  }
172
+ if (/^uploader\b/.test(lowerText) || /^\d*\s*uploader\b/.test(lowerText)) {
173
+ return 'uploader';
174
+ }
162
175
  if (/^element\b/.test(lowerText) || /^\d*\s*element\b/.test(lowerText)) {
163
176
  return 'element';
164
177
  }
@@ -188,19 +201,6 @@ export class ScaffoldGenerator {
188
201
  * Extract nth index from text after element reference
189
202
  * e.g., " field 1 with" -> 1, " button 2" -> 2, " row 1" -> 1, " text" -> 0
190
203
  */
191
- private extractNthIndex(text: string): number {
192
- // Match patterns like "field 1", "button 2", "row 1", "text 3", or just a number
193
- // The number should come right after optional keywords (field, button, text, link, row, etc.)
194
- const nthRegex = /^\s*(?:field|button|text|link|input|element|item|logo|image|img|icon|raw|table|columnheader|list|listitem|row)?\s*(\d+)\b/i;
195
- const match = nthRegex.exec(text);
196
-
197
- if (match) {
198
- return parseInt(match[1], 10);
199
- }
200
-
201
- return 0;
202
- }
203
-
204
204
  /**
205
205
  * Detect the action type from a Gherkin step line
206
206
  */
@@ -337,6 +337,16 @@ export class ScaffoldGenerator {
337
337
 
338
338
  switch (element.action) {
339
339
  case 'fill':
340
+ // For fill action with uploader type, use label type (file input triggered by label)
341
+ if (targetType === 'uploader') {
342
+ return {
343
+ locator: '',
344
+ type: 'label',
345
+ value: element.rawText,
346
+ nth,
347
+ };
348
+ }
349
+
340
350
  // For fill action with label type, use label type
341
351
  if (targetType === 'label') {
342
352
  return {
@@ -346,13 +356,13 @@ export class ScaffoldGenerator {
346
356
  nth,
347
357
  };
348
358
  }
349
-
350
- // For fill action, use placeholder type
351
- // User should update 'value' with actual placeholder text from inspection
359
+
360
+ // For fill action with field/textbox/textarea types, use role-based locator
352
361
  return {
353
362
  locator: '',
354
- type: 'placeholder',
355
- value: element.rawText, // TODO: Replace with actual placeholder from inspection
363
+ type: 'role',
364
+ value: this.getFillRoleValue(targetType),
365
+ name: element.rawText,
356
366
  nth,
357
367
  };
358
368
 
@@ -479,6 +489,19 @@ export class ScaffoldGenerator {
479
489
  }
480
490
  }
481
491
 
492
+ /**
493
+ * Get ARIA role value for fill actions — all fillable inputs use 'textbox'
494
+ */
495
+ private getFillRoleValue(targetType: string): string {
496
+ switch (targetType) {
497
+ case 'field':
498
+ case 'textbox':
499
+ case 'textarea':
500
+ default:
501
+ return 'textbox';
502
+ }
503
+ }
504
+
482
505
  /**
483
506
  * Generate YAML content from scaffold result
484
507
  */
@@ -1 +1 @@
1
- await {{> locator}}.filter({ hasText: '{{escapeQuotes dataValue}}' }).click();
1
+ await {{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}.click();
@@ -0,0 +1 @@
1
+ await {{> locator}}.setInputFiles('specs/storage/{{fileName}}');
@@ -0,0 +1 @@
1
+ await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeDisabled();
@@ -0,0 +1 @@
1
+ {{#if name}}await expect(page.getByRole('{{role}}', { name: '{{name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();{{else}}await expect(page.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();{{/if}}
@@ -0,0 +1 @@
1
+ await expect(page.getByText(/.*{{variable}}/{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})).toBeDisabled();
@@ -0,0 +1 @@
1
+ await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeHidden();
@@ -0,0 +1 @@
1
+ {{#if name}}await expect(page.getByRole('{{role}}', { name: '{{name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();{{else}}await expect(page.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();{{/if}}
@@ -0,0 +1 @@
1
+ await expect(page.getByText(/.*{{variable}}/{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})).toBeHidden();
@@ -1 +1 @@
1
- await expect({{> locator}}.filter({ hasText: '{{escapeQuotes dataValue}}' })).toBeVisible();
1
+ await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeVisible();
@@ -1 +1 @@
1
- await expect({{> locator}}.filter({ hasText: '{{dataValue}}' })).toBeVisible();
1
+ await expect({{> locator-base}}.filter({ hasText: '{{dataValue}}' }){{> locator-nth}}).toBeVisible();
@@ -0,0 +1 @@
1
+ await {{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}.waitFor({ state: '{{state}}' });
@@ -0,0 +1,10 @@
1
+ {{#switch strategy~}}
2
+ {{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
3
+ {{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
4
+ {{~#case 'placeholder'}}{{> locator-strategies/placeholder}}{{/case~}}
5
+ {{~#case 'label'}}{{> locator-strategies/label}}{{/case~}}
6
+ {{~#case 'text'}}{{> locator-strategies/text}}{{/case~}}
7
+ {{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
8
+ {{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
9
+ {{~#default}}{{> locator-strategies/default}}{{/default~}}
10
+ {{~/switch}}
@@ -0,0 +1 @@
1
+ {{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}
@@ -0,0 +1 @@
1
+ {{pageRoot}}.getByLabel('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
@@ -0,0 +1 @@
1
+ {{pageRoot}}.getByPlaceholder('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
@@ -0,0 +1 @@
1
+ {{#if name}}{{pageRoot}}.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }){{else}}{{pageRoot}}.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{/if}}
@@ -0,0 +1 @@
1
+ {{pageRoot}}.getByTestId('{{value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
@@ -0,0 +1 @@
1
+ {{pageRoot}}.getByText('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
@@ -1 +1,10 @@
1
- {{#if (eq strategy 'testid')}}page.getByTestId('{{value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{else if (eq strategy 'role')}}{{#if name}}page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }){{else}}page.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{/if}}{{else if (eq strategy 'placeholder')}}page.getByPlaceholder('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{else if (eq strategy 'label')}}page.getByLabel('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{else if (eq strategy 'text')}}page.getByText('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{else if (eq strategy 'locator')}}page.locator('{{escapeQuotes value}}'){{else if (eq strategy 'id')}}page.locator('{{selector}}'){{else}}page.locator('{{escapeQuotes selector}}'){{/if}}{{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}
1
+ {{#switch strategy~}}
2
+ {{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
3
+ {{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
4
+ {{~#case 'placeholder'}}{{> locator-strategies/placeholder}}{{/case~}}
5
+ {{~#case 'label'}}{{> locator-strategies/label}}{{/case~}}
6
+ {{~#case 'text'}}{{> locator-strategies/text}}{{/case~}}
7
+ {{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
8
+ {{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
9
+ {{~#default}}{{> locator-strategies/default}}{{/default~}}
10
+ {{~/switch}}{{> locator-nth}}
@@ -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,130 @@ 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
+ /\bhidden\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
+ variable: escapedVariable,
103
+ selectorRef: step.selectorRef,
104
+ });
105
+ return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
106
+ },
107
+ priority: 14,
108
+ },
109
+ // Pattern: Then User see [submit] button with {{label}} disabled -> toBeDisabled()
110
+ {
111
+ name: 'see-with-variable-disabled',
112
+ matcher: (step: ParsedStep) =>
113
+ (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
114
+ !!step.selectorRef &&
115
+ !!step.dataRef &&
116
+ step.text.includes('with') &&
117
+ /\bdisabled\b/.test(step.text),
118
+ generator: (step, context) => {
119
+ let resolved: any = {};
120
+ try {
121
+ resolved = context.selectorResolver.resolveSelector(
122
+ step.selectorRef!,
123
+ context.featureName,
124
+ step.elementType,
125
+ step.nth
126
+ );
127
+ } catch (error) {}
128
+
129
+ let dataValue: string;
130
+ try {
131
+ dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
132
+ } catch (error) {
133
+ dataValue = `\${${step.dataRef}}`;
134
+ }
135
+
136
+ const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
137
+
138
+ if (resolved.strategy === 'locator') {
139
+ const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
140
+ ...resolved, dataValue, nth: resolved.nth || 0,
141
+ });
142
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
143
+ }
144
+
145
+ if (resolved.strategy === 'role' && resolved.role) {
146
+ const code = context.templateEngine.renderStep('disabled-with-role-variable-assertion', {
147
+ role: resolved.role,
148
+ name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
149
+ dataValue,
150
+ nth: resolved.nth || 0,
151
+ });
152
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
153
+ }
154
+
155
+ const selectorValue = resolved.value || '';
156
+ if (selectorValue && selectorValue.trim()) {
157
+ const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
158
+ ...resolved, dataValue, nth: resolved.nth || 0,
159
+ });
160
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
161
+ }
162
+
163
+ const code = context.templateEngine.renderStep('disabled-with-variable-assertion', {
164
+ variable: escapedVariable,
165
+ selectorRef: step.selectorRef,
166
+ });
167
+ return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
168
+ },
169
+ priority: 14,
45
170
  },
46
171
  // NEW: Assertion with variable (handles both empty selector and static + variable)
47
172
  // Pattern: Then User see [error] message with {{fail_message}}
@@ -61,8 +186,9 @@ export const assertionPatterns: StepPattern[] = [
61
186
  try {
62
187
  resolved = context.selectorResolver.resolveSelector(
63
188
  step.selectorRef!,
64
- context.featureName, // Pass feature context from step context
65
- step.elementType
189
+ context.featureName,
190
+ step.elementType,
191
+ step.nth
66
192
  );
67
193
  } catch (error) {
68
194
  // Selector not in YAML or context issue - will use variable-only
@@ -143,7 +269,7 @@ export const assertionPatterns: StepPattern[] = [
143
269
  !!step.dataRef &&
144
270
  step.text.includes('with'),
145
271
  generator: (step, context) => {
146
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
272
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
147
273
 
148
274
  // Resolve the data value
149
275
  let dataValue: string;
@@ -182,7 +308,7 @@ export const assertionPatterns: StepPattern[] = [
182
308
  step.text.match(/\b(see|sees)\s+\[/)) &&
183
309
  !!step.selectorRef,
184
310
  generator: (step, context) => {
185
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
311
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
186
312
  const code = context.templateEngine.renderStep('visible-assertion', { ...resolved, selectorRef: step.selectorRef });
187
313
  return {
188
314
  code,
@@ -199,7 +325,7 @@ export const assertionPatterns: StepPattern[] = [
199
325
  step.text.includes('should not see')) &&
200
326
  !!step.selectorRef,
201
327
  generator: (step, context) => {
202
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
328
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
203
329
  const code = context.templateEngine.renderStep('not-visible-assertion', { ...resolved, selectorRef: step.selectorRef });
204
330
  return {
205
331
  code,
@@ -213,7 +339,7 @@ export const assertionPatterns: StepPattern[] = [
213
339
  matcher: (step: ParsedStep) =>
214
340
  step.text.includes('should be enabled') && !!step.selectorRef,
215
341
  generator: (step, context) => {
216
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
342
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
217
343
  const code = context.templateEngine.renderStep('enabled-assertion', { ...resolved, selectorRef: step.selectorRef });
218
344
  return {
219
345
  code,
@@ -227,7 +353,7 @@ export const assertionPatterns: StepPattern[] = [
227
353
  matcher: (step: ParsedStep) =>
228
354
  step.text.includes('should be disabled') && !!step.selectorRef,
229
355
  generator: (step, context) => {
230
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
356
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
231
357
  const code = context.templateEngine.renderStep('disabled-assertion', { ...resolved, selectorRef: step.selectorRef });
232
358
  return {
233
359
  code,
@@ -241,7 +367,7 @@ export const assertionPatterns: StepPattern[] = [
241
367
  matcher: (step: ParsedStep) =>
242
368
  step.text.includes('should contain') && !!step.selectorRef && !!(step.value || step.dataRef),
243
369
  generator: (step, context) => {
244
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
370
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
245
371
  const expectedText = step.value || `\${${step.dataRef}}`;
246
372
  const code = context.templateEngine.renderStep('contain-text-assertion', {
247
373
  ...resolved,
@@ -259,7 +385,7 @@ export const assertionPatterns: StepPattern[] = [
259
385
  matcher: (step: ParsedStep) =>
260
386
  step.text.includes('should have text') && !!step.selectorRef && !!(step.value || step.dataRef),
261
387
  generator: (step, context) => {
262
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
388
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
263
389
  const expectedText = step.value || `\${${step.dataRef}}`;
264
390
  const code = context.templateEngine.renderStep('have-text-assertion', {
265
391
  ...resolved,
@@ -277,7 +403,7 @@ export const assertionPatterns: StepPattern[] = [
277
403
  matcher: (step: ParsedStep) =>
278
404
  step.text.includes('should be empty') && !!step.selectorRef,
279
405
  generator: (step, context) => {
280
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
406
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
281
407
  const code = context.templateEngine.renderStep('empty-assertion', { ...resolved, selectorRef: step.selectorRef });
282
408
  return {
283
409
  code,
@@ -291,7 +417,7 @@ export const assertionPatterns: StepPattern[] = [
291
417
  matcher: (step: ParsedStep) =>
292
418
  step.text.includes('should be checked') && !!step.selectorRef,
293
419
  generator: (step, context) => {
294
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
420
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
295
421
  const code = context.templateEngine.renderStep('checked-assertion', { ...resolved, selectorRef: step.selectorRef });
296
422
  return {
297
423
  code,
@@ -305,7 +431,7 @@ export const assertionPatterns: StepPattern[] = [
305
431
  matcher: (step: ParsedStep) =>
306
432
  step.text.includes('should be unchecked') && !!step.selectorRef,
307
433
  generator: (step, context) => {
308
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
434
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
309
435
  const code = context.templateEngine.renderStep('not-checked-assertion', { ...resolved, selectorRef: step.selectorRef });
310
436
  return {
311
437
  code,
@@ -319,7 +445,7 @@ export const assertionPatterns: StepPattern[] = [
319
445
  matcher: (step: ParsedStep) =>
320
446
  step.text.includes('should be focused') && !!step.selectorRef,
321
447
  generator: (step, context) => {
322
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
448
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
323
449
  const code = context.templateEngine.renderStep('focused-assertion', { ...resolved, selectorRef: step.selectorRef });
324
450
  return {
325
451
  code,
@@ -379,7 +505,7 @@ export const assertionPatterns: StepPattern[] = [
379
505
  }
380
506
  }
381
507
 
382
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
508
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
383
509
  const code = context.templateEngine.renderStep('list-item-count-assertion', {
384
510
  ...resolved,
385
511
  expectedCount,
@@ -413,7 +539,7 @@ export const assertionPatterns: StepPattern[] = [
413
539
  }
414
540
  }
415
541
 
416
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
542
+ const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
417
543
  const code = context.templateEngine.renderStep('count-assertion', {
418
544
  ...resolved,
419
545
  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