@sun-asterisk/sungen 3.2.24-beta.6 → 3.2.25

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 (363) hide show
  1. package/dist/capabilities/context-router.d.ts.map +1 -1
  2. package/dist/capabilities/context-router.js +15 -7
  3. package/dist/capabilities/context-router.js.map +1 -1
  4. package/dist/cli/commands/capability.d.ts.map +1 -1
  5. package/dist/cli/commands/capability.js +6 -48
  6. package/dist/cli/commands/capability.js.map +1 -1
  7. package/dist/cli/commands/changed.d.ts +14 -0
  8. package/dist/cli/commands/changed.d.ts.map +1 -0
  9. package/dist/cli/commands/changed.js +128 -0
  10. package/dist/cli/commands/changed.js.map +1 -0
  11. package/dist/cli/commands/delivery.d.ts.map +1 -1
  12. package/dist/cli/commands/delivery.js +25 -1
  13. package/dist/cli/commands/delivery.js.map +1 -1
  14. package/dist/cli/commands/generate.js +1 -1
  15. package/dist/cli/commands/generate.js.map +1 -1
  16. package/dist/cli/commands/results-merge.d.ts +19 -0
  17. package/dist/cli/commands/results-merge.d.ts.map +1 -0
  18. package/dist/cli/commands/results-merge.js +191 -0
  19. package/dist/cli/commands/results-merge.js.map +1 -0
  20. package/dist/cli/commands/update.d.ts.map +1 -1
  21. package/dist/cli/commands/update.js +100 -12
  22. package/dist/cli/commands/update.js.map +1 -1
  23. package/dist/cli/index.js +4 -0
  24. package/dist/cli/index.js.map +1 -1
  25. package/dist/exporters/api-catalog-loader.d.ts.map +1 -1
  26. package/dist/exporters/api-catalog-loader.js +10 -3
  27. package/dist/exporters/api-catalog-loader.js.map +1 -1
  28. package/dist/exporters/api-testcase-formatter.d.ts +9 -0
  29. package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
  30. package/dist/exporters/api-testcase-formatter.js +55 -3
  31. package/dist/exporters/api-testcase-formatter.js.map +1 -1
  32. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  33. package/dist/exporters/csv-exporter.js +10 -1
  34. package/dist/exporters/csv-exporter.js.map +1 -1
  35. package/dist/exporters/json-exporter.d.ts.map +1 -1
  36. package/dist/exporters/json-exporter.js +6 -1
  37. package/dist/exporters/json-exporter.js.map +1 -1
  38. package/dist/exporters/matrix/build.d.ts.map +1 -1
  39. package/dist/exporters/matrix/build.js +10 -0
  40. package/dist/exporters/matrix/build.js.map +1 -1
  41. package/dist/exporters/matrix/export.js +1 -1
  42. package/dist/exporters/matrix/export.js.map +1 -1
  43. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  44. package/dist/exporters/matrix/render-csv.js +5 -1
  45. package/dist/exporters/matrix/render-csv.js.map +1 -1
  46. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  47. package/dist/exporters/matrix/render-xlsx.js +4 -1
  48. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  49. package/dist/exporters/matrix/types.d.ts +7 -0
  50. package/dist/exporters/matrix/types.d.ts.map +1 -1
  51. package/dist/exporters/matrix/types.js.map +1 -1
  52. package/dist/exporters/playwright-report-parser.d.ts +17 -2
  53. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  54. package/dist/exporters/playwright-report-parser.js +68 -8
  55. package/dist/exporters/playwright-report-parser.js.map +1 -1
  56. package/dist/exporters/results-merge.d.ts +46 -0
  57. package/dist/exporters/results-merge.d.ts.map +1 -0
  58. package/dist/exporters/results-merge.js +148 -0
  59. package/dist/exporters/results-merge.js.map +1 -0
  60. package/dist/exporters/selector-key-resolver.d.ts +4 -0
  61. package/dist/exporters/selector-key-resolver.d.ts.map +1 -1
  62. package/dist/exporters/selector-key-resolver.js +101 -1
  63. package/dist/exporters/selector-key-resolver.js.map +1 -1
  64. package/dist/exporters/types.d.ts +8 -1
  65. package/dist/exporters/types.d.ts.map +1 -1
  66. package/dist/exporters/xlsx-report-builder.d.ts.map +1 -1
  67. package/dist/exporters/xlsx-report-builder.js +5 -4
  68. package/dist/exporters/xlsx-report-builder.js.map +1 -1
  69. package/dist/generators/gherkin-parser/index.d.ts +2 -0
  70. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  71. package/dist/generators/gherkin-parser/index.js +52 -1
  72. package/dist/generators/gherkin-parser/index.js.map +1 -1
  73. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +20 -10
  74. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  75. package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +2 -11
  76. package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts.map +1 -1
  77. package/dist/generators/test-generator/adapters/appium/appium-adapter.js.map +1 -1
  78. package/dist/generators/test-generator/adapters/appium/templates/imports.hbs +3 -0
  79. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/biometric-action.hbs +9 -0
  80. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +11 -6
  81. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/date-picker-unsupported.hbs +4 -0
  82. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/deep-link-action.hbs +8 -0
  83. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/lifecycle-missing-app-id.hbs +5 -0
  84. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/location-services-action.hbs +11 -0
  85. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +21 -0
  86. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/restart-app-action.hbs +16 -0
  87. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/set-connectivity-action.hbs +11 -0
  88. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +9 -5
  89. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +10 -4
  90. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/device-log-assertion.hbs +16 -0
  91. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disappears-within-assertion.hbs +5 -0
  92. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/matches-format-assertion.hbs +15 -0
  93. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +6 -4
  94. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +4 -3
  95. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/still-visible-after-assertion.hbs +4 -0
  96. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +5 -2
  97. package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +3 -0
  98. package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +3 -0
  99. package/dist/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +7 -1
  100. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +302 -29
  101. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +2 -11
  102. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
  103. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
  104. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  105. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +17 -1
  106. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +6 -1
  107. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +18 -1
  108. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +5 -1
  109. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/matches-format-assertion.hbs +5 -0
  110. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +4 -2
  111. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +4 -1
  112. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/recipe-expect-text.hbs +1 -1
  113. package/dist/generators/test-generator/code-generator.d.ts +6 -0
  114. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  115. package/dist/generators/test-generator/code-generator.js +80 -7
  116. package/dist/generators/test-generator/code-generator.js.map +1 -1
  117. package/dist/generators/test-generator/diagnostics.d.ts +60 -6
  118. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  119. package/dist/generators/test-generator/diagnostics.js +59 -5
  120. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  121. package/dist/generators/test-generator/patterns/expect-patterns.d.ts +7 -0
  122. package/dist/generators/test-generator/patterns/expect-patterns.d.ts.map +1 -1
  123. package/dist/generators/test-generator/patterns/expect-patterns.js +36 -2
  124. package/dist/generators/test-generator/patterns/expect-patterns.js.map +1 -1
  125. package/dist/generators/test-generator/patterns/index.d.ts +19 -12
  126. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  127. package/dist/generators/test-generator/patterns/index.js +58 -38
  128. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  129. package/dist/generators/test-generator/patterns/types.d.ts +23 -0
  130. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  131. package/dist/generators/test-generator/patterns/unsupported-step-code.d.ts +18 -0
  132. package/dist/generators/test-generator/patterns/unsupported-step-code.d.ts.map +1 -0
  133. package/dist/generators/test-generator/patterns/unsupported-step-code.js +23 -0
  134. package/dist/generators/test-generator/patterns/unsupported-step-code.js.map +1 -0
  135. package/dist/generators/test-generator/recipe-expander.js +9 -1
  136. package/dist/generators/test-generator/recipe-expander.js.map +1 -1
  137. package/dist/generators/test-generator/reset-tier.d.ts +68 -0
  138. package/dist/generators/test-generator/reset-tier.d.ts.map +1 -0
  139. package/dist/generators/test-generator/reset-tier.js +94 -0
  140. package/dist/generators/test-generator/reset-tier.js.map +1 -0
  141. package/dist/generators/test-generator/step-mapper.d.ts +39 -0
  142. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  143. package/dist/generators/test-generator/step-mapper.js +144 -5
  144. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  145. package/dist/generators/test-generator/template-engine.d.ts +2 -10
  146. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  147. package/dist/generators/test-generator/template-engine.js +19 -2
  148. package/dist/generators/test-generator/template-engine.js.map +1 -1
  149. package/dist/generators/test-generator/utils/format-pattern.d.ts +35 -0
  150. package/dist/generators/test-generator/utils/format-pattern.d.ts.map +1 -0
  151. package/dist/generators/test-generator/utils/format-pattern.js +82 -0
  152. package/dist/generators/test-generator/utils/format-pattern.js.map +1 -0
  153. package/dist/generators/test-generator/utils/js-literal-scanner.d.ts +42 -0
  154. package/dist/generators/test-generator/utils/js-literal-scanner.d.ts.map +1 -0
  155. package/dist/generators/test-generator/utils/js-literal-scanner.js +142 -0
  156. package/dist/generators/test-generator/utils/js-literal-scanner.js.map +1 -0
  157. package/dist/generators/test-generator/utils/mask-refs-for-matching.d.ts +18 -0
  158. package/dist/generators/test-generator/utils/mask-refs-for-matching.d.ts.map +1 -0
  159. package/dist/generators/test-generator/utils/mask-refs-for-matching.js +25 -0
  160. package/dist/generators/test-generator/utils/mask-refs-for-matching.js.map +1 -0
  161. package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts +9 -1
  162. package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts.map +1 -1
  163. package/dist/generators/test-generator/utils/runtime-data-transformer.js +124 -63
  164. package/dist/generators/test-generator/utils/runtime-data-transformer.js.map +1 -1
  165. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  166. package/dist/generators/test-generator/utils/selector-resolver.js +24 -1
  167. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  168. package/dist/generators/test-generator/utils/spec-syntax-check.d.ts +14 -0
  169. package/dist/generators/test-generator/utils/spec-syntax-check.d.ts.map +1 -0
  170. package/dist/generators/test-generator/utils/spec-syntax-check.js +26 -0
  171. package/dist/generators/test-generator/utils/spec-syntax-check.js.map +1 -0
  172. package/dist/harness/annotation-overrides.d.ts +0 -12
  173. package/dist/harness/annotation-overrides.d.ts.map +1 -1
  174. package/dist/harness/annotation-overrides.js +9 -5
  175. package/dist/harness/annotation-overrides.js.map +1 -1
  176. package/dist/harness/audit.d.ts.map +1 -1
  177. package/dist/harness/audit.js +7 -5
  178. package/dist/harness/audit.js.map +1 -1
  179. package/dist/harness/capability-plan.d.ts +2 -1
  180. package/dist/harness/capability-plan.d.ts.map +1 -1
  181. package/dist/harness/capability-plan.js +14 -4
  182. package/dist/harness/capability-plan.js.map +1 -1
  183. package/dist/harness/capability.d.ts +11 -0
  184. package/dist/harness/capability.d.ts.map +1 -1
  185. package/dist/harness/capability.js +8 -0
  186. package/dist/harness/capability.js.map +1 -1
  187. package/dist/harness/catalog/drivers.yaml +5 -0
  188. package/dist/harness/parse.d.ts +1 -0
  189. package/dist/harness/parse.d.ts.map +1 -1
  190. package/dist/harness/parse.js +6 -1
  191. package/dist/harness/parse.js.map +1 -1
  192. package/dist/harness/run-manifest.d.ts +58 -0
  193. package/dist/harness/run-manifest.d.ts.map +1 -0
  194. package/dist/harness/run-manifest.js +264 -0
  195. package/dist/harness/run-manifest.js.map +1 -0
  196. package/dist/harness/script-check.d.ts.map +1 -1
  197. package/dist/harness/script-check.js +6 -2
  198. package/dist/harness/script-check.js.map +1 -1
  199. package/dist/harness/sensors.d.ts.map +1 -1
  200. package/dist/harness/sensors.js +5 -1
  201. package/dist/harness/sensors.js.map +1 -1
  202. package/dist/harness/viewpoint-ledger.d.ts +1 -1
  203. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  204. package/dist/harness/viewpoint-ledger.js +25 -4
  205. package/dist/harness/viewpoint-ledger.js.map +1 -1
  206. package/dist/index.d.ts +1 -0
  207. package/dist/index.d.ts.map +1 -1
  208. package/dist/index.js +5 -1
  209. package/dist/index.js.map +1 -1
  210. package/dist/orchestrator/lockstep-version.d.ts +22 -0
  211. package/dist/orchestrator/lockstep-version.d.ts.map +1 -0
  212. package/dist/orchestrator/lockstep-version.js +58 -0
  213. package/dist/orchestrator/lockstep-version.js.map +1 -0
  214. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
  215. package/dist/orchestrator/mobile-runtime-scaffolder.js +5 -1
  216. package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
  217. package/dist/orchestrator/project-deps-sync.d.ts +39 -0
  218. package/dist/orchestrator/project-deps-sync.d.ts.map +1 -0
  219. package/dist/orchestrator/project-deps-sync.js +175 -0
  220. package/dist/orchestrator/project-deps-sync.js.map +1 -0
  221. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  222. package/dist/orchestrator/project-initializer.js +5 -2
  223. package/dist/orchestrator/project-initializer.js.map +1 -1
  224. package/dist/orchestrator/templates/ai-src/commands/run-test.md +109 -0
  225. package/dist/orchestrator/templates/ai-src/config/claude.md +1 -1
  226. package/dist/orchestrator/templates/ai-src/config/copilot.md +1 -1
  227. package/dist/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +49 -0
  228. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -3
  229. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +120 -10
  230. package/dist/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md +1 -1
  231. package/dist/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +31 -0
  232. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -1
  233. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +9 -0
  234. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +15 -0
  235. package/dist/orchestrator/templates/env.appium.example +21 -0
  236. package/dist/orchestrator/templates/specs-api.d.ts +4 -2
  237. package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
  238. package/dist/orchestrator/templates/specs-api.js +233 -10
  239. package/dist/orchestrator/templates/specs-api.js.map +1 -1
  240. package/dist/orchestrator/templates/specs-api.ts +214 -11
  241. package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
  242. package/dist/orchestrator/templates/specs-db.js +79 -6
  243. package/dist/orchestrator/templates/specs-db.js.map +1 -1
  244. package/dist/orchestrator/templates/specs-db.ts +70 -7
  245. package/dist/orchestrator/templates/specs-regex-escape.d.ts +13 -0
  246. package/dist/orchestrator/templates/specs-regex-escape.d.ts.map +1 -0
  247. package/dist/orchestrator/templates/specs-regex-escape.js +19 -0
  248. package/dist/orchestrator/templates/specs-regex-escape.js.map +1 -0
  249. package/dist/orchestrator/templates/specs-regex-escape.ts +16 -0
  250. package/dist/orchestrator/templates/specs-test-data.ts +53 -6
  251. package/dist/orchestrator/templates/wdio.conf.ts +126 -10
  252. package/dist/utils/selector-types.d.ts +1 -1
  253. package/dist/utils/selector-types.d.ts.map +1 -1
  254. package/dist/utils/selector-types.js +1 -0
  255. package/dist/utils/selector-types.js.map +1 -1
  256. package/package.json +8 -6
  257. package/src/capabilities/context-router.ts +13 -5
  258. package/src/cli/commands/capability.ts +4 -38
  259. package/src/cli/commands/changed.ts +104 -0
  260. package/src/cli/commands/delivery.ts +27 -2
  261. package/src/cli/commands/generate.ts +1 -1
  262. package/src/cli/commands/results-merge.ts +153 -0
  263. package/src/cli/commands/update.ts +72 -13
  264. package/src/cli/index.ts +4 -0
  265. package/src/exporters/api-catalog-loader.ts +10 -2
  266. package/src/exporters/api-testcase-formatter.ts +59 -3
  267. package/src/exporters/csv-exporter.ts +11 -2
  268. package/src/exporters/json-exporter.ts +7 -2
  269. package/src/exporters/matrix/build.ts +9 -0
  270. package/src/exporters/matrix/export.ts +1 -1
  271. package/src/exporters/matrix/render-csv.ts +5 -1
  272. package/src/exporters/matrix/render-xlsx.ts +4 -1
  273. package/src/exporters/matrix/types.ts +7 -0
  274. package/src/exporters/playwright-report-parser.ts +68 -9
  275. package/src/exporters/results-merge.ts +150 -0
  276. package/src/exporters/selector-key-resolver.ts +81 -1
  277. package/src/exporters/types.ts +8 -1
  278. package/src/exporters/xlsx-report-builder.ts +5 -4
  279. package/src/generators/gherkin-parser/index.ts +55 -1
  280. package/src/generators/test-generator/adapters/adapter-interface.ts +23 -1
  281. package/src/generators/test-generator/adapters/appium/appium-adapter.ts +2 -2
  282. package/src/generators/test-generator/adapters/appium/templates/imports.hbs +3 -0
  283. package/src/generators/test-generator/adapters/appium/templates/steps/actions/biometric-action.hbs +9 -0
  284. package/src/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +11 -6
  285. package/src/generators/test-generator/adapters/appium/templates/steps/actions/date-picker-unsupported.hbs +4 -0
  286. package/src/generators/test-generator/adapters/appium/templates/steps/actions/deep-link-action.hbs +8 -0
  287. package/src/generators/test-generator/adapters/appium/templates/steps/actions/lifecycle-missing-app-id.hbs +5 -0
  288. package/src/generators/test-generator/adapters/appium/templates/steps/actions/location-services-action.hbs +11 -0
  289. package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +21 -0
  290. package/src/generators/test-generator/adapters/appium/templates/steps/actions/restart-app-action.hbs +16 -0
  291. package/src/generators/test-generator/adapters/appium/templates/steps/actions/set-connectivity-action.hbs +11 -0
  292. package/src/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +9 -5
  293. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +10 -4
  294. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/device-log-assertion.hbs +16 -0
  295. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disappears-within-assertion.hbs +5 -0
  296. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/matches-format-assertion.hbs +15 -0
  297. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +6 -4
  298. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +4 -3
  299. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/still-visible-after-assertion.hbs +4 -0
  300. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +5 -2
  301. package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +3 -0
  302. package/src/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +3 -0
  303. package/src/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +7 -1
  304. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +302 -29
  305. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +2 -2
  306. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  307. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +17 -1
  308. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +6 -1
  309. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +18 -1
  310. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +5 -1
  311. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/matches-format-assertion.hbs +5 -0
  312. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +4 -2
  313. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +4 -1
  314. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/recipe-expect-text.hbs +1 -1
  315. package/src/generators/test-generator/code-generator.ts +86 -7
  316. package/src/generators/test-generator/diagnostics.ts +60 -6
  317. package/src/generators/test-generator/patterns/expect-patterns.ts +37 -2
  318. package/src/generators/test-generator/patterns/index.ts +58 -33
  319. package/src/generators/test-generator/patterns/types.ts +36 -0
  320. package/src/generators/test-generator/patterns/unsupported-step-code.ts +19 -0
  321. package/src/generators/test-generator/recipe-expander.ts +9 -1
  322. package/src/generators/test-generator/reset-tier.ts +93 -0
  323. package/src/generators/test-generator/step-mapper.ts +149 -6
  324. package/src/generators/test-generator/template-engine.ts +22 -3
  325. package/src/generators/test-generator/utils/format-pattern.ts +79 -0
  326. package/src/generators/test-generator/utils/js-literal-scanner.ts +132 -0
  327. package/src/generators/test-generator/utils/mask-refs-for-matching.ts +21 -0
  328. package/src/generators/test-generator/utils/runtime-data-transformer.ts +126 -76
  329. package/src/generators/test-generator/utils/selector-resolver.ts +26 -1
  330. package/src/generators/test-generator/utils/spec-syntax-check.ts +22 -0
  331. package/src/harness/annotation-overrides.ts +10 -5
  332. package/src/harness/audit.ts +7 -5
  333. package/src/harness/capability-plan.ts +13 -6
  334. package/src/harness/capability.ts +16 -0
  335. package/src/harness/catalog/drivers.yaml +5 -0
  336. package/src/harness/parse.ts +7 -1
  337. package/src/harness/run-manifest.ts +254 -0
  338. package/src/harness/script-check.ts +6 -2
  339. package/src/harness/sensors.ts +5 -1
  340. package/src/harness/viewpoint-ledger.ts +26 -5
  341. package/src/index.ts +1 -0
  342. package/src/orchestrator/lockstep-version.ts +46 -0
  343. package/src/orchestrator/mobile-runtime-scaffolder.ts +5 -1
  344. package/src/orchestrator/project-deps-sync.ts +175 -0
  345. package/src/orchestrator/project-initializer.ts +5 -2
  346. package/src/orchestrator/templates/ai-src/commands/run-test.md +109 -0
  347. package/src/orchestrator/templates/ai-src/config/claude.md +1 -1
  348. package/src/orchestrator/templates/ai-src/config/copilot.md +1 -1
  349. package/src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +49 -0
  350. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +14 -3
  351. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +120 -10
  352. package/src/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md +1 -1
  353. package/src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +31 -0
  354. package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +1 -1
  355. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +9 -0
  356. package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +15 -0
  357. package/src/orchestrator/templates/env.appium.example +21 -0
  358. package/src/orchestrator/templates/specs-api.ts +214 -11
  359. package/src/orchestrator/templates/specs-db.ts +70 -7
  360. package/src/orchestrator/templates/specs-regex-escape.ts +16 -0
  361. package/src/orchestrator/templates/specs-test-data.ts +53 -6
  362. package/src/orchestrator/templates/wdio.conf.ts +126 -10
  363. package/src/utils/selector-types.ts +1 -0
@@ -0,0 +1,9 @@
1
+ // Simulated biometric input — EMULATOR/SIMULATOR only (a real device needs a real finger; the
2
+ // command is rejected there and the step fails loud). Android: `mobile: fingerprint` — finger 1
3
+ // must be ENROLLED on the emulator (Settings → Security); an unenrolled id gives the no-match
4
+ // path. iOS Simulator: `mobile: sendBiometricMatch` (Features → Touch/Face ID enrolled).
5
+ if (driver.isIOS) {
6
+ await driver.execute('mobile: sendBiometricMatch', { type: '{{#if faceId}}faceId{{else}}touchId{{/if}}', match: {{#if match}}true{{else}}false{{/if}} });
7
+ } else {
8
+ await driver.execute('mobile: fingerprint', { fingerprintId: {{#if match}}1{{else}}9{{/if}} });
9
+ }
@@ -1,9 +1,10 @@
1
1
  {{!-- WebdriverIO/Appium has no .check(). Ensure the toggle ends up ON: read the current state and
2
2
  tap only if it's not already on, so the step is idempotent (re-running a passing test, or
3
3
  running under `noReset` with unknown persisted state, won't flip it back off). State is read
4
- the same way the checked/unchecked assertions read it — `checked` attribute on Android,
5
- `value` attribute on iOS ('1' = on) since that's what actually carries state on a Flutter
6
- composite row; `isSelected()` is a fallback for widgets where it doesn't. The tap itself uses
4
+ the same way the checked/unchecked assertions read it — through the __assertCheckedState
5
+ oracle's __probeCheckedSignals (test-file preamble), which weighs `checked`/`value`, `selected`
6
+ and isSelected() on the node and its descendants, because no single one of them is reliable on
7
+ a custom-rendered control. The tap itself uses
7
8
  the trailing-edge coordinate method (not a centre .click()): on a full-width composite row the
8
9
  a11y node merges the label with the switch, so a centre tap lands on the label and doesn't
9
10
  flip the control. --}}
@@ -12,9 +13,13 @@ await (async () => {
12
13
  // Fail loud with the REAL cause when the element never resolved — getElementRect(undefined)
13
14
  // otherwise throws a cryptic `Malformed type for "elementId"` that masks the not-found (#430).
14
15
  if (!__cb.elementId) throw new Error((__cb as any).error?.message || `Element not found: ${__cb.selector}`);
15
- let __on = false;
16
- try { __on = driver.isIOS ? (await __cb.getAttribute('value')) === '1' : (await __cb.getAttribute('checked')) === 'true'; }
17
- catch { try { __on = await __cb.isSelected(); } catch { /* state unreadable tap anyway */ } }
16
+ // Read the CURRENT state through the same oracle the checked assertions use. The old read here
17
+ // was `getAttribute('checked') === 'true'` with an isSelected() fallback in a `catch` — but on
18
+ // Android that read never throws (UiAutomator2 defaults `checked` to "false" on every node), so
19
+ // the fallback never fired and a custom-rendered toggle already ON read as OFF. This step then
20
+ // tapped it and turned it OFF — the idempotence guard silently inverted.
21
+ const __signals = await __probeCheckedSignals(__cb);
22
+ const __on = __signals.some(s => s.value);
18
23
  if (__on) return;
19
24
 
20
25
  const r = await driver.getElementRect(__cb.elementId);
@@ -0,0 +1,4 @@
1
+ {{!-- A date-picker on native mobile is a DIALOG (spinner/calendar), not a text field: the generic
2
+ fill would click the trigger and setValue into it, selecting nothing while reporting success.
3
+ Same treatment as the upload step — fail loud, and name both ways forward. --}}
4
+ throw new Error("[sungen] {{escapeQuotes reason}}");
@@ -0,0 +1,8 @@
1
+ // Open a deep link into the app — VIEW intent on Android, XCUITest deepLink on iOS (Simulator).
2
+ // THE entry point for push-param / universal-link routing scenarios.
3
+ await (async () => {
4
+ {{#if dataRef}}const __url = String(testData.get('{{dataRef}}'));{{else}}const __url = '{{escapeQuotes url}}';{{/if}}
5
+ const __pkg = __resolveAppId();
6
+ await driver.execute('mobile: deepLink', driver.isIOS ? { url: __url, bundleId: __pkg } : { url: __url, package: __pkg });
7
+ await driver.pause(1500); // let the routed screen mount before the next step asserts on it
8
+ })();
@@ -0,0 +1,5 @@
1
+ {{!-- A lifecycle step (restart / reinstall) in a feature with no `Path:` app id. The lifecycle
2
+ templates resolve the app through a helper the test-file emits only when that id is present,
3
+ so without it the spec used to compile clean and die at runtime with a ReferenceError —
4
+ taking the whole spec down, not the step. Fail at the step, naming the missing line. --}}
5
+ throw new Error("[sungen] {{escapeQuotes reason}}");
@@ -0,0 +1,11 @@
1
+ // Android-only: XCUITest cannot toggle Location Services — fail loud, never silently skip.
2
+ if (driver.isIOS) {
3
+ throw new Error('turn device location {{#if on}}on{{else}}off{{/if}}: iOS/XCUITest cannot toggle Location Services — run this scenario on Android or tag it @platform:android');
4
+ }
5
+ // `mobile: toggleGps` flips blindly, so read `mobile: isGpsEnabled` first and only toggle when the
6
+ // state differs (idempotent). NOTE: the execute-method aliases, not the legacy JSONWP wrappers —
7
+ // WDIO v9 removed driver.isLocationServicesEnabled()/toggleLocationServices().
8
+ await (async () => {
9
+ const __locOn = await driver.execute('mobile: isGpsEnabled');
10
+ if (__locOn !== {{#if on}}true{{else}}false{{/if}}) { await driver.execute('mobile: toggleGps'); }
11
+ })();
@@ -0,0 +1,21 @@
1
+ // Reinstall the app: remove → install from the session's build file → launch → settle.
2
+ // Persistence oracles (dismissals, pins) hinge on what survives a reinstall.
3
+ await (async () => {
4
+ const __pkg = __resolveAppId();
5
+ const __caps = (driver.requestedCapabilities || {}) as Record<string, string>;
6
+ const __appPath = __caps['appium:app'] || __caps['app'];
7
+ if (!__appPath) {
8
+ throw new Error('reinstall the app: the session must install from a build file — set APP_APK (Android) / IOS_APP (iOS) so the appium:app capability is present.');
9
+ }
10
+ try { await driver.removeApp(__pkg); } catch { /* not installed */ }
11
+ await driver.installApp(__appPath);
12
+ await driver.activateApp(__pkg);
13
+ let __prevHad = false;
14
+ await driver
15
+ .waitUntil(async () => {
16
+ const src = await driver.getPageSource();
17
+ const has = /content-desc="[^"]+"|\btext="[^"]+"/.test(src);
18
+ const ok = has && __prevHad; __prevHad = has; return ok;
19
+ }, { timeout: 45000, interval: 400 })
20
+ .catch(() => { /* best-effort — first launch after install can be the slowest */ });
21
+ })();
@@ -0,0 +1,16 @@
1
+ // Restart the app: kill → relaunch → wait for rendered content (same settle oracle as the
2
+ // per-scenario relaunch hook — a cold start first paints a contentless skeleton).
3
+ await (async () => {
4
+ const __pkg = __resolveAppId();
5
+ try { await driver.terminateApp(__pkg); } catch { /* not running */ }
6
+ await driver.pause(500);
7
+ await driver.activateApp(__pkg);
8
+ let __prevHad = false;
9
+ await driver
10
+ .waitUntil(async () => {
11
+ const src = await driver.getPageSource();
12
+ const has = /content-desc="[^"]+"|\btext="[^"]+"/.test(src);
13
+ const ok = has && __prevHad; __prevHad = has; return ok;
14
+ }, { timeout: 30000, interval: 400 })
15
+ .catch(() => { /* best-effort — the next step has its own wait */ });
16
+ })();
@@ -0,0 +1,11 @@
1
+ // Android-only: XCUITest exposes no connectivity control — fail loud, never silently skip.
2
+ if (driver.isIOS) {
3
+ throw new Error('turn {{kind}} {{#if on}}on{{else}}off{{/if}}: iOS/XCUITest cannot toggle device connectivity — run this scenario on Android or tag it @platform:android');
4
+ }
5
+ {{#if isAirplane}}
6
+ // Airplane {{#if on}}ON kills wifi+data{{else}}OFF restores wifi+data{{/if}} — set all three explicitly.
7
+ await driver.execute('mobile: setConnectivity', { wifi: {{#if on}}false{{else}}true{{/if}}, data: {{#if on}}false{{else}}true{{/if}}, airplaneMode: {{#if on}}true{{else}}false{{/if}} });
8
+ {{else}}
9
+ await driver.execute('mobile: setConnectivity', { {{kindKey}}: {{#if on}}true{{else}}false{{/if}} });
10
+ {{/if}}
11
+ await driver.pause(1500); // let the radio state settle before the next step acts on the UI
@@ -1,7 +1,7 @@
1
1
  {{!-- WebdriverIO/Appium has no .uncheck(). Ensure the toggle ends up OFF: tap only if currently on,
2
2
  so the step is idempotent under repeat runs / persisted `noReset` state. State read + tap
3
- method mirror check-action.hbs (see there for why): platform-correct attribute read
4
- (`checked`/`value`) with an isSelected() fallback, then a trailing-edge coordinate tap rather
3
+ method mirror check-action.hbs (see there for why): state via __probeCheckedSignals (the
4
+ multi-signal oracle), then a trailing-edge coordinate tap rather
5
5
  than a centre .click() (a centre tap on a full-width composite row hits the label, not the
6
6
  switch). --}}
7
7
  await (async () => {
@@ -9,9 +9,13 @@ await (async () => {
9
9
  // Fail loud with the REAL cause when the element never resolved — getElementRect(undefined)
10
10
  // otherwise throws a cryptic `Malformed type for "elementId"` that masks the not-found (#430).
11
11
  if (!__cb.elementId) throw new Error((__cb as any).error?.message || `Element not found: ${__cb.selector}`);
12
- let __on = false;
13
- try { __on = driver.isIOS ? (await __cb.getAttribute('value')) === '1' : (await __cb.getAttribute('checked')) === 'true'; }
14
- catch { try { __on = await __cb.isSelected(); } catch { /* state unreadable leave as-is */ } }
12
+ // Read the CURRENT state through the same oracle the checked assertions use. The old read here
13
+ // was `getAttribute('checked') === 'true'` with an isSelected() fallback in a `catch` — but on
14
+ // Android that read never throws (UiAutomator2 defaults `checked` to "false" on every node), so
15
+ // the fallback never fired and a custom-rendered toggle already ON read as OFF. This step then
16
+ // tapped it and turned it ON — the idempotence guard silently inverted.
17
+ const __signals = await __probeCheckedSignals(__cb);
18
+ const __on = __signals.some(s => s.value);
15
19
  if (!__on) return;
16
20
 
17
21
  const r = await driver.getElementRect(__cb.elementId);
@@ -1,4 +1,10 @@
1
- // Mobile toggle state lives in the `checked` attribute (Android) or `value` "1"/"0" (iOS) — NOT
2
- // `selected` (what toBeSelected() reads; always false for a Flutter Switch). The composite row node
3
- // carries it, so the row selector resolves the state directly.
4
- await expect({{> locator}}).toHaveAttribute(driver.isIOS ? 'value' : 'checked', driver.isIOS ? '1' : 'true');
1
+ {{!-- Checkbox/toggle state via the __assertCheckedState oracle (test-file preamble), NOT a bare
2
+ toHaveAttribute. A single `checked`(Android)/`value`(iOS) read cannot be trusted on its own:
3
+ UiAutomator2 defaults `checked` to "false" on EVERY node, so a custom-rendered control that
4
+ publishes no checked semantics (Flutter without Semantics(checked:), RN without
5
+ accessibilityState) reports a confident false while sitting visibly ON. The oracle probes
6
+ `checked`/`value`, `selected` and isSelected() on the node AND on its descendants (on a
7
+ composite row the state routinely sits on an inner node), and passes on ANY positive signal.
8
+ `selected` alone is no substitute either — toBeSelected() is always false for a Flutter
9
+ Switch, which is why this template stopped using it. --}}
10
+ await __assertCheckedState(await ({{> locator}}), true, '{{escapeQuotes selectorRef}}');
@@ -0,0 +1,16 @@
1
+ {{!-- Device-log oracle (Android: logcat · iOS: syslog) — makes SILENT failure modes and analytics
2
+ events observable. getLogs returns entries SINCE THE LAST CALL (incremental buffer), so
3
+ poll-accumulate up to 10s before failing loud with the log type in the message. --}}
4
+ await (async () => {
5
+ {{#if dataRef}}const __needle = String(testData.get('{{dataRef}}'));{{else}}const __needle = '{{escapeQuotes value}}';{{/if}}
6
+ const __type = driver.isIOS ? 'syslog' : 'logcat';
7
+ let __buf = '';
8
+ const __t0 = Date.now();
9
+ while (Date.now() - __t0 < 10000) {
10
+ const __entries = (await driver.getLogs(__type)) as Array<{ message?: string } | string>;
11
+ __buf += __entries.map((e) => (typeof e === 'string' ? e : (e && e.message) || '')).join('\n') + '\n';
12
+ if (__buf.includes(__needle)) return;
13
+ await driver.pause(500);
14
+ }
15
+ throw new Error(`device log (${__type}) did not contain ${JSON.stringify(__needle)} within 10s`);
16
+ })();
@@ -0,0 +1,5 @@
1
+ {{!-- The element must vanish within the window (auto-dismiss / expiry oracle). Plain $() on
2
+ purpose — never __ensureVisible: scroll-hunting for something that should disappear would
3
+ drag the screen away. A 2s grace absorbs accessibility-tree churn latency without hiding a
4
+ genuinely stuck element. --}}
5
+ await $({{> appium-selector-expr}}).waitForDisplayed({ reverse: true, timeout: {{seconds}} * 1000 + 2000, timeoutMsg: '{{selectorRef}} still visible after the {{seconds}}s disappear window' });
@@ -0,0 +1,15 @@
1
+ {{!-- Format assertion (mobile): same multi-source read as have-text/contain-text
2
+ (@text / @content-desc on Android, label/value on iOS), matched against the shape the spec
3
+ states. The error quotes the FORMAT and the actual string — the compiled regex is noise to
4
+ the person who wrote the requirement. --}}
5
+ {
6
+ const __el = await {{> locator}};
7
+ await __el.waitForDisplayed();
8
+ const __sources = driver.isIOS
9
+ ? [await __el.getAttribute('label'), await __el.getAttribute('value')]
10
+ : [await __el.getText(), await __el.getAttribute('content-desc')];
11
+ const __actual = String(__sources.find(s => s && String(s).trim() !== '') ?? '').trim();
12
+ if (!{{{formatRegex}}}.test(__actual)) {
13
+ throw new Error(`[sungen] Expected [{{escapeQuotes selectorRef}}] to match the format "{{escapeQuotes format}}" but it reads "${__actual}"`);
14
+ }
15
+ }
@@ -1,4 +1,6 @@
1
- // Mobile toggle state lives in the `checked` attribute (Android) or `value` "1"/"0" (iOS) — NOT
2
- // `selected` (what toBeSelected() reads; always false for a Flutter Switch). The composite row node
3
- // carries it, so the row selector resolves the state directly.
4
- await expect({{> locator}}).not.toHaveAttribute(driver.isIOS ? 'value' : 'checked', driver.isIOS ? '1' : 'true');
1
+ {{!-- The unchecked twin of checked-assertion same __assertCheckedState oracle, expected=false.
2
+ Deliberately NOT symmetric with the checked direction: "unchecked" requires that NO probed
3
+ signal anywhere (node or descendant, `checked`/`value`/`selected`/isSelected()) reads
4
+ positive, so a visibly-ON custom-rendered control cannot slip through as a false PASS the way
5
+ a single Android `checked="false"` read would let it. --}}
6
+ await __assertCheckedState(await ({{> locator}}), false, '{{escapeQuotes selectorRef}}');
@@ -1,6 +1,7 @@
1
1
  {{!-- State-with-filter (mobile): $$ over the base selector, per-OS text read (hand-rolled — no
2
2
  CSS filter() combinator on Appium) matched against dataValue, then assert the state on the
3
- match. checked/selected map to the composite-row attribute (mirrors checked-assertion);
3
+ match. checked/selected go through the __assertCheckedState oracle (mirrors checked-assertion)
4
+ a single `checked`/`value` read is unreliable on a custom-rendered control;
4
5
  empty is a text read; enabled/focused use direct WDIO matchers. A state with no
5
6
  native-mobile analog (sorted*, aria-busy loading) fails loud, matching sorted-assertion's
6
7
  house style. --}}
@@ -13,8 +14,8 @@
13
14
  if (String(__s.find(s => s && String(s).trim() !== '') ?? '').trim() === __expected) { __el = __c; break; }
14
15
  }
15
16
  if (!__el) throw new Error(`[sungen] No match with text "${__expected}" to assert state on`);
16
- {{#if (or selectedProbe (eq stateAssertion "toBeChecked()"))}}await expect(__el).toHaveAttribute(driver.isIOS ? 'value' : 'checked', driver.isIOS ? '1' : 'true');
17
- {{else if (eq stateAssertion "toBeChecked({ checked: false })")}}await expect(__el).not.toHaveAttribute(driver.isIOS ? 'value' : 'checked', driver.isIOS ? '1' : 'true');
17
+ {{#if (or selectedProbe (eq stateAssertion "toBeChecked()"))}}await __assertCheckedState(__el, true, `item with "${__expected}"`);
18
+ {{else if (eq stateAssertion "toBeChecked({ checked: false })")}}await __assertCheckedState(__el, false, `item with "${__expected}"`);
18
19
  {{else if (eq stateAssertion "toBeEnabled()")}}await expect(__el).toBeEnabled();
19
20
  {{else if (eq stateAssertion "toBeFocused()")}}await expect(__el).toBeFocused();
20
21
  {{else if (eq stateAssertion "toBeEmpty()")}}await expect(__el).toHaveText('');
@@ -0,0 +1,4 @@
1
+ {{!-- The element must SURVIVE the window (e.g. a barcode valid for 60s, a banner that must not
2
+ auto-dismiss). Wait out the window first, then run the platform-aware visibility oracle. --}}
3
+ await driver.pause({{seconds}} * 1000);
4
+ await __assertVisible({{> appium-selector-expr}});
@@ -1,9 +1,12 @@
1
1
  {{!-- Filtered visible (mobile): $$ over the base selector, per-OS text read matched against
2
2
  dataValue — no CSS filter() combinator exists on Appium, so the match is hand-rolled
3
3
  (covers both the locator- and role-strategy call sites, since both resolve through
4
- appium-selector-expr). Assert at least one match is displayed. --}}
4
+ appium-selector-expr). Assert at least one match is displayed. Substring match by
5
+ default, matching the web hasText string contract: case-insensitive with collapsed
6
+ whitespace; `exact` opts into full case-sensitive equality (the anchored-regex twin). --}}
5
7
  {
6
8
  const __expected = String('{{escapeQuotes dataValue}}').trim();
9
+ const __norm = (s: string) => s.replace(/\s+/g, ' ').trim().toLowerCase();
7
10
  const __items = await $$({{> appium-selector-expr}});
8
11
  let __found = false;
9
12
  for (const __el of __items) {
@@ -11,7 +14,7 @@
11
14
  ? [await __el.getAttribute('label'), await __el.getAttribute('value')]
12
15
  : [await __el.getText(), await __el.getAttribute('content-desc')];
13
16
  const __actual = String(__sources.find(s => s && String(s).trim() !== '') ?? '').trim();
14
- if (__actual === __expected && await __el.isDisplayed()) { __found = true; break; }
17
+ if ({{#if exact}}__actual === __expected{{else}}__norm(__actual).includes(__norm(__expected)){{/if}} && await __el.isDisplayed()) { __found = true; break; }
15
18
  }
16
19
  if (!__found) throw new Error(`[sungen] Expected a visible match with text "${__expected}"`);
17
20
  }
@@ -1,4 +1,7 @@
1
1
  await (async () => { const e = await {{> locator}};
2
+ // Fail loud on a missing target — an undefined elementId otherwise surfaces as the opaque
3
+ // server error "The swipe area coordinates must be provided if element is not set".
4
+ if (!e.elementId) { throw new Error('pull-to-refresh target not found: ' + {{> appium-selector-expr}}); }
2
5
  // Pull-to-refresh = drag the content down. Platform-native swipe per OS.
3
6
  if (driver.isIOS) { await driver.execute('mobile: swipe', { elementId: e.elementId, direction: 'down' }); }
4
7
  else { await driver.execute('mobile: swipeGesture', { elementId: e.elementId, direction: 'down', percent: 0.9, speed: 5000 }); }
@@ -1,4 +1,7 @@
1
1
  await (async () => { const e = await {{> locator}};
2
+ // Fail loud on a missing target — an undefined elementId otherwise surfaces as the opaque
3
+ // server error "The swipe area coordinates must be provided if element is not set".
4
+ if (!e.elementId) { throw new Error('swipe target not found: ' + {{> appium-selector-expr}}); }
2
5
  // Platform-native swipe: XCUITest `mobile: swipe` (direction only) vs UiAutomator2 `mobile: swipeGesture`.
3
6
  if (driver.isIOS) { await driver.execute('mobile: swipe', { elementId: e.elementId, direction: '{{direction}}' }); }
4
7
  else { await driver.execute('mobile: swipeGesture', { elementId: e.elementId, direction: '{{direction}}', percent: 0.75 }); }
@@ -8,7 +8,13 @@
8
8
  {{~#case 'xpath'}}{{escapeQuotes value}}{{/case~}}
9
9
  {{~#case 'android-uiautomator'}}android={{escapeQuotes value}}{{/case~}}
10
10
  {{~#case 'ios-predicate'}}-ios predicate string:{{escapeQuotes value}}{{/case~}}
11
- {{~#case 'id'}}id={{escapeQuotes value}}{{/case~}}
11
+ {{~!--
12
+ 'id' — Appium's id strategy prepends `<appPackage>:id/` to any value WITHOUT a colon, so a bare
13
+ Compose testTagsAsResourceId value ("bottom_bar") silently never matches. Bare values therefore
14
+ compile to an exact UiSelector.resourceId match (Android); package-qualified classic View ids
15
+ ("com.foo:id/btn") keep the plain id strategy, which passes them through unchanged.
16
+ --~}}
17
+ {{~#case 'id'}}{{#if (includes value ':')}}id={{escapeQuotes value}}{{else}}android=new UiSelector().resourceId("{{escapeQuotes value}}"){{/if}}{{/case~}}
12
18
  {{~#case 'locator'}}{{escapeQuotes value}}{{/case~}}
13
19
  {{~#case 'css'}}{{escapeQuotes value}}{{/case~}}
14
20
  {{~#default}}~{{escapeQuotes value}}{{/default~}}