@sun-asterisk/sungen 3.2.29 → 3.2.30

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 (335) hide show
  1. package/dist/cli/commands/delivery.d.ts.map +1 -1
  2. package/dist/cli/commands/delivery.js +2 -1
  3. package/dist/cli/commands/delivery.js.map +1 -1
  4. package/dist/cli/commands/generate.d.ts.map +1 -1
  5. package/dist/cli/commands/generate.js +6 -2
  6. package/dist/cli/commands/generate.js.map +1 -1
  7. package/dist/cli/commands/inspect.js +1 -1
  8. package/dist/cli/commands/inspect.js.map +1 -1
  9. package/dist/cli/commands/results-merge.d.ts.map +1 -1
  10. package/dist/cli/commands/results-merge.js +9 -2
  11. package/dist/cli/commands/results-merge.js.map +1 -1
  12. package/dist/cli/commands/results-sanitize.d.ts +24 -0
  13. package/dist/cli/commands/results-sanitize.d.ts.map +1 -0
  14. package/dist/cli/commands/results-sanitize.js +126 -0
  15. package/dist/cli/commands/results-sanitize.js.map +1 -0
  16. package/dist/cli/commands/script-check.d.ts.map +1 -1
  17. package/dist/cli/commands/script-check.js +3 -1
  18. package/dist/cli/commands/script-check.js.map +1 -1
  19. package/dist/cli/commands/update.d.ts.map +1 -1
  20. package/dist/cli/commands/update.js +3 -0
  21. package/dist/cli/commands/update.js.map +1 -1
  22. package/dist/exporters/api-testcase-formatter.d.ts +3 -6
  23. package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
  24. package/dist/exporters/api-testcase-formatter.js +7 -10
  25. package/dist/exporters/api-testcase-formatter.js.map +1 -1
  26. package/dist/exporters/matrix/build.d.ts.map +1 -1
  27. package/dist/exporters/matrix/build.js +4 -2
  28. package/dist/exporters/matrix/build.js.map +1 -1
  29. package/dist/exporters/matrix/render-coverage-sheet.d.ts.map +1 -1
  30. package/dist/exporters/matrix/render-coverage-sheet.js +3 -1
  31. package/dist/exporters/matrix/render-coverage-sheet.js.map +1 -1
  32. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  33. package/dist/exporters/matrix/render-csv.js +3 -1
  34. package/dist/exporters/matrix/render-csv.js.map +1 -1
  35. package/dist/exporters/matrix/render-overview-sheet.d.ts.map +1 -1
  36. package/dist/exporters/matrix/render-overview-sheet.js +5 -9
  37. package/dist/exporters/matrix/render-overview-sheet.js.map +1 -1
  38. package/dist/exporters/matrix/result-totals.d.ts +23 -0
  39. package/dist/exporters/matrix/result-totals.d.ts.map +1 -0
  40. package/dist/exporters/matrix/result-totals.js +21 -0
  41. package/dist/exporters/matrix/result-totals.js.map +1 -0
  42. package/dist/exporters/matrix/testcase-summary-band.d.ts.map +1 -1
  43. package/dist/exporters/matrix/testcase-summary-band.js +2 -6
  44. package/dist/exporters/matrix/testcase-summary-band.js.map +1 -1
  45. package/dist/exporters/qa-env-secret-denylist.js +2 -2
  46. package/dist/exporters/qa-env-secret-denylist.js.map +1 -1
  47. package/dist/exporters/report-path-sanitizer.d.ts +38 -0
  48. package/dist/exporters/report-path-sanitizer.d.ts.map +1 -0
  49. package/dist/exporters/report-path-sanitizer.js +137 -0
  50. package/dist/exporters/report-path-sanitizer.js.map +1 -0
  51. package/dist/exporters/result-variants.d.ts.map +1 -1
  52. package/dist/exporters/result-variants.js +2 -1
  53. package/dist/exporters/result-variants.js.map +1 -1
  54. package/dist/exporters/sensitive-key.d.ts +31 -0
  55. package/dist/exporters/sensitive-key.d.ts.map +1 -0
  56. package/dist/exporters/sensitive-key.js +50 -0
  57. package/dist/exporters/sensitive-key.js.map +1 -0
  58. package/dist/exporters/test-data-resolver.d.ts +4 -1
  59. package/dist/exporters/test-data-resolver.d.ts.map +1 -1
  60. package/dist/exporters/test-data-resolver.js +12 -2
  61. package/dist/exporters/test-data-resolver.js.map +1 -1
  62. package/dist/generators/gherkin-parser/index.d.ts +3 -0
  63. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  64. package/dist/generators/gherkin-parser/index.js +17 -9
  65. package/dist/generators/gherkin-parser/index.js.map +1 -1
  66. package/dist/generators/gherkin-parser/parent-scope-clause.d.ts +54 -0
  67. package/dist/generators/gherkin-parser/parent-scope-clause.d.ts.map +1 -0
  68. package/dist/generators/gherkin-parser/parent-scope-clause.js +109 -0
  69. package/dist/generators/gherkin-parser/parent-scope-clause.js.map +1 -0
  70. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +23 -18
  71. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  72. package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +4 -19
  73. package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts.map +1 -1
  74. package/dist/generators/test-generator/adapters/appium/appium-adapter.js.map +1 -1
  75. package/dist/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
  76. package/dist/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
  77. package/dist/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
  78. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
  79. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
  80. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
  81. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
  82. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
  83. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
  84. package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
  85. package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
  86. package/dist/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
  87. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +54 -23
  88. package/dist/generators/test-generator/adapters/index.d.ts +1 -1
  89. package/dist/generators/test-generator/adapters/index.d.ts.map +1 -1
  90. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +4 -19
  91. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
  92. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
  93. package/dist/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
  94. package/dist/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
  95. package/dist/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
  96. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  97. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
  98. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
  99. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
  100. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
  101. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +3 -1
  102. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +3 -1
  103. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
  104. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
  105. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
  106. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
  107. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
  108. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
  109. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
  110. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
  111. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
  112. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
  113. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
  114. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
  115. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
  116. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
  117. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  118. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +14 -3
  119. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +13 -2
  120. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
  121. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  122. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  123. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
  124. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
  125. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
  126. package/dist/generators/test-generator/code-generator.d.ts +37 -0
  127. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  128. package/dist/generators/test-generator/code-generator.js +160 -39
  129. package/dist/generators/test-generator/code-generator.js.map +1 -1
  130. package/dist/generators/test-generator/diagnostics.d.ts +79 -5
  131. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  132. package/dist/generators/test-generator/diagnostics.js +78 -4
  133. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  134. package/dist/generators/test-generator/patterns/index.d.ts +24 -5
  135. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  136. package/dist/generators/test-generator/patterns/index.js +92 -14
  137. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  138. package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +18 -0
  139. package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -0
  140. package/dist/generators/test-generator/patterns/with-parent-scope.js +40 -0
  141. package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -0
  142. package/dist/generators/test-generator/step-mapper.d.ts +63 -9
  143. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  144. package/dist/generators/test-generator/step-mapper.js +166 -28
  145. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  146. package/dist/generators/test-generator/template-engine.d.ts +4 -19
  147. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  148. package/dist/generators/test-generator/template-engine.js +17 -12
  149. package/dist/generators/test-generator/template-engine.js.map +1 -1
  150. package/dist/generators/test-generator/utils/data-resolver.d.ts +7 -0
  151. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  152. package/dist/generators/test-generator/utils/data-resolver.js +19 -2
  153. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  154. package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts +41 -0
  155. package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts.map +1 -0
  156. package/dist/generators/test-generator/utils/dialog-scope-identity.js +61 -0
  157. package/dist/generators/test-generator/utils/dialog-scope-identity.js.map +1 -0
  158. package/dist/generators/test-generator/utils/escape-js-string.d.ts +17 -0
  159. package/dist/generators/test-generator/utils/escape-js-string.d.ts.map +1 -0
  160. package/dist/generators/test-generator/utils/escape-js-string.js +29 -0
  161. package/dist/generators/test-generator/utils/escape-js-string.js.map +1 -0
  162. package/dist/generators/test-generator/utils/nearest-match.d.ts +24 -0
  163. package/dist/generators/test-generator/utils/nearest-match.d.ts.map +1 -0
  164. package/dist/generators/test-generator/utils/nearest-match.js +56 -0
  165. package/dist/generators/test-generator/utils/nearest-match.js.map +1 -0
  166. package/dist/generators/test-generator/utils/selector-entry-fields.d.ts +58 -0
  167. package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -0
  168. package/dist/generators/test-generator/utils/selector-entry-fields.js +119 -0
  169. package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -0
  170. package/dist/generators/test-generator/utils/selector-entry-walk.d.ts +25 -0
  171. package/dist/generators/test-generator/utils/selector-entry-walk.d.ts.map +1 -0
  172. package/dist/generators/test-generator/utils/selector-entry-walk.js +54 -0
  173. package/dist/generators/test-generator/utils/selector-entry-walk.js.map +1 -0
  174. package/dist/generators/test-generator/utils/selector-nth-validation.d.ts +59 -0
  175. package/dist/generators/test-generator/utils/selector-nth-validation.d.ts.map +1 -0
  176. package/dist/generators/test-generator/utils/selector-nth-validation.js +91 -0
  177. package/dist/generators/test-generator/utils/selector-nth-validation.js.map +1 -0
  178. package/dist/generators/test-generator/utils/selector-resolver.d.ts +30 -1
  179. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  180. package/dist/generators/test-generator/utils/selector-resolver.js +195 -15
  181. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  182. package/dist/generators/test-generator/utils/selector-type-validation.d.ts +51 -0
  183. package/dist/generators/test-generator/utils/selector-type-validation.d.ts.map +1 -0
  184. package/dist/generators/test-generator/utils/selector-type-validation.js +77 -0
  185. package/dist/generators/test-generator/utils/selector-type-validation.js.map +1 -0
  186. package/dist/harness/data-driven-lint.d.ts.map +1 -1
  187. package/dist/harness/data-driven-lint.js +217 -33
  188. package/dist/harness/data-driven-lint.js.map +1 -1
  189. package/dist/harness/script-check.d.ts +2 -2
  190. package/dist/harness/script-check.d.ts.map +1 -1
  191. package/dist/harness/script-check.js +186 -25
  192. package/dist/harness/script-check.js.map +1 -1
  193. package/dist/index.d.ts +2 -0
  194. package/dist/index.d.ts.map +1 -1
  195. package/dist/index.js +10 -1
  196. package/dist/index.js.map +1 -1
  197. package/dist/orchestrator/project-initializer.d.ts +6 -0
  198. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  199. package/dist/orchestrator/project-initializer.js +16 -0
  200. package/dist/orchestrator/project-initializer.js.map +1 -1
  201. package/dist/orchestrator/report-sanitize-reporter-install.d.ts +24 -0
  202. package/dist/orchestrator/report-sanitize-reporter-install.d.ts.map +1 -0
  203. package/dist/orchestrator/report-sanitize-reporter-install.js +161 -0
  204. package/dist/orchestrator/report-sanitize-reporter-install.js.map +1 -0
  205. package/dist/orchestrator/templates/ai-src/commands/run-test.md +9 -1
  206. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
  207. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -2
  208. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +108 -8
  209. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -0
  210. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +9 -2
  211. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +4 -4
  212. package/dist/orchestrator/templates/playwright.config.ts +7 -0
  213. package/dist/orchestrator/templates/specs-checked-state.d.ts +83 -0
  214. package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -0
  215. package/dist/orchestrator/templates/specs-checked-state.js +261 -0
  216. package/dist/orchestrator/templates/specs-checked-state.js.map +1 -0
  217. package/dist/orchestrator/templates/specs-checked-state.ts +324 -0
  218. package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts +36 -0
  219. package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts.map +1 -0
  220. package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js +155 -0
  221. package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js.map +1 -0
  222. package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
  223. package/dist/orchestrator/templates/specs-url-assert.d.ts +50 -25
  224. package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
  225. package/dist/orchestrator/templates/specs-url-assert.js +65 -24
  226. package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
  227. package/dist/orchestrator/templates/specs-url-assert.ts +101 -28
  228. package/dist/utils/selector-types.d.ts +43 -1
  229. package/dist/utils/selector-types.d.ts.map +1 -1
  230. package/dist/utils/selector-types.js +51 -7
  231. package/dist/utils/selector-types.js.map +1 -1
  232. package/package.json +3 -3
  233. package/src/cli/commands/delivery.ts +2 -1
  234. package/src/cli/commands/generate.ts +5 -2
  235. package/src/cli/commands/inspect.ts +1 -1
  236. package/src/cli/commands/results-merge.ts +9 -2
  237. package/src/cli/commands/results-sanitize.ts +98 -0
  238. package/src/cli/commands/script-check.ts +3 -1
  239. package/src/cli/commands/update.ts +3 -0
  240. package/src/exporters/api-testcase-formatter.ts +5 -7
  241. package/src/exporters/matrix/build.ts +4 -2
  242. package/src/exporters/matrix/render-coverage-sheet.ts +3 -1
  243. package/src/exporters/matrix/render-csv.ts +3 -1
  244. package/src/exporters/matrix/render-overview-sheet.ts +5 -10
  245. package/src/exporters/matrix/result-totals.ts +37 -0
  246. package/src/exporters/matrix/testcase-summary-band.ts +2 -7
  247. package/src/exporters/qa-env-secret-denylist.ts +1 -1
  248. package/src/exporters/report-path-sanitizer.ts +144 -0
  249. package/src/exporters/result-variants.ts +2 -1
  250. package/src/exporters/sensitive-key.ts +48 -0
  251. package/src/exporters/test-data-resolver.ts +12 -2
  252. package/src/generators/gherkin-parser/index.ts +21 -10
  253. package/src/generators/gherkin-parser/parent-scope-clause.ts +130 -0
  254. package/src/generators/test-generator/adapters/adapter-interface.ts +25 -3
  255. package/src/generators/test-generator/adapters/appium/appium-adapter.ts +4 -4
  256. package/src/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
  257. package/src/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
  258. package/src/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
  259. package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
  260. package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
  261. package/src/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
  262. package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
  263. package/src/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
  264. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
  265. package/src/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
  266. package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
  267. package/src/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
  268. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +54 -23
  269. package/src/generators/test-generator/adapters/index.ts +1 -1
  270. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +4 -4
  271. package/src/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
  272. package/src/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
  273. package/src/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
  274. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  275. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
  276. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
  277. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
  278. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
  279. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +3 -1
  280. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +3 -1
  281. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
  282. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
  283. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
  284. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
  285. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
  286. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
  287. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
  288. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
  289. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
  290. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
  291. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
  292. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
  293. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
  294. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
  295. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  296. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +14 -3
  297. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +13 -2
  298. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
  299. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  300. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
  301. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
  302. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
  303. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
  304. package/src/generators/test-generator/code-generator.ts +176 -45
  305. package/src/generators/test-generator/diagnostics.ts +79 -5
  306. package/src/generators/test-generator/patterns/index.ts +97 -16
  307. package/src/generators/test-generator/patterns/with-parent-scope.ts +41 -0
  308. package/src/generators/test-generator/step-mapper.ts +170 -26
  309. package/src/generators/test-generator/template-engine.ts +21 -21
  310. package/src/generators/test-generator/utils/data-resolver.ts +23 -2
  311. package/src/generators/test-generator/utils/dialog-scope-identity.ts +71 -0
  312. package/src/generators/test-generator/utils/escape-js-string.ts +24 -0
  313. package/src/generators/test-generator/utils/nearest-match.ts +55 -0
  314. package/src/generators/test-generator/utils/selector-entry-fields.ts +131 -0
  315. package/src/generators/test-generator/utils/selector-entry-walk.ts +58 -0
  316. package/src/generators/test-generator/utils/selector-nth-validation.ts +99 -0
  317. package/src/generators/test-generator/utils/selector-resolver.ts +245 -16
  318. package/src/generators/test-generator/utils/selector-type-validation.ts +87 -0
  319. package/src/harness/data-driven-lint.ts +192 -32
  320. package/src/harness/script-check.ts +186 -21
  321. package/src/index.ts +7 -0
  322. package/src/orchestrator/project-initializer.ts +17 -0
  323. package/src/orchestrator/report-sanitize-reporter-install.ts +109 -0
  324. package/src/orchestrator/templates/ai-src/commands/run-test.md +9 -1
  325. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
  326. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -2
  327. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +108 -8
  328. package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -0
  329. package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +9 -2
  330. package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +4 -4
  331. package/src/orchestrator/templates/playwright.config.ts +7 -0
  332. package/src/orchestrator/templates/specs-checked-state.ts +324 -0
  333. package/src/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
  334. package/src/orchestrator/templates/specs-url-assert.ts +101 -28
  335. package/src/utils/selector-types.ts +54 -5
@@ -13,7 +13,8 @@ user-invocable: false
13
13
  - **Actor**: Always `User`, always active voice.
14
14
  - **Value**: `with {{snake_case}}` — never hardcode static data.
15
15
  - **State**: `is <keyword>` — never use `{{}}` for states.
16
- - **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation.
16
+ - **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation. Container types: `table` `list` `list-item` `section` `dialog` `modal` `drawer` `overlay` `form` `card` `item` `row` `group` `panel` `region` `container` `tab` `menu`. Anything else is not a container, and the clause is DROPPED with `SG-W028` rather than silently widening the step to the whole page.
17
+ - **Parent scope BY CONTENT** (web only): `… with {{v}} in [Parent] parentType with {{v2}}` — the SECOND `with` picks the one container whose content is `{{v2}}`, for a list of look-alike cards/rows. One `with` after the clause still belongs to the TARGET (`see [first] row in [apply list] table with {{job_name}}`), so the by-content form always carries two. On the mobile adapter it reports `SG-W028` — an Appium parent chain has no content filter.
17
18
 
18
19
  ## Keyword → Action Rules
19
20
 
@@ -45,6 +46,8 @@ User select [T] dropdown with {{v}}
45
46
  User click [T] button | tab | column | breadcrumb
46
47
  User click [T] row with {{v}}
47
48
  User try to click [T] button | link # DISABLED element only — see rule below (v3.3) [web]
49
+ User click [T] overlay at position "top-left" # corner preset: top-left | top-right | bottom-left | bottom-right [web]
50
+ User click [T] overlay at offset x=24px, y=10% # px or % per axis, from the element's top-left [web]
48
51
  User double click [T] element
49
52
  User hover [T] icon | row # no-op on mobile (see Platform Support)
50
53
  User drag [T] to [T2]
@@ -57,7 +60,17 @@ User expand | collapse [T] row
57
60
  scenario asserts nothing happens (cooldown re-send, submit before the form is valid). `click` waits for
58
61
  `enabled` → it can only time out there; `try to click` compiles to `.click({ force: true })`. Always
59
62
  pair it with the proof: `Then User see [T] TYPE is disabled` + an assertion that the effect did NOT occur.
60
- NEVER use it for a click that is supposed to work — it deletes the actionability wait.
63
+ NEVER use it for a click that is supposed to work — it deletes the actionability wait. (`force: true` is
64
+ reachable ONLY through `try to click` — there is no separate `force` keyword.)
65
+
66
+ **click at position rule** (web only): a plain `click` lands on the element's CENTRE. To click a
67
+ modal backdrop where the dialog is not, or any control whose centre is covered, append
68
+ `at position "<corner>"` (5% in from that corner) or `at offset x=<n>px|%, y=<n>px|%`. The position
69
+ is checked against the element's real box at run time (an offset past it fails naming the size). The
70
+ suffix is refused at compile time (`SG-E026`, the step throws instead of clicking the centre) when it
71
+ is misspelt or unquoted, when a value is negative / above 100% / missing an axis, when anything
72
+ follows it, and when it is combined with `try to click`, a `with {{v}}` text filter, `… and accept [OK]
73
+ alert`, or `double click` — those forms take no position.
61
74
 
62
75
  ### Alert / Keyboard / Wait / Scroll
63
76
 
@@ -68,12 +81,11 @@ User click [OK | Cancel] alert # two-step form: must come BEF
68
81
  User fill [T] alert with {{v}} # no-op on mobile — native prompt fill is app-specific
69
82
  User see [message text] alert
70
83
  User press Escape key | [Enter] key | Tab key 5 times | Enter on [T] field
71
- User wait for N seconds | [T] page # [T] page: web waits for the URL; mobile pauses (settle) — see Platform Support
84
+ User wait for N seconds | [T] page # [T] page: web waits for the URL, then HOLDS it (see Pattern 8); mobile pauses (settle) — see Platform Support
72
85
  User wait for [T] TYPE is visible | hidden | enabled | disabled # ANY reference (v3.3)
73
86
  User wait for [T] TYPE with {{v}} # until it shows the value
74
87
  User wait for [T] table to refresh # filter/search/pagination round-trip (v3.3)
75
88
  User scroll to [T] section
76
- User switch to [T] frame | [main] frame # web: iframe; mobile: hybrid-app WebView context (no-op if the screen has no WebView)
77
89
  ```
78
90
 
79
91
  > **Browser alerts (native `window.confirm/alert/prompt` only):** prefer the compound form —
@@ -86,6 +98,36 @@ User switch to [T] frame | [main] frame # web: iframe; mobile: hybrid-
86
98
  > `wait for N seconds` stays a last resort. `table to refresh` watches the app's loading
87
99
  > indicator (`qa/app.yaml` `feedback.loading.indicator`, default `[aria-busy="true"]`).
88
100
 
101
+ ### Scope control
102
+
103
+ ```
104
+ User is on [T] dialog # ENTER dialog scope, rooted at [T]'s selectors.yaml entry
105
+ User use dialog # ENTER dialog scope, unnamed — plain page.getByRole('dialog')
106
+ User close dialog | dismiss dialog | exit dialog # EXIT dialog scope (three spellings, identical)
107
+ User switch to [T] frame # ENTER frame scope — web: iframe; mobile: hybrid-app WebView context
108
+ User switch to [main] frame # EXIT frame scope (no-op if the screen has no WebView)
109
+ ```
110
+
111
+ > **Dialog scope is STICKY — it ends only where you end it.** Once a scenario enters dialog scope,
112
+ > EVERY later locator in that scenario is prefixed with the dialog root, **including the steps that
113
+ > run after the dialog has closed**. Nothing implicit drops it: not a click on the dialog's own
114
+ > Close/OK button, and **not** `wait for [T] dialog is hidden` — that step asserts the dialog went
115
+ > away and then keeps resolving the following steps INSIDE it, which can never succeed. Only
116
+ > `close`/`dismiss`/`exit dialog`, or the start of the next scenario, drops the scope. The failure
117
+ > has a recognisable shape: a step after the dialog closed times out reading "element not found"
118
+ > while the element is plainly on screen — you are not missing a selector, you are still scoped
119
+ > inside a dead dialog.
120
+ >
121
+ > ```gherkin
122
+ > Given User is on [Confirm Delete] dialog # scope enters here — implicitly, but it still needs an EXPLICIT exit
123
+ > When User click [OK] button # scoped — correct, the button IS in the dialog
124
+ > And User exit dialog # ← REQUIRED: the click closed the dialog, the scope did not
125
+ > Then User see [Deleted] toast # page-rooted again
126
+ > ```
127
+ >
128
+ > Dialog scope is `[web]`-effective only — no Appium template reads `inDialog`, so on mobile the
129
+ > following steps resolve against the whole screen (`SG-W021`). Frame scope works on both.
130
+
89
131
  ### Positional table rows (v3.3) `[web]`
90
132
 
91
133
  ```
@@ -142,6 +184,20 @@ detail:
142
184
  anchor: { type: 'role', value: 'heading', name: 'Item Detail' } # the rendered-content proof
143
185
  ```
144
186
 
187
+ **`wait for [T] page` shares that hold, and only that.** Since 3.2.30 the wait compiles to
188
+ `waitForPage(page, {…})` on the same helper: it arrives, settles, holds the URL, and fails naming
189
+ where the user went — so the steps AFTER the wait no longer run against a page mid-redirect, and
190
+ `/vi/search` no longer resolves on `/vi/search-results`. Two differences from the `Then` side:
191
+
192
+ - **A wait does not constrain the query.** `see [Results] page` with no `with {{v}}` demands a bare
193
+ URL; `wait for [Results] page` after submitting a search tolerates the `?q=…` it lands on. Prove
194
+ the query in the `Then` that follows.
195
+ - **A wait is not an oracle.** It never counts toward a scenario's assertions — `ORACLE-URL-ONLY`
196
+ and the assertionless-test check both ignore it. Always follow it with a real `Then`.
197
+
198
+ An absolute page `value` (`https://app.example.com/reception/:id`) compiles its host into a
199
+ separate anchored `hostname:` — the same path on another host no longer satisfies the wait.
200
+
145
201
  **Pattern 8 — the page assertion judges pathname AND query together.** Both `see [T] page` and
146
202
  `is on [T] page` (Then-side) compile to ONE web-first check — `expectPage(page, {…})`, built on
147
203
  `toHaveURL(urlMatches({…}))` and retrying like any other assertion — comparing the **exact** pathname
@@ -211,6 +267,15 @@ User see [Table] table match data:
211
267
 
212
268
  Row scope: `see [Ref] row in [Table] table with {{v}}` enters scope. Subsequent `see [Col] column with {{v}}` checks cell in that row. Use `table match data:` for multi-row verification.
213
269
 
270
+ **[Col] does not have to be the DOM header text.** `see [Col] column in [T] table` looks for a header whose accessible name is the reference, which cannot match a page in another language. Keep the reference readable — it is what the delivered test case shows — and declare the real header on the table entry (or on a `<col>--columnheader` entry's `name:`, which also takes `{{variables}}` for per-locale text):
271
+ ```yaml
272
+ article list:
273
+ type: role
274
+ value: table
275
+ columns:
276
+ category: { index: 2, header: '記事分類' }
277
+ ```
278
+
214
279
  **[Ref] of a row step is a display label UNLESS declared in selectors.yaml.** Without an entry the step checks the value exists in ANY row — a name like `[First Contact Row]` does NOT position the row, and an ordering rule would pass even when the row moved (SG-W010 warns on positional-looking undeclared labels). To assert a POSITION, declare the row (a `type: locator` value RELATIVE to the table):
215
280
  ```yaml
216
281
  first contact row:
@@ -402,7 +467,7 @@ Full syntax → `sungen-mobile-gestures`.
402
467
  |---|---|---|
403
468
  | `see [T] page` \| `page with {{v}}` | asserts path+query | **silent no-op** — asserts nothing; the scenario passes regardless. Assert a screen-marker element instead |
404
469
  | `is on [T] page` \| `open [T] page` (Given/When) | navigates via URL | `is on` = the app-launch anchor, correctly a no-op. `open`/`navigate to` = a no-op that **reports `SG-W026`** — it promises a screen change and performs none; use tap/gesture steps instead |
405
- | `wait for [T] page` | waits for the URL | fixed `driver.pause(500)` settle — not a real wait condition |
470
+ | `wait for [T] page` | waits for the URL, then settles and HOLDS it (`waitForPage`) | fixed `driver.pause(500)` settle — not a real wait condition |
406
471
  | `go back` (OS/hardware back) | **not a web step** — reports `SG-W020`; re-enter with `is on [X] page` or tag `@manual` | Android: `KEYCODE_BACK`, the real hardware key. iOS: WDA **navigation**-back, *not* the user's edge swipe-back (iOS has no hardware back). Distinct from `tap [Back] button`: system back skips the screen's own handler unless the app wires it |
407
472
  | `hover [T] icon \| row` | real hover | no-op — hover-revealed content is normally already visible on mobile; use `tap` |
408
473
  | `fill [T] alert with {{v}}` | fills native `prompt()` | no-op (comment only) — app-specific, handle manually |
@@ -492,7 +557,17 @@ award:
492
557
  | `upload` | — | — | file inputs |
493
558
  | `frame` | iframe selector | — | iframes |
494
559
 
495
- Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `columns`
560
+ Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `columns` `checked` `selected`
561
+
562
+ `checked: { class: <cls> }` or `checked: { attribute: <attr> }` — how a checkbox-like control that is
563
+ NOT a real checkbox (a `<div>` tile with no `role`/`aria-checked`; `.check()` throws "Not a checkbox or
564
+ radio button") shows its state. `selected:` is an ALIAS of the same signal, for a card/tab picker where
565
+ `checked:` reads wrong; declare ONE spelling and ONE of class/attribute. `check`/`uncheck` click only
566
+ when the state differs, then assert it; `is checked` / `is unchecked` / `is selected` (plain or
567
+ `with {{v}}`) assert it. The class is read on the located element OR on a descendant (a checkmark
568
+ `<svg>` that only mounts when on); the attribute is read on the located element itself. Other states on
569
+ the same entry (`is focused`, …) keep their native matchers. The value must be ONE plain class token or
570
+ attribute name — whitespace, a quote or a backslash is refused at compile time, naming the entry.
496
571
 
497
572
  `scope` (e.g. `scope: dialog`) is `[web]`-effective only — no Appium template reads `inDialog`, so
498
573
  on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`).
@@ -809,7 +884,32 @@ Only when `@cleanup:*` tags aren't enough — feature-specific logic.
809
884
 
810
885
  ### Layer 3: `@beforeAll` / `@afterAll` (optional)
811
886
 
812
- For one-time setup/teardown.
887
+ For one-time setup/teardown. A hook scenario takes `@api:` / `@query:` preconditions exactly like an
888
+ ordinary scenario (override args and `@concurrent:N` included) — they run inside the hook and bind
889
+ `{{name}}`:
890
+
891
+ ```gherkin
892
+ @beforeAll @api:login(email={{email}},password={{pw}}) @api:list_items(token={{login.body.token}})
893
+ Scenario: seed the session once before any scenario
894
+ Then expect {{login.status}} is 200
895
+ And expect {{list_items.status}} is 200
896
+
897
+ @afterAll @api:cleanup
898
+ Scenario: drop the seeded data after the last scenario
899
+ Then expect {{cleanup.status}} is 204
900
+ ```
901
+
902
+ - A variable bound in `@beforeAll` (`{{login.body.token}}`) is readable from EVERY scenario of the
903
+ feature (it runs before each of them). `@afterEach`/`@afterAll` bindings stay hook-local.
904
+ - A failing hook skips every test in the file, so hook steps are COLLECTED: each `Then` that fails is
905
+ recorded and the next step still runs; a failing action step (`Given`/`When`) stops the hook there.
906
+ The hook fails ONCE with the full list (`Hook preconditions not met`), not with the first failure.
907
+ - `@cases` and `@extend` are not supported on a hook — they are ignored with a compile warning.
908
+ - A hook has no `page` fixture on a per-scenario (non-`@serial`) feature — keep its steps to `@api:`/
909
+ `@query:` preconditions and `expect` assertions there.
813
910
 
814
911
  **Rendering order in `.spec.ts`:**
815
- `test.describe` → `test.use(storageState)` → `test.use(autoCleanup)` → `test.beforeAll` → `test.beforeEach` → `test.afterEach` → `test.afterAll` → `test()` blocks
912
+ `test.describe` → `test.use(storageState)` → `test.use(autoCleanup)` → `test.beforeAll` → `test.beforeEach` → `test.afterEach` → `test.afterAll` → `test()` blocks.
913
+ On a `@serial` feature the user `@beforeAll`/`@afterAll` blocks are registered right after the built-in
914
+ `beforeAll` (page creation) and BEFORE the built-in `afterAll` (page close): Playwright runs `afterAll`
915
+ hooks in registration order, so the shared `page` is still open for the user's teardown steps.
@@ -96,6 +96,7 @@ When running Phase 0 for a **flow** (`qa/flows/<name>/`), check existing screen
96
96
  - Toast/alert named from content (no aria-label) → `feedback.toast: { role: status, named_by: content }` + `name: null` on the selector entries.
97
97
  - Loading indicator for `wait for [T] table to refresh` → `feedback.loading.indicator` (skip if the app sets `aria-busy`).
98
98
  - **Every qa/app.yaml edit MUST carry provenance** in `detection:`: who/when (`detected_by`) and the observed evidence per key (`evidence:`). A profile without evidence is a guess a human cannot review.
99
+ 7c. **Checkbox-like control that is not a checkbox** (`.check()` failed with `Not a checkbox or radio button`; the snapshot shows a `<div>`/`<button>` tile with no `role="checkbox"` and no `aria-checked`): keep the locator that finds the tile and add the state signal to the SAME entry — `checked: { class: '<class present when on>' }` (a class on that element itself, e.g. `bg-selected`) or `checked: { attribute: '<attr>' }` (a `'true'`/`'false'` attribute such as `data-checked`); on a card/tab picker write `selected:` instead — same signal, different spelling. This is what makes `check`/`uncheck`/`is checked`/`is selected` compile for the tile. ONE plain class token or attribute name only — whitespace or a quoted fragment is refused at compile time, naming the entry. Same shape as `expanded:` for accordions; `checked.class` matches on the located element OR a descendant carrying it (the checkmark icon), while `checked.attribute` is read on the located element itself.
99
100
  8. **Merge, don't overwrite**: preserve the page selector and any user-authored entries in `selectors.yaml`. Only add missing keys.
100
101
  - **Type-collision check** (see `sungen-selector-keys` § Collision rule): before writing a base key, check whether it already exists with a *different* `type`. If so, give **both** entries a `--type` suffix instead of clobbering or sharing the base key — a base-key entry of the wrong type gets picked up by any step referencing that label. Watch `page` especially: a `[X] page` step reads `value` as a URL, so if `X` already holds a button (`value: 'button'`) the compiler won't use the button's value as a URL, but it can't invent the real one either — it falls back to the feature's own path and warns `SG-W013`, asserting a page that isn't `X`'s. Write `X--page` (URL from the live page / feature `Path:`) alongside `X--button`.
101
102
  9. **Show summary + confirm**: list the keys that will be added, ask the user to approve, then write the file.
@@ -219,7 +219,7 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
219
219
  | `[X] button` | `getByRole('button', { name: 'X' })` | Button's accessible name = X |
220
220
  | `[X] link` | `getByRole('link', { name: 'X' })` | Link text = X |
221
221
  | `[X] heading` / `header` | `getByRole('heading', { name: 'X' })` | Heading text = X |
222
- | `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` | Checkbox label = X |
222
+ | `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` | Checkbox label = X — a checkbox-LIKE tile with no `role`/`aria-checked` needs an explicit entry plus `checked: { class: <cls> }` or `checked: { attribute: <attr> }` (read on the located element itself; `expanded.class` reads a descendant) |
223
223
  | `[X] radio` | `getByRole('radio', { name: 'X' })` | Radio label = X |
224
224
  | `[X] field` | `getByPlaceholder('X')` | Placeholder text = X AND field has a placeholder |
225
225
  | `[X] text` / `message` / `label` | `getByText('X')` | Visible text = X (partial match) |
@@ -231,7 +231,7 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
231
231
  | `[X] tab` | `getByRole('tab', { name: 'X' })` | Tab text = X |
232
232
  | `[X] table` | `getByRole('table', { name: 'X' })` | Table aria-label = X |
233
233
  | `[X] list` | `getByRole('list', { name: 'X' })` | List aria-label = X |
234
- | `[X] column` | `getByRole('columnheader', { name: 'X' })` | Column header text = X |
234
+ | `[X] column` | `getByRole('columnheader', { name: 'X' })` | Column header text = X — **overridable**, see the column-header note below |
235
235
  | `[X] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'X' })` | Dialog aria-label/heading = X |
236
236
  | `[X] dropdown` / `select` | `getByRole('combobox', { name: 'X' })` | Combobox label = X |
237
237
  | `[X] menuitem` | `getByRole('menuitem', { name: 'X' })` | Menu item text = X |
@@ -244,6 +244,8 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
244
244
  | `[X] breadcrumb` | `getByRole('navigation', { name: 'X' })` | Navigation aria-label = X |
245
245
  | `[X] badge` / `tooltip` / `tag` | `getByText('X')` | Visible text = X |
246
246
 
247
+ **Column headers: the `[Reference]` is not required to be the DOM text.** `[X] column` defaults its accessible name to the reference, which is unmatchable when the page is not in the reference's language (a `[Category]` column whose header reads 記事分類). Declare the real header instead of renaming the reference — the reference is what ships in the delivered test case. Most specific first: the table entry's `columns.<col>.header`, then a `<col>--columnheader` entry's `name:`, then the reference text. A `header` may hold `{{variables}}` for per-locale text, like any `name:`. A same-named entry that is NOT a column header is ignored and cannot hijack the lookup.
248
+
247
249
  **Special note on the list members `[X] item` / `[X] option` / `[X] cell`** — the only rows above that behave differently per platform:
248
250
 
249
251
  - **Web**: they infer the role with **no `name`**, on purpose. A list member takes its accessible name from its own CONTENT, so a name defaulted from the `[Reference]` text matches zero elements — and `toBeHidden()` PASSES on an empty locator, so the scenario would go green having proved nothing. The role alone is the whole locator; `nth` or a count assertion narrows it. An explicit `name:` you write yourself is always honoured.
@@ -255,6 +257,11 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
255
257
 
256
258
  **Only add a YAML entry when** auto-infer cannot work: DOM name differs from Gherkin label, need `testid`, need `nth`, need `exact: true`, need `name: null`, or the field type requires explicit config.
257
259
 
260
+ **`nth:` takes an integer `>= -1`** — `0`, `1`, `2`… are zero-based indexes and `-1` is the LAST
261
+ match (`.last()` on web, `.at(-1)` on mobile). Nothing else is an index: `nth: -2`, `nth: 'last'`
262
+ and `nth: 1.5` are ignored, the selector stops filtering by position, and the compiler reports
263
+ `SG-W018`. Reach for `nth` only when `exact: true` or `scope` cannot disambiguate.
264
+
258
265
  ### `name: null` — name-from-content roles (toast, alert, spinner)
259
266
 
260
267
  For a `type: role` entry **without** `name:`, the compiler defaults the accessible-name filter to the Gherkin `[Reference]` text. That is right for buttons/links — and **always wrong** for roles whose accessible name derives from their CONTENT (`status`, `alert`, `log`, `marquee`, `timer`): a toast's name is its message text, never the string "Success Toast". The compiler warns (`SG-W003`). Opt out explicitly:
@@ -97,7 +97,7 @@ Hard-fail gate. Any ✗ is a syntax error — must be fixed; do not score furthe
97
97
 
98
98
  - [ ] Selector keys: lowercase, keep Unicode, use **spaces** (no underscores/dots); same label → `--type`/`--N` suffix; names >30 chars → shorten to 1–3 words
99
99
  - [ ] Types requiring explicit YAML: `date-picker`, `uploader`, `overlay`, `frame`, `step`
100
- - [ ] Every `{{var}}` in `.feature` exists in `test-data.yaml`; NO orphan keys
100
+ - [ ] Every `{{var}}` in `.feature` exists in `test-data.yaml`, and every test-data key is bound by some scenario. Both directions are checked by the advisory **data-driven lint** printed at the end of `sungen generate` (warn, never blocking; it is NOT an `audit` finding): **forward** — `{{x}} is not a test-data key and nothing binds it at run time` (the compiler does not fail; the step compiles to the literal text `'${x}'` and runs green having tested nothing, so treat this warning as a defect); **reverse** — `test-data key "x" is defined but never referenced` (dead data). Not counted as unbound/orphan: `{{$…}}` dynamic vars, runtime-bound vars (`remember … as {{v}}`, the scenario's own `@api:`/`@query:` names, a `@beforeAll` hook's `@api:`/`@query:` bindings, a `@steps:` base's captures for its `@extend`ers, and a `@query`/`@api` param bound by name with no override), `@cases` dataset columns, `_*` factory metadata keys, `{{var}}` used in `selectors.yaml` (i18n names) or `mock/mocks.yaml` (mock bodies), and `{{var}}` inside a `@manual` scenario's `# Tester verifies:` comments
101
101
  - [ ] Dynamic vars (`{{$timestamp}}`/`{{$uuid}}`/`{{$random:a:b}}`) used correctly for run-unique data (CRUD)
102
102
  - [ ] `locator` (CSS) only as a last resort
103
103
 
@@ -291,7 +291,7 @@ Do NOT deduct points when a `.feature` lacks the following viewpoints (Playwrigh
291
291
  1. ID unique + correct format `VP-<CAT>-<NNN>`?
292
292
  2. Title understandable immediately, no context needed?
293
293
  3. Every step uses the right keyword (`Given`=is on / `When`=action / `Then`=see)?
294
- 4. Every `{{var}}` exists in `test-data.yaml`?
294
+ 4. Every `{{var}}` exists in `test-data.yaml`, and every test-data key is bound by some scenario? (`sungen generate` ends with a data-driven lint warning for either direction — an unbound `{{x}}` compiles to the literal placeholder, it does not fail)
295
295
  5. At least one happy path?
296
296
  6. Negative cases for invalid classes (EP)?
297
297
  7. Boundary covers the 4 points (`min-1/min/max/max+1`)?
@@ -318,7 +318,7 @@ Do NOT deduct points when a `.feature` lacks the following viewpoints (Playwrigh
318
318
  9. **Missing negative path** → map invalid classes, add scenarios.
319
319
  10. **All `@high`** → reset by user impact per the `sungen-gherkin-syntax` priority table (auth/CRUD/security/required → `@high`; format/standard-range boundary/search → `@normal`; cosmetic → `@low`).
320
320
  11. **`@manual` without reason** → add a technical reason comment explaining why automation is not possible.
321
- 12. **Orphan var in test-data** → delete or reconnect.
321
+ 12. **Orphan key in test-data** (the advisory lint after `sungen generate` says `defined but never referenced`; a `@query`/`@api` param bound by name, a `@manual` comment ref or a `_*` metadata key is not one) → delete it or bind it into a scenario. **Unbound `{{x}}` in a step** (the same lint says `is not a test-data key and nothing binds it`) → add the key or fix the ref — left alone it runs as the literal text `${x}`.
322
322
  13. **Unnecessary CSS/locator selector** → switch to role+name/label/text; keys use spaces.
323
323
  14. **Scope crammed into `Background`** → split into `@steps`/`@extend`.
324
324
  15. **Mixed auth groups missing `@parallel`** → add `@parallel`.
@@ -361,7 +361,7 @@ If `selectors/<name>.yaml` exists, count lines matching `@needs-live-verify` (pr
361
361
  - [ ] Keyword→Action correct
362
362
  - [ ] Action→Type correct; click-rule correct
363
363
  - [ ] Assertions follow the 8 patterns, no redundant `is visible`
364
- - [ ] Every {{var}} exists in test-data.yaml; no orphan keys
364
+ - [ ] Every {{var}} exists in test-data.yaml and no orphan keys — both reported by the advisory data-driven lint at `sungen generate` (not `audit`); an unbound {{var}} compiles to a literal placeholder, never a compile error
365
365
  - [ ] Selector keys follow sungen-selector-keys; locator only as last resort
366
366
  - [ ] Tags valid; @parallel when mixing auth groups
367
367
  - Syntax errors found: <n>
@@ -84,9 +84,16 @@ export default defineConfig({
84
84
  /* Output file path is controlled by PLAYWRIGHT_JSON_OUTPUT_NAME env var for per-screen isolation. */
85
85
  /* When SUNGEN_ENV is set, the env name is inserted before `.json` so locale */
86
86
  /* runs don't overwrite each other (e.g. `<name>-test-result.vi.json`). */
87
+ /* The sanitize reporter rewrites the JSON report's absolute machine paths to project-relative ones on
88
+ exit — the file is committed next to the spec, so a username or machine layout must not ship in it.
89
+ Registered only while its file exists (`sungen init`/`generate`/`update` write it): a cleaned
90
+ specs/generated must not stop Playwright from loading this config. */
87
91
  reporter: [
88
92
  ['html'],
89
93
  ['json', { outputFile: resolveJsonOutputFile() }],
94
+ ...(fs.existsSync(path.join(__dirname, 'specs', 'generated', 'report-path-sanitize-reporter.ts'))
95
+ ? [['./specs/generated/report-path-sanitize-reporter.ts'] as [string]]
96
+ : []),
90
97
  ],
91
98
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
92
99
  use: {
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Sungen checked-state helper — auto-generated into specs/checked-state.ts. DO NOT EDIT.
3
+ *
4
+ * Playwright's `.check()` / `.uncheck()` accept ONLY native checkbox/radio inputs and
5
+ * `role=checkbox|radio|switch`. Anything else throws "Not a checkbox or radio button" (#675).
6
+ * A toggle BUTTON — `<button aria-pressed>`, the correct ARIA pattern for a pressed/unpressed
7
+ * control — is none of those, so a bare `.check()` could never drive one.
8
+ *
9
+ * Rewriting the Gherkin to `click` is NOT an equivalent workaround, and that is the whole reason
10
+ * this helper exists: `click` flips UNCONDITIONALLY, so a scenario that re-runs, or starts from an
11
+ * unknown state, drives the toggle the WRONG way. Idempotence — end up in the requested state
12
+ * regardless of where you started — is the entire meaning of `check` / `uncheck`.
13
+ *
14
+ * The mechanism is chosen from the ELEMENT at run time, not from the selector's declared role,
15
+ * because the resolved selector is very often a testid/CSS locator carrying no role at all — so
16
+ * codegen cannot know what it will hit.
17
+ *
18
+ * Deliberately depends on NOTHING but the `Locator` TYPE: no `expect`, no test-runner context. That
19
+ * keeps the branching testable against a fake locator without a browser (tests/runtime/
20
+ * checked-state.run.ts) — this file runs inside every user's suite, so its logic is worth pinning.
21
+ */
22
+ import type { Locator } from '@playwright/test';
23
+ /** A signal DECLARED in selectors.yaml: the class token, or the boolean attribute, marking ON. */
24
+ export interface DeclaredSignal {
25
+ class?: string;
26
+ attribute?: string;
27
+ }
28
+ /** Where the control publishes its on/off state — decided in the page, from the real element. */
29
+ export type StateSource = 'native' | 'aria-pressed' | 'aria-checked' | 'class' | 'attribute' | 'none';
30
+ export interface ControlProbe {
31
+ source: StateSource;
32
+ /** Current state, already normalised. Meaningless when `source` is 'native' (Playwright reads it). */
33
+ on: boolean;
34
+ /** Button-like: a control a human would press. Decides how a MISSING state is read — see below. */
35
+ buttonLike: boolean;
36
+ }
37
+ /**
38
+ * Runs IN THE PAGE, so it must stay self-contained: Playwright serialises it and the browser has
39
+ * none of this module's scope.
40
+ *
41
+ * It also must contain NO inner function of its own — not even an arrow helper. A bundler with
42
+ * keepNames (esbuild, which is what Playwright itself transpiles specs with) rewrites every named
43
+ * inner function to `__name(fn, "…")`, that wrapper travels inside the serialised source, and the
44
+ * page throws `ReferenceError: __name is not defined` on EVERY check step. Straight-line code with
45
+ * the normalise repeated inline is the price of being serialisable; `tests/runtime/
46
+ * checked-state.run.ts` pins it so the next edit cannot quietly reintroduce a helper.
47
+ *
48
+ * Structurally typed rather than via the DOM lib names because this file is also typechecked
49
+ * inside the compiler build, whose tsconfig carries no DOM lib.
50
+ */
51
+ export declare function probeControl(node: unknown, declared?: DeclaredSignal): ControlProbe;
52
+ /**
53
+ * Drive `target` to the requested on/off state, idempotently.
54
+ *
55
+ * @param target the control
56
+ * @param want `true` = checked/pressed, `false` = unchecked/unpressed
57
+ * @param label the `[Selector Ref]` from the feature file, used only in error messages
58
+ * @param options.checkedClass `checked:`/`selected:` from selectors.yaml — the class marking ON,
59
+ * for a control that publishes no ARIA at all (issue #730). Overrides every ARIA reading.
60
+ * @param options.checkedAttribute the same declaration published as a boolean attribute
61
+ * ('true'/'false') instead of a class. A selector declares one or the other, never both.
62
+ */
63
+ export declare function setCheckedState(target: Locator, want: boolean, label: string, options?: {
64
+ timeout?: number;
65
+ checkedClass?: string;
66
+ checkedAttribute?: string;
67
+ }): Promise<void>;
68
+ /**
69
+ * Assert the on/off state of the same control family — the read side of #675.
70
+ *
71
+ * `expect(locator).toBeChecked()` throws the identical "Not a checkbox or radio button" on a
72
+ * `<button aria-pressed>`, so driving a toggle worked while asserting it did not. Polls like
73
+ * Playwright's own web-first assertions rather than sampling once, and reads state exactly the way
74
+ * setCheckedState does, so an action and the assertion after it can never disagree about what the
75
+ * control says. Uses `locator.isChecked()` (not `expect`) for the native family, which keeps this
76
+ * file free of any test-runner dependency.
77
+ */
78
+ export declare function expectCheckedState(target: Locator, expected: boolean, label: string, options?: {
79
+ timeout?: number;
80
+ checkedClass?: string;
81
+ checkedAttribute?: string;
82
+ }): Promise<void>;
83
+ //# sourceMappingURL=specs-checked-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs-checked-state.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-checked-state.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAchD,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAEtE,iGAAiG;AACjG,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AACtG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,sGAAsG;IACtG,EAAE,EAAE,OAAO,CAAC;IACZ,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,YAAY,CAkDnF;AA4BD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAC7C,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA6Gf;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EACjD,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA4Cf"}