@sun-asterisk/sungen 1.0.20 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/dist/cli/commands/add.d.ts +3 -0
  2. package/dist/cli/commands/add.d.ts.map +1 -0
  3. package/dist/cli/commands/add.js +27 -0
  4. package/dist/cli/commands/add.js.map +1 -0
  5. package/dist/cli/commands/cache-clear.d.ts +3 -0
  6. package/dist/cli/commands/cache-clear.d.ts.map +1 -0
  7. package/dist/cli/commands/cache-clear.js +24 -0
  8. package/dist/cli/commands/cache-clear.js.map +1 -0
  9. package/dist/cli/commands/full.d.ts +3 -0
  10. package/dist/cli/commands/full.d.ts.map +1 -0
  11. package/dist/cli/commands/full.js +37 -0
  12. package/dist/cli/commands/full.js.map +1 -0
  13. package/dist/cli/commands/generate.d.ts +3 -0
  14. package/dist/cli/commands/generate.d.ts.map +1 -0
  15. package/dist/cli/commands/generate.js +53 -0
  16. package/dist/cli/commands/generate.js.map +1 -0
  17. package/dist/cli/commands/init.d.ts +3 -0
  18. package/dist/cli/commands/init.d.ts.map +1 -0
  19. package/dist/cli/commands/init.js +20 -0
  20. package/dist/cli/commands/init.js.map +1 -0
  21. package/dist/cli/commands/live-scan.d.ts +3 -0
  22. package/dist/cli/commands/live-scan.d.ts.map +1 -0
  23. package/dist/cli/commands/{live-scan-command.js → live-scan.js} +8 -15
  24. package/dist/cli/commands/live-scan.js.map +1 -0
  25. package/dist/cli/commands/makeauth.d.ts +3 -0
  26. package/dist/cli/commands/makeauth.d.ts.map +1 -0
  27. package/dist/cli/commands/makeauth.js +76 -0
  28. package/dist/cli/commands/makeauth.js.map +1 -0
  29. package/dist/cli/commands/map.d.ts +3 -0
  30. package/dist/cli/commands/map.d.ts.map +1 -0
  31. package/dist/cli/commands/map.js +93 -0
  32. package/dist/cli/commands/map.js.map +1 -0
  33. package/dist/cli/commands/validate.d.ts +3 -0
  34. package/dist/cli/commands/validate.d.ts.map +1 -0
  35. package/dist/cli/commands/validate.js +43 -0
  36. package/dist/cli/commands/validate.js.map +1 -0
  37. package/dist/cli/index.js +29 -442
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/cli/types.d.ts +9 -0
  40. package/dist/cli/types.d.ts.map +1 -0
  41. package/dist/cli/types.js +7 -0
  42. package/dist/cli/types.js.map +1 -0
  43. package/dist/cli/utils.d.ts +6 -0
  44. package/dist/cli/utils.d.ts.map +1 -0
  45. package/dist/cli/utils.js +101 -0
  46. package/dist/cli/utils.js.map +1 -0
  47. package/dist/core/live-scanner/matrix-reader.d.ts.map +1 -1
  48. package/dist/core/live-scanner/matrix-reader.js +2 -40
  49. package/dist/core/live-scanner/matrix-reader.js.map +1 -1
  50. package/dist/core/live-scanner/step-replayer.d.ts.map +1 -1
  51. package/dist/core/live-scanner/step-replayer.js +7 -0
  52. package/dist/core/live-scanner/step-replayer.js.map +1 -1
  53. package/dist/core/validator/selector-validator.d.ts.map +1 -1
  54. package/dist/core/validator/selector-validator.js +2 -1
  55. package/dist/core/validator/selector-validator.js.map +1 -1
  56. package/dist/generators/scaffold-generator/index.d.ts +2 -1
  57. package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
  58. package/dist/generators/scaffold-generator/index.js +21 -1
  59. package/dist/generators/scaffold-generator/index.js.map +1 -1
  60. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
  61. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
  62. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
  63. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
  64. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
  65. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  66. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
  67. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
  68. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
  69. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
  70. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
  71. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
  72. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
  73. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
  74. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
  75. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
  76. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
  77. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
  78. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
  79. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  80. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
  81. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  82. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
  83. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
  84. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
  85. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
  86. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
  87. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
  88. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
  89. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  90. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
  91. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  92. package/dist/generators/test-generator/patterns/assertion-patterns.js +95 -58
  93. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  94. package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -2
  95. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  96. package/dist/generators/test-generator/patterns/form-patterns.js +34 -47
  97. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  98. package/dist/generators/test-generator/patterns/index.d.ts +3 -1
  99. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  100. package/dist/generators/test-generator/patterns/index.js +20 -3
  101. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  102. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -1
  103. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  104. package/dist/generators/test-generator/patterns/interaction-patterns.js +44 -85
  105. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  106. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts +7 -0
  107. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts.map +1 -0
  108. package/dist/generators/test-generator/patterns/legacy-patterns.js +98 -0
  109. package/dist/generators/test-generator/patterns/legacy-patterns.js.map +1 -0
  110. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -2
  111. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
  112. package/dist/generators/test-generator/patterns/navigation-patterns.js +14 -42
  113. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  114. package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -1
  115. package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +1 -1
  116. package/dist/generators/test-generator/patterns/setup-patterns.js +23 -35
  117. package/dist/generators/test-generator/patterns/setup-patterns.js.map +1 -1
  118. package/dist/generators/test-generator/patterns/types.d.ts +18 -3
  119. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  120. package/dist/generators/test-generator/step-mapper.d.ts +0 -15
  121. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  122. package/dist/generators/test-generator/step-mapper.js +4 -106
  123. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  124. package/dist/{executor/test-generator.d.ts → generators/test-generator/types.d.ts} +4 -25
  125. package/dist/generators/test-generator/types.d.ts.map +1 -0
  126. package/dist/generators/test-generator/types.js +106 -0
  127. package/dist/generators/test-generator/types.js.map +1 -0
  128. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  129. package/dist/generators/test-generator/utils/data-resolver.js +8 -17
  130. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  131. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  132. package/dist/generators/test-generator/utils/selector-resolver.js +10 -18
  133. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  134. package/dist/orchestrator/cache-manager.d.ts +1 -23
  135. package/dist/orchestrator/cache-manager.d.ts.map +1 -1
  136. package/dist/orchestrator/cache-manager.js +1 -87
  137. package/dist/orchestrator/cache-manager.js.map +1 -1
  138. package/dist/orchestrator/pipeline.d.ts +11 -28
  139. package/dist/orchestrator/pipeline.d.ts.map +1 -1
  140. package/dist/orchestrator/pipeline.js +52 -371
  141. package/dist/orchestrator/pipeline.js.map +1 -1
  142. package/dist/orchestrator/reporter.d.ts +1 -1
  143. package/dist/orchestrator/reporter.d.ts.map +1 -1
  144. package/dist/orchestrator/screen-manager.js +1 -1
  145. package/dist/orchestrator/screen-manager.js.map +1 -1
  146. package/dist/utils/feature-finder.d.ts +9 -0
  147. package/dist/utils/feature-finder.d.ts.map +1 -0
  148. package/dist/utils/feature-finder.js +67 -0
  149. package/dist/utils/feature-finder.js.map +1 -0
  150. package/dist/utils/screen-paths.d.ts +10 -0
  151. package/dist/utils/screen-paths.d.ts.map +1 -0
  152. package/dist/utils/screen-paths.js +73 -0
  153. package/dist/utils/screen-paths.js.map +1 -0
  154. package/dist/utils/selector-loader.d.ts +6 -0
  155. package/dist/utils/selector-loader.d.ts.map +1 -0
  156. package/dist/utils/selector-loader.js +20 -0
  157. package/dist/utils/selector-loader.js.map +1 -0
  158. package/dist/utils/selector-types.d.ts +7 -0
  159. package/dist/utils/selector-types.d.ts.map +1 -0
  160. package/dist/utils/selector-types.js +19 -0
  161. package/dist/utils/selector-types.js.map +1 -0
  162. package/dist/utils/test-data-loader.d.ts +6 -0
  163. package/dist/utils/test-data-loader.d.ts.map +1 -0
  164. package/dist/utils/test-data-loader.js +20 -0
  165. package/dist/utils/test-data-loader.js.map +1 -0
  166. package/dist/utils/yaml-io.d.ts +14 -0
  167. package/dist/utils/yaml-io.d.ts.map +1 -0
  168. package/dist/utils/yaml-io.js +72 -0
  169. package/dist/utils/yaml-io.js.map +1 -0
  170. package/package.json +1 -1
  171. package/src/cli/commands/add.ts +25 -0
  172. package/src/cli/commands/cache-clear.ts +22 -0
  173. package/src/cli/commands/full.ts +35 -0
  174. package/src/cli/commands/generate.ts +55 -0
  175. package/src/cli/commands/init.ts +17 -0
  176. package/src/cli/commands/{live-scan-command.ts → live-scan.ts} +8 -17
  177. package/src/cli/commands/makeauth.ts +77 -0
  178. package/src/cli/commands/map.ts +97 -0
  179. package/src/cli/commands/validate.ts +43 -0
  180. package/src/cli/index.ts +32 -473
  181. package/src/cli/types.ts +9 -0
  182. package/src/cli/utils.ts +106 -0
  183. package/src/core/live-scanner/matrix-reader.ts +2 -8
  184. package/src/core/live-scanner/step-replayer.ts +7 -0
  185. package/src/core/validator/selector-validator.ts +3 -2
  186. package/src/generators/scaffold-generator/index.ts +23 -2
  187. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
  188. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
  189. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
  190. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
  191. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
  192. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  193. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
  194. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
  195. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
  196. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
  197. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
  198. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
  199. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
  200. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
  201. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
  202. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
  203. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
  204. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
  205. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
  206. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  207. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
  208. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  209. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
  210. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
  211. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
  212. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
  213. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
  214. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
  215. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
  216. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  217. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
  218. package/src/generators/test-generator/patterns/assertion-patterns.ts +102 -62
  219. package/src/generators/test-generator/patterns/form-patterns.ts +38 -60
  220. package/src/generators/test-generator/patterns/index.ts +22 -4
  221. package/src/generators/test-generator/patterns/interaction-patterns.ts +47 -107
  222. package/src/generators/test-generator/patterns/legacy-patterns.ts +104 -0
  223. package/src/generators/test-generator/patterns/navigation-patterns.ts +27 -69
  224. package/src/generators/test-generator/patterns/setup-patterns.ts +23 -41
  225. package/src/generators/test-generator/patterns/types.ts +26 -9
  226. package/src/generators/test-generator/step-mapper.ts +4 -124
  227. package/src/generators/test-generator/types.ts +131 -0
  228. package/src/generators/test-generator/utils/data-resolver.ts +8 -13
  229. package/src/generators/test-generator/utils/selector-resolver.ts +15 -17
  230. package/src/orchestrator/cache-manager.ts +1 -107
  231. package/src/orchestrator/pipeline.ts +58 -433
  232. package/src/orchestrator/reporter.ts +1 -1
  233. package/src/orchestrator/screen-manager.ts +1 -1
  234. package/src/utils/feature-finder.ts +33 -0
  235. package/src/utils/screen-paths.ts +37 -0
  236. package/src/utils/selector-loader.ts +23 -0
  237. package/src/utils/selector-types.ts +17 -0
  238. package/src/utils/test-data-loader.ts +23 -0
  239. package/src/utils/yaml-io.ts +33 -0
  240. package/dist/cli/commands/auto-tag-command.d.ts +0 -8
  241. package/dist/cli/commands/auto-tag-command.d.ts.map +0 -1
  242. package/dist/cli/commands/auto-tag-command.js +0 -104
  243. package/dist/cli/commands/auto-tag-command.js.map +0 -1
  244. package/dist/cli/commands/live-scan-command.d.ts +0 -9
  245. package/dist/cli/commands/live-scan-command.d.ts.map +0 -1
  246. package/dist/cli/commands/live-scan-command.js.map +0 -1
  247. package/dist/executor/playwright/playwright-generator.d.ts +0 -33
  248. package/dist/executor/playwright/playwright-generator.d.ts.map +0 -1
  249. package/dist/executor/playwright/playwright-generator.js +0 -136
  250. package/dist/executor/playwright/playwright-generator.js.map +0 -1
  251. package/dist/executor/test-generator.d.ts.map +0 -1
  252. package/dist/executor/test-generator.js +0 -30
  253. package/dist/executor/test-generator.js.map +0 -1
  254. package/dist/generators/cli.d.ts +0 -7
  255. package/dist/generators/cli.d.ts.map +0 -1
  256. package/dist/generators/cli.js +0 -570
  257. package/dist/generators/cli.js.map +0 -1
  258. package/dist/input/cli-adapter.d.ts +0 -75
  259. package/dist/input/cli-adapter.d.ts.map +0 -1
  260. package/dist/input/cli-adapter.js +0 -218
  261. package/dist/input/cli-adapter.js.map +0 -1
  262. package/dist/input/config-adapter.d.ts +0 -25
  263. package/dist/input/config-adapter.d.ts.map +0 -1
  264. package/dist/input/config-adapter.js +0 -70
  265. package/dist/input/config-adapter.js.map +0 -1
  266. package/dist/input/input-adapter.d.ts +0 -28
  267. package/dist/input/input-adapter.d.ts.map +0 -1
  268. package/dist/input/input-adapter.js +0 -17
  269. package/dist/input/input-adapter.js.map +0 -1
  270. package/dist/input/vscode-adapter.d.ts +0 -62
  271. package/dist/input/vscode-adapter.d.ts.map +0 -1
  272. package/dist/input/vscode-adapter.js +0 -64
  273. package/dist/input/vscode-adapter.js.map +0 -1
  274. package/dist/tools/auto-tagger.d.ts +0 -107
  275. package/dist/tools/auto-tagger.d.ts.map +0 -1
  276. package/dist/tools/auto-tagger.js +0 -502
  277. package/dist/tools/auto-tagger.js.map +0 -1
  278. package/src/cli/commands/auto-tag-command.ts +0 -80
  279. package/src/executor/playwright/playwright-generator.ts +0 -125
  280. package/src/executor/test-generator.ts +0 -90
  281. package/src/generators/cli.ts +0 -640
  282. package/src/input/cli-adapter.ts +0 -233
  283. package/src/input/config-adapter.ts +0 -71
  284. package/src/input/input-adapter.ts +0 -32
  285. package/src/input/vscode-adapter.ts +0 -90
  286. package/src/tools/auto-tagger.ts +0 -572
@@ -8,10 +8,11 @@ import * as path from 'path';
8
8
  import yaml from 'yaml';
9
9
  import { ValidationIssue } from './index';
10
10
  import { SelectorResolver } from '../../generators/test-generator/utils/selector-resolver';
11
+ import { VALID_SELECTOR_TYPES, SelectorType } from '../../utils/selector-types';
11
12
 
12
13
  interface SelectorEntry {
13
14
  selector?: string;
14
- type?: 'placeholder' | 'role' | 'testid' | 'label' | 'text' | 'page';
15
+ type?: SelectorType;
15
16
  value?: string;
16
17
  name?: string;
17
18
  nth?: number;
@@ -19,7 +20,7 @@ interface SelectorEntry {
19
20
 
20
21
  type SelectorFile = Record<string, SelectorEntry>;
21
22
 
22
- const VALID_TYPES = ['placeholder', 'role', 'testid', 'label', 'text', 'page'];
23
+ const VALID_TYPES = VALID_SELECTOR_TYPES;
23
24
 
24
25
  export class SelectorValidator {
25
26
  private screenName: string;
@@ -8,10 +8,11 @@ import path from 'path';
8
8
  import yaml from 'yaml';
9
9
  import { glob } from 'glob';
10
10
  import { SelectorResolver } from '../test-generator/utils/selector-resolver';
11
+ import { SelectorType } from '../../utils/selector-types';
11
12
 
12
13
  export interface ScaffoldElement {
13
14
  locator: string;
14
- type: 'placeholder' | 'role' | 'text' | 'label' | 'page' | 'testid';
15
+ type: SelectorType;
15
16
  value: string;
16
17
  name?: string; // For role type (accessible name), or label text
17
18
  nth: number;
@@ -176,6 +177,9 @@ export class ScaffoldGenerator {
176
177
  if (/^element\b/.test(lowerText) || /^\d*\s*element\b/.test(lowerText)) {
177
178
  return 'element';
178
179
  }
180
+ if (/^(column|columnheader)\b/.test(lowerText) || /^\d*\s*(column|columnheader)\b/.test(lowerText)) {
181
+ return 'column';
182
+ }
179
183
  if (/^(logo|image|img|icon)\b/.test(lowerText) || /^\d*\s*(logo|image|img|icon)\b/.test(lowerText)) {
180
184
  return 'img';
181
185
  }
@@ -395,9 +399,19 @@ export class ScaffoldGenerator {
395
399
  };
396
400
 
397
401
  case 'see':
402
+ // Column type: used for table column cell assertions, no DOM selector needed
403
+ if (targetType === 'column') {
404
+ return {
405
+ locator: '',
406
+ type: 'column',
407
+ value: element.rawText,
408
+ nth,
409
+ };
410
+ }
411
+
398
412
  // For assertions - respect targetType for role-based elements
399
413
  if (targetType === 'img' || targetType === 'button' || targetType === 'link' ||
400
- targetType === 'checkbox' || targetType === 'radio' || targetType === 'heading' || targetType === 'dialog') {
414
+ targetType === 'checkbox' || targetType === 'radio' || targetType === 'heading' || targetType === 'dialog' || targetType === 'dropdown') {
401
415
  const seeRoleValue = this.getRoleValue(targetType);
402
416
  return {
403
417
  locator: '',
@@ -469,6 +483,8 @@ export class ScaffoldGenerator {
469
483
  return 'heading';
470
484
  case 'dialog':
471
485
  return 'dialog';
486
+ case 'dropdown':
487
+ return 'button';
472
488
  default:
473
489
  return 'button'; // Default to button
474
490
  }
@@ -643,6 +659,11 @@ export class ScaffoldGenerator {
643
659
  const enriched: ScaffoldResult = { ...scaffold };
644
660
 
645
661
  for (const [key, scaffoldElement] of Object.entries(enriched)) {
662
+ // Column-type elements use header text matching, skip live-scan enrichment
663
+ if (scaffoldElement.type === 'column') continue;
664
+ // Target elements (empty value) are matched by data value at runtime, skip enrichment
665
+ if (!scaffoldElement.value && !scaffoldElement.name) continue;
666
+
646
667
  // Try exact key first, then base key without --N suffix or --type suffix
647
668
  let liveElement = elements[key];
648
669
  if (!liveElement && key.includes('--')) {
@@ -0,0 +1,2 @@
1
+ await {{> locator}}.click();
2
+ await page.getByText('{{selectValue}}', { exact: true }).first().click();
@@ -0,0 +1 @@
1
+ await page.getByRole('{{role}}').filter({ hasText: '{{escapeQuotes dataValue}}' }).waitFor({ state: '{{state}}' });
@@ -0,0 +1 @@
1
+ await page.getByRole('{{role}}').waitFor({ state: '{{state}}' });
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeChecked();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeChecked();
@@ -0,0 +1,3 @@
1
+ await page.waitForLoadState('networkidle');
2
+ const {{columnIndexVar}} = (await page.getByRole('columnheader').allTextContents()).findIndex(h => h.includes('{{columnName}}'));
3
+ await expect(page.getByRole('row').nth({{rowNth}}).getByRole('cell').nth({{columnIndexVar}})).toHaveText('{{dataValue}}');
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toContainText('{{expectedText}}');
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toContainText('{{expectedText}}');
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toHaveCount({{expectedCount}});
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toHaveCount({{expectedCount}});
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeDisabled();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeDisabled();
@@ -1 +1,2 @@
1
- await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeDisabled();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeDisabled();
@@ -1,5 +1,6 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  {{#if name}}
2
- await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
3
+ await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
3
4
  {{else}}
4
- await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
5
- {{/if}}
5
+ await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
6
+ {{/if}}
@@ -1 +1,2 @@
1
- await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeEmpty();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeEmpty();
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeEnabled();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeEnabled();
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeFocused();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeFocused();
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toHaveText('{{expectedText}}');
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toHaveText('{{expectedText}}');
@@ -0,0 +1,2 @@
1
+ await page.waitForLoadState('networkidle');
2
+ await expect(page.getByRole('dialog').getByRole('heading', { name: '{{escapeQuotes dataValue}}' })).toBeHidden();
@@ -1 +1,2 @@
1
- await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeHidden();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeHidden();
@@ -1,5 +1,6 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  {{#if name}}
2
- await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
3
+ await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
3
4
  {{else}}
4
- await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
5
- {{/if}}
5
+ await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
6
+ {{/if}}
@@ -1 +1,2 @@
1
- await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();
@@ -1 +1,2 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  await expect({{> locator}}).toBeHidden();
@@ -1 +1,2 @@
1
- await expect({{> locator}}.getByRole('listitem')).toHaveCount({{expectedCount}});
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}.getByRole('listitem')).toHaveCount({{expectedCount}});
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeChecked({ checked: false });
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeChecked({ checked: false });
@@ -1 +1,2 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  await expect(page).toHaveURL(/{{pathRegex}}/);
@@ -1 +1,2 @@
1
- await expect({{> locator}}).toBeVisible();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator}}).toBeVisible();
@@ -0,0 +1,2 @@
1
+ await page.waitForLoadState('networkidle');
2
+ await expect(page.getByRole('dialog').getByRole('heading', { name: '{{escapeQuotes dataValue}}' })).toBeVisible();
@@ -1 +1,2 @@
1
- await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeVisible();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeVisible();
@@ -1,9 +1,10 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  {{#if name}}
2
3
  {{#if dataValue}}
3
- await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
4
+ await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
4
5
  {{else}}
5
6
  await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
6
7
  {{/if}}
7
8
  {{else}}
8
- await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: '{{escapeQuotes dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
9
- {{/if}}
9
+ await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
10
+ {{/if}}
@@ -1 +1,2 @@
1
- await expect(page.getByText('{{escapeQuotes value}}').filter({ hasText: '{{escapeQuotes dataValue}}' })).toBeVisible();
1
+ await page.waitForLoadState('networkidle');
2
+ await expect(page.getByText('{{escapeQuotes value}}').filter({ hasText: /^{{escapeRegex dataValue}}$/ })).toBeVisible();
@@ -1 +1,2 @@
1
+ await page.waitForLoadState('networkidle');
1
2
  await expect(page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: /.*{{escapeRegex dataValue}}$/ }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeVisible();
@@ -1 +1 @@
1
- await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}');
1
+ await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}', { waitUntil: 'networkidle' });
@@ -1 +1 @@
1
- await {{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}.waitFor({ state: '{{state}}' });
1
+ await {{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}.waitFor({ state: '{{state}}' });
@@ -1,11 +1,45 @@
1
1
  import { ParsedStep } from '../../gherkin-parser';
2
- import { StepPattern } from './types';
2
+ import { StepPattern, StepTemplateData } from './types';
3
3
 
4
4
  /**
5
5
  * Assertion patterns: visibility, text content, state, attributes
6
6
  * Uses template engine for framework-agnostic code generation
7
7
  */
8
8
  export const assertionPatterns: StepPattern[] = [
9
+ // Column cell assertion: "see [Department] column 1 with {{value}}" -> check table cell text
10
+ {
11
+ name: 'column-cell-assertion',
12
+ matcher: (step: ParsedStep) =>
13
+ (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
14
+ !!step.selectorRef &&
15
+ !!step.dataRef &&
16
+ (step.elementType === 'column' || step.elementType === 'columnheader'),
17
+ resolver: (step, context): StepTemplateData => {
18
+ let dataValue: string;
19
+ try {
20
+ dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
21
+ } catch (error) {
22
+ dataValue = `\${${step.dataRef}}`;
23
+ }
24
+
25
+ // getByRole('row') includes header row as nth(0), so Gherkin nth=1 maps to locator nth(1)
26
+ const rowNth = step.nth || 1;
27
+ // Create a safe variable name from column name
28
+ const columnIndexVar = `colIndex${step.selectorRef!.replace(/[^a-zA-Z0-9]/g, '')}`;
29
+
30
+ return {
31
+ templateName: 'column-cell-assertion',
32
+ data: {
33
+ columnName: step.selectorRef,
34
+ rowNth,
35
+ columnIndexVar,
36
+ dataValue,
37
+ },
38
+ comment: `Assert ${step.selectorRef} column row ${step.nth || 1} has text ${step.dataRef}`,
39
+ };
40
+ },
41
+ priority: 15, // Higher than all other assertion patterns
42
+ },
9
43
  // Page assertion pattern: "see [home] page" -> check URL matches selector value
10
44
  {
11
45
  name: 'page-assertion',
@@ -14,9 +48,9 @@ export const assertionPatterns: StepPattern[] = [
14
48
  step.text.includes('see') ||
15
49
  step.text.includes('sees')) &&
16
50
  step.elementType === 'page',
17
- generator: (step, context) => {
51
+ resolver: (step, context): StepTemplateData => {
18
52
  let path = step.featurePath || '/';
19
-
53
+
20
54
  // If selector is present, extract path from selector's value attribute
21
55
  if (step.selectorRef) {
22
56
  try {
@@ -33,12 +67,12 @@ export const assertionPatterns: StepPattern[] = [
33
67
  path = step.featurePath || '/';
34
68
  }
35
69
  }
36
-
70
+
37
71
  // Convert path to regex-safe string
38
72
  const pathRegex = path.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
39
- const code = context.templateEngine.renderStep('page-assertion', { pathRegex });
40
73
  return {
41
- code,
74
+ templateName: 'page-assertion',
75
+ data: { pathRegex },
42
76
  comment: step.selectorRef ? `Assert on ${step.selectorRef} page` : `Assert on page`,
43
77
  };
44
78
  },
@@ -81,6 +115,14 @@ export const assertionPatterns: StepPattern[] = [
81
115
  }
82
116
 
83
117
  if (resolved.strategy === 'role' && resolved.role) {
118
+ // Dialog role: check heading inside dialog instead of filter on full text content
119
+ if (resolved.role === 'dialog') {
120
+ const code = context.templateEngine.renderStep('hidden-dialog-heading-assertion', {
121
+ dataValue,
122
+ });
123
+ return { code, comment: `Assert ${step.selectorRef} dialog hidden with heading ${step.dataRef}` };
124
+ }
125
+
84
126
  const code = context.templateEngine.renderStep('hidden-with-role-variable-assertion', {
85
127
  role: resolved.role,
86
128
  name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
@@ -223,6 +265,17 @@ export const assertionPatterns: StepPattern[] = [
223
265
 
224
266
  // Check if it's a role-based selector with data
225
267
  if (resolved.strategy === 'role' && resolved.role) {
268
+ // Dialog role: check heading inside dialog instead of filter on full text content
269
+ if (resolved.role === 'dialog') {
270
+ const code = context.templateEngine.renderStep('visible-dialog-heading-assertion', {
271
+ dataValue,
272
+ });
273
+ return {
274
+ code,
275
+ comment: `Assert ${step.selectorRef} dialog with heading ${step.dataRef}`,
276
+ };
277
+ }
278
+
226
279
  const hasName = resolved.name && resolved.name.trim();
227
280
  // For img role: images have no text content, so put dataValue in name instead of filter
228
281
  const isImgRole = resolved.role === 'img';
@@ -278,11 +331,11 @@ export const assertionPatterns: StepPattern[] = [
278
331
  step.text.includes('should see') ||
279
332
  step.text.match(/\b(see|sees)\s+\[/)) &&
280
333
  !!step.selectorRef,
281
- generator: (step, context) => {
334
+ resolver: (step, context): StepTemplateData => {
282
335
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
283
- const code = context.templateEngine.renderStep('visible-assertion', { ...resolved, selectorRef: step.selectorRef });
284
336
  return {
285
- code,
337
+ templateName: 'visible-assertion',
338
+ data: { ...resolved, selectorRef: step.selectorRef },
286
339
  comment: `Assert ${step.selectorRef} is visible`,
287
340
  };
288
341
  },
@@ -294,11 +347,11 @@ export const assertionPatterns: StepPattern[] = [
294
347
  matcher: (step: ParsedStep) =>
295
348
  /\b(see|sees)\s+\[/.test(step.text) && /\bis hidden\b/.test(step.text) &&
296
349
  !step.dataRef && !!step.selectorRef,
297
- generator: (step, context) => {
350
+ resolver: (step, context): StepTemplateData => {
298
351
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
299
- const code = context.templateEngine.renderStep('is-hidden-assertion', { ...resolved, selectorRef: step.selectorRef });
300
352
  return {
301
- code,
353
+ templateName: 'is-hidden-assertion',
354
+ data: { ...resolved, selectorRef: step.selectorRef },
302
355
  comment: `Assert ${step.selectorRef} is not visible`,
303
356
  };
304
357
  },
@@ -308,11 +361,11 @@ export const assertionPatterns: StepPattern[] = [
308
361
  name: 'should-be-enabled',
309
362
  matcher: (step: ParsedStep) =>
310
363
  /\b(see|sees)\s+\[/.test(step.text) && /\bis enabled\b/.test(step.text) && !!step.selectorRef,
311
- generator: (step, context) => {
364
+ resolver: (step, context): StepTemplateData => {
312
365
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
313
- const code = context.templateEngine.renderStep('enabled-assertion', { ...resolved, selectorRef: step.selectorRef });
314
366
  return {
315
- code,
367
+ templateName: 'enabled-assertion',
368
+ data: { ...resolved, selectorRef: step.selectorRef },
316
369
  comment: `Assert ${step.selectorRef} is enabled`,
317
370
  };
318
371
  },
@@ -322,11 +375,11 @@ export const assertionPatterns: StepPattern[] = [
322
375
  name: 'should-be-disabled',
323
376
  matcher: (step: ParsedStep) =>
324
377
  /\b(see|sees)\s+\[/.test(step.text) && /\bis disabled\b/.test(step.text) && !!step.selectorRef,
325
- generator: (step, context) => {
378
+ resolver: (step, context): StepTemplateData => {
326
379
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
327
- const code = context.templateEngine.renderStep('disabled-assertion', { ...resolved, selectorRef: step.selectorRef });
328
380
  return {
329
- code,
381
+ templateName: 'disabled-assertion',
382
+ data: { ...resolved, selectorRef: step.selectorRef },
330
383
  comment: `Assert ${step.selectorRef} is disabled`,
331
384
  };
332
385
  },
@@ -336,15 +389,12 @@ export const assertionPatterns: StepPattern[] = [
336
389
  name: 'should-contain-text',
337
390
  matcher: (step: ParsedStep) =>
338
391
  step.text.includes('should contain') && !!step.selectorRef && !!(step.value || step.dataRef),
339
- generator: (step, context) => {
392
+ resolver: (step, context): StepTemplateData => {
340
393
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
341
394
  const expectedText = step.value || `\${${step.dataRef}}`;
342
- const code = context.templateEngine.renderStep('contain-text-assertion', {
343
- ...resolved,
344
- expectedText,
345
- });
346
395
  return {
347
- code,
396
+ templateName: 'contain-text-assertion',
397
+ data: { ...resolved, expectedText },
348
398
  comment: `Assert ${step.selectorRef} contains "${step.value || step.dataRef}"`,
349
399
  };
350
400
  },
@@ -354,15 +404,12 @@ export const assertionPatterns: StepPattern[] = [
354
404
  name: 'should-have-text',
355
405
  matcher: (step: ParsedStep) =>
356
406
  step.text.includes('should have text') && !!step.selectorRef && !!(step.value || step.dataRef),
357
- generator: (step, context) => {
407
+ resolver: (step, context): StepTemplateData => {
358
408
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
359
409
  const expectedText = step.value || `\${${step.dataRef}}`;
360
- const code = context.templateEngine.renderStep('have-text-assertion', {
361
- ...resolved,
362
- expectedText,
363
- });
364
410
  return {
365
- code,
411
+ templateName: 'have-text-assertion',
412
+ data: { ...resolved, expectedText },
366
413
  comment: `Assert ${step.selectorRef} has text "${step.value || step.dataRef}"`,
367
414
  };
368
415
  },
@@ -372,11 +419,11 @@ export const assertionPatterns: StepPattern[] = [
372
419
  name: 'is-empty',
373
420
  matcher: (step: ParsedStep) =>
374
421
  /\b(see|sees)\s+\[/.test(step.text) && /\bis empty\b/.test(step.text) && !!step.selectorRef,
375
- generator: (step, context) => {
422
+ resolver: (step, context): StepTemplateData => {
376
423
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
377
- const code = context.templateEngine.renderStep('empty-assertion', { ...resolved, selectorRef: step.selectorRef });
378
424
  return {
379
- code,
425
+ templateName: 'empty-assertion',
426
+ data: { ...resolved, selectorRef: step.selectorRef },
380
427
  comment: `Assert ${step.selectorRef} is empty`,
381
428
  };
382
429
  },
@@ -386,11 +433,11 @@ export const assertionPatterns: StepPattern[] = [
386
433
  name: 'is-checked',
387
434
  matcher: (step: ParsedStep) =>
388
435
  /\b(see|sees)\s+\[/.test(step.text) && /\bis checked\b/.test(step.text) && !!step.selectorRef,
389
- generator: (step, context) => {
436
+ resolver: (step, context): StepTemplateData => {
390
437
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
391
- const code = context.templateEngine.renderStep('checked-assertion', { ...resolved, selectorRef: step.selectorRef });
392
438
  return {
393
- code,
439
+ templateName: 'checked-assertion',
440
+ data: { ...resolved, selectorRef: step.selectorRef },
394
441
  comment: `Assert ${step.selectorRef} is checked`,
395
442
  };
396
443
  },
@@ -400,11 +447,11 @@ export const assertionPatterns: StepPattern[] = [
400
447
  name: 'is-unchecked',
401
448
  matcher: (step: ParsedStep) =>
402
449
  /\b(see|sees)\s+\[/.test(step.text) && /\bis unchecked\b/.test(step.text) && !!step.selectorRef,
403
- generator: (step, context) => {
450
+ resolver: (step, context): StepTemplateData => {
404
451
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
405
- const code = context.templateEngine.renderStep('not-checked-assertion', { ...resolved, selectorRef: step.selectorRef });
406
452
  return {
407
- code,
453
+ templateName: 'not-checked-assertion',
454
+ data: { ...resolved, selectorRef: step.selectorRef },
408
455
  comment: `Assert ${step.selectorRef} is unchecked`,
409
456
  };
410
457
  },
@@ -414,11 +461,11 @@ export const assertionPatterns: StepPattern[] = [
414
461
  name: 'is-focused',
415
462
  matcher: (step: ParsedStep) =>
416
463
  /\b(see|sees)\s+\[/.test(step.text) && /\bis focused\b/.test(step.text) && !!step.selectorRef,
417
- generator: (step, context) => {
464
+ resolver: (step, context): StepTemplateData => {
418
465
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
419
- const code = context.templateEngine.renderStep('focused-assertion', { ...resolved, selectorRef: step.selectorRef });
420
466
  return {
421
- code,
467
+ templateName: 'focused-assertion',
468
+ data: { ...resolved, selectorRef: step.selectorRef },
422
469
  comment: `Assert ${step.selectorRef} is focused`,
423
470
  };
424
471
  },
@@ -431,7 +478,7 @@ export const assertionPatterns: StepPattern[] = [
431
478
  step.text.match(/\b(see|sees)\b/)) &&
432
479
  !step.selectorRef &&
433
480
  !!step.dataRef,
434
- generator: (step, context) => {
481
+ resolver: (step, context): StepTemplateData => {
435
482
  // Resolve data reference to actual value
436
483
  let dataValue: string;
437
484
  try {
@@ -440,14 +487,13 @@ export const assertionPatterns: StepPattern[] = [
440
487
  dataValue = `\${${step.dataRef}}`;
441
488
  }
442
489
 
443
- const code = context.templateEngine.renderStep('visible-assertion', {
444
- strategy: 'text',
445
- value: dataValue,
446
- nth: 0,
447
- });
448
-
449
490
  return {
450
- code,
491
+ templateName: 'visible-assertion',
492
+ data: {
493
+ strategy: 'text',
494
+ value: dataValue,
495
+ nth: 0,
496
+ },
451
497
  comment: `Assert ${step.dataRef} is visible`,
452
498
  };
453
499
  },
@@ -459,7 +505,7 @@ export const assertionPatterns: StepPattern[] = [
459
505
  step.text.includes('should have') && step.text.includes('count') &&
460
506
  !!step.selectorRef && step.text.includes('items') &&
461
507
  (step.elementType === 'list' || step.elementType === 'list-item' || step.elementType === 'listitem'),
462
- generator: (step, context) => {
508
+ resolver: (step, context): StepTemplateData => {
463
509
  let expectedCount: number | string = 1;
464
510
 
465
511
  const match = step.text.match(/count\s+(\d+)/);
@@ -476,12 +522,9 @@ export const assertionPatterns: StepPattern[] = [
476
522
  }
477
523
 
478
524
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
479
- const code = context.templateEngine.renderStep('list-item-count-assertion', {
480
- ...resolved,
481
- expectedCount,
482
- });
483
525
  return {
484
- code,
526
+ templateName: 'list-item-count-assertion',
527
+ data: { ...resolved, expectedCount },
485
528
  comment: `Assert ${step.selectorRef} list has ${expectedCount} items`,
486
529
  };
487
530
  },
@@ -491,7 +534,7 @@ export const assertionPatterns: StepPattern[] = [
491
534
  name: 'should-have-count',
492
535
  matcher: (step: ParsedStep) =>
493
536
  step.text.includes('should have') && step.text.includes('count') && !!step.selectorRef,
494
- generator: (step, context) => {
537
+ resolver: (step, context): StepTemplateData => {
495
538
  let expectedCount: number | string = 1;
496
539
 
497
540
  // Try literal digit first: "count 10"
@@ -510,12 +553,9 @@ export const assertionPatterns: StepPattern[] = [
510
553
  }
511
554
 
512
555
  const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
513
- const code = context.templateEngine.renderStep('count-assertion', {
514
- ...resolved,
515
- expectedCount,
516
- });
517
556
  return {
518
- code,
557
+ templateName: 'count-assertion',
558
+ data: { ...resolved, expectedCount },
519
559
  comment: `Assert ${step.selectorRef} has count ${expectedCount}`,
520
560
  };
521
561
  },