swipium 1.4.0 → 2.0.0

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 (541) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/README.md +175 -219
  3. package/THREAT_MODEL.md +223 -47
  4. package/dist/appMap/automationLink.js +39 -26
  5. package/dist/appMap/automationLink.js.map +1 -1
  6. package/dist/appMap/build.js +68 -20
  7. package/dist/appMap/build.js.map +1 -1
  8. package/dist/appMap/codeIndex.js +31 -5
  9. package/dist/appMap/codeIndex.js.map +1 -1
  10. package/dist/appMap/featureIndex.js +23 -5
  11. package/dist/appMap/featureIndex.js.map +1 -1
  12. package/dist/appMap/featureModel.js +44 -8
  13. package/dist/appMap/featureModel.js.map +1 -1
  14. package/dist/appMap/firstRunApply.js +24 -6
  15. package/dist/appMap/firstRunApply.js.map +1 -1
  16. package/dist/appMap/fsWalk.js +19 -2
  17. package/dist/appMap/fsWalk.js.map +1 -1
  18. package/dist/appMap/issues.js +10 -30
  19. package/dist/appMap/issues.js.map +1 -1
  20. package/dist/appMap/migrations.js +6 -6
  21. package/dist/appMap/migrations.js.map +1 -1
  22. package/dist/appMap/prelaunch.js +2 -2
  23. package/dist/appMap/prelaunch.js.map +1 -1
  24. package/dist/appMap/projectRegistry.js +83 -11
  25. package/dist/appMap/projectRegistry.js.map +1 -1
  26. package/dist/appMap/provenance.js +2 -2
  27. package/dist/appMap/provenance.js.map +1 -1
  28. package/dist/appMap/query.js +21 -5
  29. package/dist/appMap/query.js.map +1 -1
  30. package/dist/appMap/runtimeMerge.js +12 -5
  31. package/dist/appMap/runtimeMerge.js.map +1 -1
  32. package/dist/appMap/schema.js +1 -1
  33. package/dist/appMap/schema.js.map +1 -1
  34. package/dist/appMap/screenMatch.js +6 -2
  35. package/dist/appMap/screenMatch.js.map +1 -1
  36. package/dist/appMap/staticScan.js +151 -39
  37. package/dist/appMap/staticScan.js.map +1 -1
  38. package/dist/appMap/store.js +58 -85
  39. package/dist/appMap/store.js.map +1 -1
  40. package/dist/appMap/tsAstScan.js +34 -12
  41. package/dist/appMap/tsAstScan.js.map +1 -1
  42. package/dist/artifacts/bundletool.js +49 -12
  43. package/dist/artifacts/bundletool.js.map +1 -1
  44. package/dist/artifacts/resolve.js +41 -14
  45. package/dist/artifacts/resolve.js.map +1 -1
  46. package/dist/automation/capabilities.js +112 -30
  47. package/dist/automation/capabilities.js.map +1 -1
  48. package/dist/automation/gestures.js +6 -2
  49. package/dist/automation/gestures.js.map +1 -1
  50. package/dist/automation/plan.js +71 -23
  51. package/dist/automation/plan.js.map +1 -1
  52. package/dist/automation/report.js +15 -5
  53. package/dist/automation/report.js.map +1 -1
  54. package/dist/automation/selectors.js +22 -28
  55. package/dist/automation/selectors.js.map +1 -1
  56. package/dist/automation/types.js +1 -1
  57. package/dist/automation/types.js.map +1 -1
  58. package/dist/automation/waits.js +14 -8
  59. package/dist/automation/waits.js.map +1 -1
  60. package/dist/automation/webview.js +9 -3
  61. package/dist/automation/webview.js.map +1 -1
  62. package/dist/automationGen/appiumModel.js +16 -8
  63. package/dist/automationGen/appiumModel.js.map +1 -1
  64. package/dist/automationGen/ciEmitter.js +6 -6
  65. package/dist/automationGen/ciEmitter.js.map +1 -1
  66. package/dist/automationGen/identifiers.js +224 -0
  67. package/dist/automationGen/identifiers.js.map +1 -0
  68. package/dist/automationGen/jsEmitter.js +288 -121
  69. package/dist/automationGen/jsEmitter.js.map +1 -1
  70. package/dist/automationGen/packagePatch.js +2 -2
  71. package/dist/automationGen/packagePatch.js.map +1 -1
  72. package/dist/automationGen/platformResolve.js +36 -0
  73. package/dist/automationGen/platformResolve.js.map +1 -0
  74. package/dist/automationGen/projectProfile.js +76 -27
  75. package/dist/automationGen/projectProfile.js.map +1 -1
  76. package/dist/automationGen/pythonEmitter.js +278 -107
  77. package/dist/automationGen/pythonEmitter.js.map +1 -1
  78. package/dist/automationGen/readmeEmitter.js +7 -6
  79. package/dist/automationGen/readmeEmitter.js.map +1 -1
  80. package/dist/automationGen/run.js +388 -0
  81. package/dist/automationGen/run.js.map +1 -0
  82. package/dist/automationGen/suitePlan.js +17 -10
  83. package/dist/automationGen/suitePlan.js.map +1 -1
  84. package/dist/automationGen/validation.js +52 -12
  85. package/dist/automationGen/validation.js.map +1 -1
  86. package/dist/build/parseBuildLog.js +235 -31
  87. package/dist/build/parseBuildLog.js.map +1 -1
  88. package/dist/build/plan.js +39 -18
  89. package/dist/build/plan.js.map +1 -1
  90. package/dist/ci/preflight.js +13 -4
  91. package/dist/ci/preflight.js.map +1 -1
  92. package/dist/cli/gc.js +74 -0
  93. package/dist/cli/gc.js.map +1 -0
  94. package/dist/cli/init.js +217 -26
  95. package/dist/cli/init.js.map +1 -1
  96. package/dist/cli/main.js +50 -0
  97. package/dist/cli/main.js.map +1 -0
  98. package/dist/cli/report.js +218 -0
  99. package/dist/cli/report.js.map +1 -0
  100. package/dist/cli/scan.js +36 -15
  101. package/dist/cli/scan.js.map +1 -1
  102. package/dist/cli/suite.js +7 -7
  103. package/dist/cli/suite.js.map +1 -1
  104. package/dist/cli/verify.js +7 -7
  105. package/dist/cli/verify.js.map +1 -1
  106. package/dist/consent/consent.js +162 -5
  107. package/dist/consent/consent.js.map +1 -1
  108. package/dist/context/detect.js +55 -18
  109. package/dist/context/detect.js.map +1 -1
  110. package/dist/context/findApps.js +22 -8
  111. package/dist/context/findApps.js.map +1 -1
  112. package/dist/context/projectRoot.js +174 -19
  113. package/dist/context/projectRoot.js.map +1 -1
  114. package/dist/context/scan.js +1 -1
  115. package/dist/context/scan.js.map +1 -1
  116. package/dist/core/capabilityGroups.js +90 -0
  117. package/dist/core/capabilityGroups.js.map +1 -0
  118. package/dist/core/target.js +10 -8
  119. package/dist/core/target.js.map +1 -1
  120. package/dist/core/targetPlan.js +160 -39
  121. package/dist/core/targetPlan.js.map +1 -1
  122. package/dist/drivers/DirectDriver.js +317 -43
  123. package/dist/drivers/DirectDriver.js.map +1 -1
  124. package/dist/drivers/SimctlDriver.js +124 -8
  125. package/dist/drivers/SimctlDriver.js.map +1 -1
  126. package/dist/drivers/WdaDriver.js +365 -55
  127. package/dist/drivers/WdaDriver.js.map +1 -1
  128. package/dist/explore/candidates.js +7 -3
  129. package/dist/explore/candidates.js.map +1 -1
  130. package/dist/explore/graph.js +15 -6
  131. package/dist/explore/graph.js.map +1 -1
  132. package/dist/explore/memory.js +4 -1
  133. package/dist/explore/memory.js.map +1 -1
  134. package/dist/explore/planner.js +54 -9
  135. package/dist/explore/planner.js.map +1 -1
  136. package/dist/explore/policy.js +11 -6
  137. package/dist/explore/policy.js.map +1 -1
  138. package/dist/explore/runner.js +363 -190
  139. package/dist/explore/runner.js.map +1 -1
  140. package/dist/explore/signatures.js +4 -2
  141. package/dist/explore/signatures.js.map +1 -1
  142. package/dist/explore/suite.js +3 -1
  143. package/dist/explore/suite.js.map +1 -1
  144. package/dist/featureTesting/executionBootstrap.js +137 -29
  145. package/dist/featureTesting/executionBootstrap.js.map +1 -1
  146. package/dist/featureTesting/featureMap.js +6 -3
  147. package/dist/featureTesting/featureMap.js.map +1 -1
  148. package/dist/featureTesting/featureScope.js +101 -20
  149. package/dist/featureTesting/featureScope.js.map +1 -1
  150. package/dist/featureTesting/mapFeatureScope.js +44 -15
  151. package/dist/featureTesting/mapFeatureScope.js.map +1 -1
  152. package/dist/featureTesting/objectiveModel.js +15 -11
  153. package/dist/featureTesting/objectiveModel.js.map +1 -1
  154. package/dist/featureTesting/resultMerge.js +16 -8
  155. package/dist/featureTesting/resultMerge.js.map +1 -1
  156. package/dist/featureTesting/sources.js +8 -5
  157. package/dist/featureTesting/sources.js.map +1 -1
  158. package/dist/featureTesting/suiteBridge.js +8 -2
  159. package/dist/featureTesting/suiteBridge.js.map +1 -1
  160. package/dist/featureTesting/synonyms.js +32 -4
  161. package/dist/featureTesting/synonyms.js.map +1 -1
  162. package/dist/featureTesting/testCaseFactory.js +27 -15
  163. package/dist/featureTesting/testCaseFactory.js.map +1 -1
  164. package/dist/featureTesting/testPlan.js +34 -12
  165. package/dist/featureTesting/testPlan.js.map +1 -1
  166. package/dist/firstRun/authStateMachine.js +1 -1
  167. package/dist/firstRun/authStateMachine.js.map +1 -1
  168. package/dist/firstRun/classifyScreen.js +9 -6
  169. package/dist/firstRun/classifyScreen.js.map +1 -1
  170. package/dist/firstRun/firstRunPlanner.js +16 -13
  171. package/dist/firstRun/firstRunPlanner.js.map +1 -1
  172. package/dist/firstRun/firstRunRunner.js +92 -31
  173. package/dist/firstRun/firstRunRunner.js.map +1 -1
  174. package/dist/firstRun/generatedDataPolicy.js +25 -15
  175. package/dist/firstRun/generatedDataPolicy.js.map +1 -1
  176. package/dist/firstRun/inputPlanner.js +45 -11
  177. package/dist/firstRun/inputPlanner.js.map +1 -1
  178. package/dist/firstRun/onboardingStateMachine.js +1 -1
  179. package/dist/firstRun/onboardingStateMachine.js.map +1 -1
  180. package/dist/firstRun/paywallPolicy.js +3 -3
  181. package/dist/firstRun/paywallPolicy.js.map +1 -1
  182. package/dist/firstRun/types.js +1 -1
  183. package/dist/firstRun/types.js.map +1 -1
  184. package/dist/fixtures/catalog.js +72 -9
  185. package/dist/fixtures/catalog.js.map +1 -1
  186. package/dist/fixtures/load.js +50 -0
  187. package/dist/fixtures/load.js.map +1 -0
  188. package/dist/flows/discover.js +2 -2
  189. package/dist/flows/discover.js.map +1 -1
  190. package/dist/flows/generate.js +68 -17
  191. package/dist/flows/generate.js.map +1 -1
  192. package/dist/flows/lint.js +88 -14
  193. package/dist/flows/lint.js.map +1 -1
  194. package/dist/flows/pack.js +21 -5
  195. package/dist/flows/pack.js.map +1 -1
  196. package/dist/flows/paths.js +57 -0
  197. package/dist/flows/paths.js.map +1 -0
  198. package/dist/flows/repair.js +176 -37
  199. package/dist/flows/repair.js.map +1 -1
  200. package/dist/flows/run.js +308 -99
  201. package/dist/flows/run.js.map +1 -1
  202. package/dist/flows/schema.js +107 -25
  203. package/dist/flows/schema.js.map +1 -1
  204. package/dist/flows/seedExec.js +8 -4
  205. package/dist/flows/seedExec.js.map +1 -1
  206. package/dist/flows/templates.js +5 -5
  207. package/dist/index.js +50 -8
  208. package/dist/index.js.map +1 -1
  209. package/dist/ios/signing.js +32 -3
  210. package/dist/ios/signing.js.map +1 -1
  211. package/dist/issues/classify.js +28 -20
  212. package/dist/issues/classify.js.map +1 -1
  213. package/dist/issues/fingerprint.js +35 -9
  214. package/dist/issues/fingerprint.js.map +1 -1
  215. package/dist/issues/index.js +93 -46
  216. package/dist/issues/index.js.map +1 -1
  217. package/dist/issues/metrics.js +6 -5
  218. package/dist/issues/metrics.js.map +1 -1
  219. package/dist/issues/recurrence.js +35 -8
  220. package/dist/issues/recurrence.js.map +1 -1
  221. package/dist/issues/report.js +12 -8
  222. package/dist/issues/report.js.map +1 -1
  223. package/dist/issues/reportBridge.js +40 -9
  224. package/dist/issues/reportBridge.js.map +1 -1
  225. package/dist/issues/schema.js +2 -2
  226. package/dist/issues/schema.js.map +1 -1
  227. package/dist/issues/sourceRevision.js +2 -2
  228. package/dist/issues/sourceRevision.js.map +1 -1
  229. package/dist/issues/store.js +110 -32
  230. package/dist/issues/store.js.map +1 -1
  231. package/dist/lib/abortScope.js +48 -0
  232. package/dist/lib/abortScope.js.map +1 -0
  233. package/dist/lib/android.js +84 -8
  234. package/dist/lib/android.js.map +1 -1
  235. package/dist/lib/coordSpace.js +1 -1
  236. package/dist/lib/coordSpace.js.map +1 -1
  237. package/dist/lib/device.js +53 -12
  238. package/dist/lib/device.js.map +1 -1
  239. package/dist/lib/gestures.js +141 -0
  240. package/dist/lib/gestures.js.map +1 -0
  241. package/dist/lib/gitignore.js +4 -3
  242. package/dist/lib/gitignore.js.map +1 -1
  243. package/dist/lib/image.js +12 -3
  244. package/dist/lib/image.js.map +1 -1
  245. package/dist/lib/lockfile.js +399 -0
  246. package/dist/lib/lockfile.js.map +1 -0
  247. package/dist/lib/needsInput.js +9 -6
  248. package/dist/lib/needsInput.js.map +1 -1
  249. package/dist/lib/png.js +4 -3
  250. package/dist/lib/png.js.map +1 -1
  251. package/dist/lib/redact.js +170 -9
  252. package/dist/lib/redact.js.map +1 -1
  253. package/dist/lib/result.js +94 -10
  254. package/dist/lib/result.js.map +1 -1
  255. package/dist/lib/schemaHash.js +7 -5
  256. package/dist/lib/schemaHash.js.map +1 -1
  257. package/dist/lib/sensitive.js +6 -3
  258. package/dist/lib/sensitive.js.map +1 -1
  259. package/dist/lib/simctl.js +3 -3
  260. package/dist/lib/simctl.js.map +1 -1
  261. package/dist/lib/spawn.js +78 -17
  262. package/dist/lib/spawn.js.map +1 -1
  263. package/dist/lib/toolAnnotations.js +114 -0
  264. package/dist/lib/toolAnnotations.js.map +1 -0
  265. package/dist/lib/wda.js +321 -25
  266. package/dist/lib/wda.js.map +1 -1
  267. package/dist/lib/wdaConfig.js +12 -5
  268. package/dist/lib/wdaConfig.js.map +1 -1
  269. package/dist/lib/wdaTune.js +52 -23
  270. package/dist/lib/wdaTune.js.map +1 -1
  271. package/dist/mobileAudit/checks.js +150 -28
  272. package/dist/mobileAudit/checks.js.map +1 -1
  273. package/dist/mobileAudit/evidence.js +4 -4
  274. package/dist/mobileAudit/evidence.js.map +1 -1
  275. package/dist/mobileAudit/profiles.js +121 -20
  276. package/dist/mobileAudit/profiles.js.map +1 -1
  277. package/dist/mobileAudit/results.js +9 -4
  278. package/dist/mobileAudit/results.js.map +1 -1
  279. package/dist/mobileAudit/runner.js +214 -35
  280. package/dist/mobileAudit/runner.js.map +1 -1
  281. package/dist/oracle/auth.js +1 -1
  282. package/dist/oracle/auth.js.map +1 -1
  283. package/dist/oracle/failures.js +1085 -145
  284. package/dist/oracle/failures.js.map +1 -1
  285. package/dist/oracle/health.js +148 -26
  286. package/dist/oracle/health.js.map +1 -1
  287. package/dist/oracle/locator.js +148 -19
  288. package/dist/oracle/locator.js.map +1 -1
  289. package/dist/oracle/record.js +3 -3
  290. package/dist/oracle/record.js.map +1 -1
  291. package/dist/orchestration/envelope.js +2 -2
  292. package/dist/orchestration/envelope.js.map +1 -1
  293. package/dist/orchestration/goal.js +7 -13
  294. package/dist/orchestration/goal.js.map +1 -1
  295. package/dist/orchestration/testThis/execute.js +167 -0
  296. package/dist/orchestration/testThis/execute.js.map +1 -0
  297. package/dist/orchestration/testThis/pipeline.js +413 -0
  298. package/dist/orchestration/testThis/pipeline.js.map +1 -0
  299. package/dist/orchestration/testThis/plan.js +519 -0
  300. package/dist/orchestration/testThis/plan.js.map +1 -0
  301. package/dist/orchestration/testThis/sessionIntent.js +72 -0
  302. package/dist/orchestration/testThis/sessionIntent.js.map +1 -0
  303. package/dist/orchestration/testThis/terminal.js +136 -0
  304. package/dist/orchestration/testThis/terminal.js.map +1 -0
  305. package/dist/orchestration/testThis/types.js +12 -0
  306. package/dist/orchestration/testThis/types.js.map +1 -0
  307. package/dist/plan/plan.js +9 -9
  308. package/dist/plan/plan.js.map +1 -1
  309. package/dist/prompts/index.js +24 -24
  310. package/dist/prompts/index.js.map +1 -1
  311. package/dist/report/coverage.js +0 -1
  312. package/dist/report/coverage.js.map +1 -1
  313. package/dist/report/evidence.js.map +1 -1
  314. package/dist/report/export.js +449 -72
  315. package/dist/report/export.js.map +1 -1
  316. package/dist/report/findingsDedupe.js +41 -0
  317. package/dist/report/findingsDedupe.js.map +1 -0
  318. package/dist/report/flake.js +13 -14
  319. package/dist/report/flake.js.map +1 -1
  320. package/dist/report/history.js +72 -38
  321. package/dist/report/history.js.map +1 -1
  322. package/dist/report/policy.js +18 -7
  323. package/dist/report/policy.js.map +1 -1
  324. package/dist/report/qaLevel.js +10 -16
  325. package/dist/report/qaLevel.js.map +1 -1
  326. package/dist/report/readiness.js +42 -12
  327. package/dist/report/readiness.js.map +1 -1
  328. package/dist/report/sarifSources.js +160 -0
  329. package/dist/report/sarifSources.js.map +1 -0
  330. package/dist/report/summary.js +2 -2
  331. package/dist/report/summary.js.map +1 -1
  332. package/dist/report/testCatalog.js +4 -4
  333. package/dist/report/testCatalog.js.map +1 -1
  334. package/dist/report/timing.js +23 -23
  335. package/dist/report/timing.js.map +1 -1
  336. package/dist/report/toolHealth.js +124 -0
  337. package/dist/report/toolHealth.js.map +1 -0
  338. package/dist/server.js +501 -69
  339. package/dist/server.js.map +1 -1
  340. package/dist/services/automationGenerate.js +8 -2
  341. package/dist/services/automationGenerate.js.map +1 -1
  342. package/dist/services/build.js +10 -4
  343. package/dist/services/build.js.map +1 -1
  344. package/dist/services/flowGenerate.js +75 -0
  345. package/dist/services/flowGenerate.js.map +1 -0
  346. package/dist/services/preflight.js +40 -8
  347. package/dist/services/preflight.js.map +1 -1
  348. package/dist/services/prepareAndroid.js +92 -18
  349. package/dist/services/prepareAndroid.js.map +1 -1
  350. package/dist/services/prepareIos.js +94 -17
  351. package/dist/services/prepareIos.js.map +1 -1
  352. package/dist/services/report.js +271 -80
  353. package/dist/services/report.js.map +1 -1
  354. package/dist/services/smoke.js +68 -22
  355. package/dist/services/smoke.js.map +1 -1
  356. package/dist/services/suiteGenerate.js +43 -6
  357. package/dist/services/suiteGenerate.js.map +1 -1
  358. package/dist/services/testSuiteKnowledge.js +78 -33
  359. package/dist/services/testSuiteKnowledge.js.map +1 -1
  360. package/dist/session/attach.js +284 -4
  361. package/dist/session/attach.js.map +1 -1
  362. package/dist/session/processRegistry.js +433 -0
  363. package/dist/session/processRegistry.js.map +1 -0
  364. package/dist/session/progress.js +1 -1
  365. package/dist/session/progress.js.map +1 -1
  366. package/dist/session/retention.js +201 -0
  367. package/dist/session/retention.js.map +1 -0
  368. package/dist/session/store.js +459 -59
  369. package/dist/session/store.js.map +1 -1
  370. package/dist/snapshot/overlays.js +60 -10
  371. package/dist/snapshot/overlays.js.map +1 -1
  372. package/dist/snapshot/parse.js +30 -12
  373. package/dist/snapshot/parse.js.map +1 -1
  374. package/dist/snapshot/present.js +36 -6
  375. package/dist/snapshot/present.js.map +1 -1
  376. package/dist/snapshot/settle.js +39 -9
  377. package/dist/snapshot/settle.js.map +1 -1
  378. package/dist/state/consent.js +99 -0
  379. package/dist/state/consent.js.map +1 -0
  380. package/dist/state/profile.js +82 -11
  381. package/dist/state/profile.js.map +1 -1
  382. package/dist/suite/compile.js +15 -6
  383. package/dist/suite/compile.js.map +1 -1
  384. package/dist/suite/lint.js +22 -4
  385. package/dist/suite/lint.js.map +1 -1
  386. package/dist/suite/pom.js +75 -44
  387. package/dist/suite/pom.js.map +1 -1
  388. package/dist/suite/secretGuard.js +266 -0
  389. package/dist/suite/secretGuard.js.map +1 -0
  390. package/dist/suite/testcase.js +15 -11
  391. package/dist/suite/testcase.js.map +1 -1
  392. package/dist/testSuite/exporter.js +7 -3
  393. package/dist/testSuite/exporter.js.map +1 -1
  394. package/dist/testSuite/generator.js +43 -14
  395. package/dist/testSuite/generator.js.map +1 -1
  396. package/dist/testSuite/history.js +1 -1
  397. package/dist/testSuite/history.js.map +1 -1
  398. package/dist/testSuite/issueLinks.js +3 -3
  399. package/dist/testSuite/issueLinks.js.map +1 -1
  400. package/dist/testSuite/lint.js +4 -4
  401. package/dist/testSuite/lint.js.map +1 -1
  402. package/dist/testSuite/merge.js +13 -7
  403. package/dist/testSuite/merge.js.map +1 -1
  404. package/dist/testSuite/schema.js +21 -16
  405. package/dist/testSuite/schema.js.map +1 -1
  406. package/dist/testSuite/store.js +12 -4
  407. package/dist/testSuite/store.js.map +1 -1
  408. package/dist/testSuite/traceability.js +2 -2
  409. package/dist/testSuite/traceability.js.map +1 -1
  410. package/dist/tools/act.js +868 -174
  411. package/dist/tools/act.js.map +1 -1
  412. package/dist/tools/agent.js +521 -112
  413. package/dist/tools/agent.js.map +1 -1
  414. package/dist/tools/appControl.js +82 -19
  415. package/dist/tools/appControl.js.map +1 -1
  416. package/dist/tools/appMap.js +393 -189
  417. package/dist/tools/appMap.js.map +1 -1
  418. package/dist/tools/build.js +92 -51
  419. package/dist/tools/build.js.map +1 -1
  420. package/dist/tools/bundletool.js +141 -40
  421. package/dist/tools/bundletool.js.map +1 -1
  422. package/dist/tools/clearOverlay.js +98 -18
  423. package/dist/tools/clearOverlay.js.map +1 -1
  424. package/dist/tools/device.js +108 -19
  425. package/dist/tools/device.js.map +1 -1
  426. package/dist/tools/doctor.js +98 -35
  427. package/dist/tools/doctor.js.map +1 -1
  428. package/dist/tools/explore.js +110 -42
  429. package/dist/tools/explore.js.map +1 -1
  430. package/dist/tools/featureTesting.js +152 -130
  431. package/dist/tools/featureTesting.js.map +1 -1
  432. package/dist/tools/firstRun.js +94 -66
  433. package/dist/tools/firstRun.js.map +1 -1
  434. package/dist/tools/flow.js +347 -132
  435. package/dist/tools/flow.js.map +1 -1
  436. package/dist/tools/flowRepair.js +42 -10
  437. package/dist/tools/flowRepair.js.map +1 -1
  438. package/dist/tools/generate.js +199 -0
  439. package/dist/tools/generate.js.map +1 -0
  440. package/dist/tools/getArtifact.js +37 -8
  441. package/dist/tools/getArtifact.js.map +1 -1
  442. package/dist/tools/health.js +17 -12
  443. package/dist/tools/health.js.map +1 -1
  444. package/dist/tools/ios.js +148 -194
  445. package/dist/tools/ios.js.map +1 -1
  446. package/dist/tools/issues.js +256 -171
  447. package/dist/tools/issues.js.map +1 -1
  448. package/dist/tools/jobs.js +55 -11
  449. package/dist/tools/jobs.js.map +1 -1
  450. package/dist/tools/metro.js +84 -38
  451. package/dist/tools/metro.js.map +1 -1
  452. package/dist/tools/mobileAudit.js +83 -26
  453. package/dist/tools/mobileAudit.js.map +1 -1
  454. package/dist/tools/network.js +36 -14
  455. package/dist/tools/network.js.map +1 -1
  456. package/dist/tools/note.js +31 -17
  457. package/dist/tools/note.js.map +1 -1
  458. package/dist/tools/prepareIosTarget.js +31 -14
  459. package/dist/tools/prepareIosTarget.js.map +1 -1
  460. package/dist/tools/prepareTarget.js +193 -47
  461. package/dist/tools/prepareTarget.js.map +1 -1
  462. package/dist/tools/report.js +16 -8
  463. package/dist/tools/report.js.map +1 -1
  464. package/dist/tools/resolveArtifact.js +12 -12
  465. package/dist/tools/resolveArtifact.js.map +1 -1
  466. package/dist/tools/resolveTarget.js +81 -18
  467. package/dist/tools/resolveTarget.js.map +1 -1
  468. package/dist/tools/screenRecord.js +85 -20
  469. package/dist/tools/screenRecord.js.map +1 -1
  470. package/dist/tools/screenshot.js +42 -18
  471. package/dist/tools/screenshot.js.map +1 -1
  472. package/dist/tools/smoke.js +29 -12
  473. package/dist/tools/smoke.js.map +1 -1
  474. package/dist/tools/snapshot.js +157 -107
  475. package/dist/tools/snapshot.js.map +1 -1
  476. package/dist/tools/startSession.js +117 -84
  477. package/dist/tools/startSession.js.map +1 -1
  478. package/dist/tools/suite.js +391 -310
  479. package/dist/tools/suite.js.map +1 -1
  480. package/dist/tools/testSuite.js +112 -36
  481. package/dist/tools/testSuite.js.map +1 -1
  482. package/dist/tools/testThis.js +35 -802
  483. package/dist/tools/testThis.js.map +1 -1
  484. package/dist/tools/visual.js +698 -116
  485. package/dist/tools/visual.js.map +1 -1
  486. package/dist/tools/wait.js +9 -23
  487. package/dist/tools/wait.js.map +1 -1
  488. package/dist/tools/wda.js +312 -54
  489. package/dist/tools/wda.js.map +1 -1
  490. package/dist/version.js +37 -64
  491. package/dist/version.js.map +1 -1
  492. package/dist/visual/ocr.js +65 -8
  493. package/dist/visual/ocr.js.map +1 -1
  494. package/dist/visual/provider.js +87 -15
  495. package/dist/visual/provider.js.map +1 -1
  496. package/docs/README.md +24 -10
  497. package/docs/ci-reports.md +328 -0
  498. package/docs/concepts.md +227 -0
  499. package/docs/flows.md +153 -0
  500. package/docs/mcp-server.md +210 -104
  501. package/docs/physical-devices.md +84 -0
  502. package/docs/tools.md +808 -217
  503. package/package.json +16 -6
  504. package/dist/appMap/diff.js +0 -60
  505. package/dist/appMap/diff.js.map +0 -1
  506. package/dist/automation/maestroIr.js +0 -282
  507. package/dist/automation/maestroIr.js.map +0 -1
  508. package/dist/interop/maestro.js +0 -151
  509. package/dist/interop/maestro.js.map +0 -1
  510. package/dist/tools/assertVisual.js +0 -74
  511. package/dist/tools/assertVisual.js.map +0 -1
  512. package/dist/tools/automationGenerate.js +0 -394
  513. package/dist/tools/automationGenerate.js.map +0 -1
  514. package/dist/tools/capabilities.js +0 -209
  515. package/dist/tools/capabilities.js.map +0 -1
  516. package/dist/tools/detectContext.js +0 -41
  517. package/dist/tools/detectContext.js.map +0 -1
  518. package/dist/tools/flowGenerate.js +0 -55
  519. package/dist/tools/flowGenerate.js.map +0 -1
  520. package/dist/tools/history.js +0 -56
  521. package/dist/tools/history.js.map +0 -1
  522. package/dist/tools/idling.js +0 -104
  523. package/dist/tools/idling.js.map +0 -1
  524. package/dist/tools/inputCapabilities.js +0 -30
  525. package/dist/tools/inputCapabilities.js.map +0 -1
  526. package/dist/tools/locator.js +0 -57
  527. package/dist/tools/locator.js.map +0 -1
  528. package/dist/tools/maestro.js +0 -78
  529. package/dist/tools/maestro.js.map +0 -1
  530. package/dist/tools/permissions.js +0 -149
  531. package/dist/tools/permissions.js.map +0 -1
  532. package/dist/tools/plan.js +0 -49
  533. package/dist/tools/plan.js.map +0 -1
  534. package/dist/tools/screenInfo.js +0 -70
  535. package/dist/tools/screenInfo.js.map +0 -1
  536. package/dist/tools/seed.js +0 -101
  537. package/dist/tools/seed.js.map +0 -1
  538. package/dist/tools/state.js +0 -170
  539. package/dist/tools/state.js.map +0 -1
  540. package/dist/tools/visualText.js +0 -61
  541. package/dist/tools/visualText.js.map +0 -1
@@ -1,16 +1,75 @@
1
- // SWIPIUM-REQ-04 — JS/TS WebdriverIO + Appium emitter. Pure: turns the shared Appium model into a
1
+ // JS/TS WebdriverIO + Appium emitter. Pure: turns the shared Appium model into a
2
2
  // runnable WebdriverIO POM suite (page/screen classes with locator getters + action methods, env-
3
3
  // driven capabilities, structured waits). No XPath is ever emitted; coordinate fallbacks are marked
4
4
  // non-release-grade. Aligns with the Appium JS quickstart (WebdriverIO) locator-strategy guidance.
5
+ import { defaultPlatformOf } from './platformResolve.js';
6
+ import { NameAllocator, UnemittableStepError, checkDirection, checkKey, className, commentSafe, isJsIdentifier, jsMemberName, pascalWords, screensOf, } from './identifiers.js';
5
7
  const DEFAULT_TIMEOUT = 15000;
6
- function pascal(s) {
7
- const parts = s.replace(/[^A-Za-z0-9]+/g, ' ').trim().split(/\s+/).filter(Boolean);
8
- return parts.map((p) => p[0].toUpperCase() + p.slice(1)).join('') || 'X';
8
+ const MAX_SCROLLS = 8;
9
+ /** Every member BaseScreen declares. A generated field/method must never shadow one (a field named
10
+ * `tap` would replace the tap() helper, and in TS it is a type error). */
11
+ const JS_BASE_MEMBERS = [
12
+ 'constructor',
13
+ 'element',
14
+ 'tap',
15
+ 'type',
16
+ 'typeFocused',
17
+ 'scrollTo',
18
+ 'scrollToText',
19
+ 'scrollOnce',
20
+ 'isVisible',
21
+ 'assertVisible',
22
+ 'assertTextVisible',
23
+ 'textSelector',
24
+ 'tapAt',
25
+ 'pressKey',
26
+ 'swipe',
27
+ 'openUrl',
28
+ ];
29
+ function methodKey(action, element) {
30
+ return `${action}|${element}`;
9
31
  }
10
- /** Env var (SWIPIUM_TEST_PASSWORD) → a stable testData key (password). */
32
+ function planJsNames(model) {
33
+ const classes = new NameAllocator(['BaseScreen', 'TestData', 'PlatformLocator'], (s) => s.toLowerCase());
34
+ const instances = new NameAllocator(['testData', 'driver', 'browser', 'describe', 'it', 'expect', '$', '$$']);
35
+ const plan = new Map();
36
+ for (const screen of screensOf(model)) {
37
+ const cls = classes.alloc(className(screen.className));
38
+ const instance = instances.alloc(cls[0].toLowerCase() + cls.slice(1));
39
+ const members = new NameAllocator(JS_BASE_MEMBERS);
40
+ const fields = new Map();
41
+ for (const el of screen.elements)
42
+ if (!fields.has(el.name))
43
+ fields.set(el.name, members.alloc(jsMemberName(el.name)));
44
+ const methods = new Map();
45
+ for (const s of model.steps) {
46
+ if (s.screen !== screen.className || !s.element)
47
+ continue;
48
+ const field = fields.get(s.element);
49
+ if (!field) {
50
+ throw new UnemittableStepError(`cannot emit ${s.action} step on ${screen.className}: element ${JSON.stringify(s.element)} is not declared on that screen`);
51
+ }
52
+ const key = methodKey(s.action, s.element);
53
+ if (methods.has(key))
54
+ continue;
55
+ methods.set(key, members.alloc(methodBase(s.action, pascalWords(field) || 'Element')));
56
+ }
57
+ plan.set(screen.className, { cls, instance, fields, methods });
58
+ }
59
+ return plan;
60
+ }
61
+ function jsProp(obj, key) {
62
+ return isJsIdentifier(key) ? `${obj}.${key}` : `${obj}[${JSON.stringify(key)}]`;
63
+ }
64
+ /** Env var (SWIPIUM_TEST_PASSWORD) > a stable testData key (password). */
11
65
  export function varToKey(v) {
12
66
  const stripped = v.replace(/^SWIPIUM_(TEST_)?/i, '');
13
- const parts = stripped.replace(/[^A-Za-z0-9]+/g, ' ').trim().toLowerCase().split(/\s+/).filter(Boolean);
67
+ const parts = stripped
68
+ .replace(/[^A-Za-z0-9]+/g, ' ')
69
+ .trim()
70
+ .toLowerCase()
71
+ .split(/\s+/)
72
+ .filter(Boolean);
14
73
  if (!parts.length)
15
74
  return 'value';
16
75
  return parts.map((p, i) => (i === 0 ? p : p[0].toUpperCase() + p.slice(1))).join('');
@@ -51,8 +110,11 @@ function platformLocatorLiteral(el) {
51
110
  export function emitJsSuite(input) {
52
111
  const ts = input.language === 'typescript';
53
112
  const ext = ts ? 'ts' : 'js';
54
- const t = (annotation) => (ts ? annotation : '');
55
113
  const files = [];
114
+ // Plan every class/field/method name up front (H4) and validate every step is emittable (§4);
115
+ // generation fails loudly here rather than writing a suite with silent no-op steps.
116
+ const names = planJsNames(input.model);
117
+ const smoke = smokeTest(input.model, names);
56
118
  files.push({ path: 'package.json', content: packageJson(input) });
57
119
  if (ts)
58
120
  files.push({ path: 'tsconfig.json', content: tsconfigJson() });
@@ -61,11 +123,12 @@ export function emitJsSuite(input) {
61
123
  files.push({ path: `src/utils/locators.${ext}`, content: locatorsUtil(ts) });
62
124
  files.push({ path: `src/utils/waits.${ext}`, content: waitsUtil(ts) });
63
125
  files.push({ path: `src/screens/BaseScreen.${ext}`, content: baseScreen(ts) });
64
- for (const screen of input.model.screens) {
65
- files.push({ path: `src/screens/${screen.className}.${ext}`, content: screenClass(screen, input.model.steps, ts, t) });
126
+ for (const screen of screensOf(input.model)) {
127
+ const n = names.get(screen.className);
128
+ files.push({ path: `src/screens/${n.cls}.${ext}`, content: screenClass(screen, n, input.model.steps, ts) });
66
129
  }
67
130
  files.push({ path: `src/data/testData.${ext}`, content: testData(input.model, ts) });
68
- files.push({ path: `test/smoke.e2e.${ext}`, content: smokeTest(input.model, ts) });
131
+ files.push({ path: `test/smoke.e2e.${ext}`, content: smoke });
69
132
  return files;
70
133
  }
71
134
  function packageJson(input) {
@@ -75,7 +138,7 @@ function packageJson(input) {
75
138
  name,
76
139
  private: true,
77
140
  type: 'module',
78
- description: 'Generated by Swipium — WebdriverIO + Appium POM suite (review before committing).',
141
+ description: 'Generated by Swipium: WebdriverIO + Appium POM suite (review before committing).',
79
142
  scripts: {
80
143
  test: 'wdio run ./wdio.conf.' + (ts ? 'ts' : 'js'),
81
144
  },
@@ -92,7 +155,7 @@ function packageJson(input) {
92
155
  return JSON.stringify(pkg, null, 2) + '\n';
93
156
  }
94
157
  function tsconfigJson() {
95
- return JSON.stringify({
158
+ return (JSON.stringify({
96
159
  compilerOptions: {
97
160
  target: 'ES2022',
98
161
  module: 'NodeNext',
@@ -103,10 +166,10 @@ function tsconfigJson() {
103
166
  skipLibCheck: true,
104
167
  },
105
168
  include: ['src/**/*.ts', 'test/**/*.ts', 'wdio.conf.ts'],
106
- }, null, 2) + '\n';
169
+ }, null, 2) + '\n');
107
170
  }
108
171
  function wdioConf(ts, ext) {
109
- const header = '// Generated by Swipium. WebdriverIO + Appium config — capabilities come from env (see src/config/capabilities).';
172
+ const header = '// Generated by Swipium. WebdriverIO + Appium config. Capabilities come from env (see src/config/capabilities).';
110
173
  const importLine = `import { buildCapabilities } from './src/config/capabilities.js';`;
111
174
  const typeAnno = ts ? `\nimport type { Options } from '@wdio/types';\n` : '\n';
112
175
  const cfgType = ts ? ': WebdriverIO.Config' : '';
@@ -130,38 +193,39 @@ export const config${cfgType} = {
130
193
  `;
131
194
  }
132
195
  function capabilities(input, ts) {
133
- const android = input.model.platforms.android;
134
- const ios = input.model.platforms.ios;
196
+ const primary = defaultPlatformOf(input.profile, input.model);
135
197
  const ret = ts ? ': WebdriverIO.Capabilities' : '';
136
198
  const appId = input.appId ?? '';
137
199
  const lines = [];
138
- lines.push('// Generated by Swipium. ALL device/app config is environment-driven — no secrets, no hardcoded paths.');
139
- lines.push('// Android is the default fast-feedback backend; set SWIPIUM_PLATFORM=ios to target iOS (XCUITest).');
200
+ lines.push('// Generated by Swipium. ALL device/app config is environment-driven: no secrets, no hardcoded paths.');
201
+ lines.push(primary === 'ios'
202
+ ? '// iOS (XCUITest) is the default for this suite; set SWIPIUM_PLATFORM=android to target Android (UiAutomator2).'
203
+ : '// Android (UiAutomator2) is the default for this suite; set SWIPIUM_PLATFORM=ios to target iOS (XCUITest).');
140
204
  lines.push('');
141
205
  lines.push(`export function buildCapabilities()${ret} {`);
142
- lines.push(` const platform = (process.env.SWIPIUM_PLATFORM || ${JSON.stringify(android ? 'android' : 'ios')}).toLowerCase();`);
143
- lines.push(' if (platform === \'ios\') {');
206
+ lines.push(` const platform = (process.env.SWIPIUM_PLATFORM || ${JSON.stringify(primary)}).toLowerCase();`);
207
+ lines.push(" if (platform === 'ios') {");
144
208
  lines.push(' return {');
145
- lines.push(' platformName: \'iOS\',');
146
- lines.push(' \'appium:automationName\': \'XCUITest\',');
147
- lines.push(' \'appium:deviceName\': process.env.IOS_DEVICE_NAME || \'iPhone 15\',');
148
- lines.push(' \'appium:platformVersion\': process.env.IOS_PLATFORM_VERSION,');
209
+ lines.push(" platformName: 'iOS',");
210
+ lines.push(" 'appium:automationName': 'XCUITest',");
211
+ lines.push(" 'appium:deviceName': process.env.IOS_DEVICE_NAME || 'iPhone 15',");
212
+ lines.push(" 'appium:platformVersion': process.env.IOS_PLATFORM_VERSION,");
149
213
  lines.push(` 'appium:bundleId': process.env.IOS_BUNDLE_ID${appId ? ` || ${JSON.stringify(appId)}` : ''},`);
150
- lines.push(' \'appium:app\': process.env.IOS_APP_PATH,');
151
- lines.push(' \'appium:udid\': process.env.IOS_UDID,');
152
- lines.push(' \'appium:noReset\': process.env.SWIPIUM_NO_RESET === \'true\',');
214
+ lines.push(" 'appium:app': process.env.IOS_APP_PATH,");
215
+ lines.push(" 'appium:udid': process.env.IOS_UDID,");
216
+ lines.push(" 'appium:noReset': process.env.SWIPIUM_NO_RESET === 'true',");
153
217
  lines.push(' };');
154
218
  lines.push(' }');
155
- lines.push(' // Android UiAutomator2 (default).');
219
+ lines.push(' // Android UiAutomator2.');
156
220
  lines.push(' return {');
157
- lines.push(' platformName: \'Android\',');
158
- lines.push(' \'appium:automationName\': \'UiAutomator2\',');
159
- lines.push(' \'appium:deviceName\': process.env.ANDROID_DEVICE_NAME || \'Android Emulator\',');
221
+ lines.push(" platformName: 'Android',");
222
+ lines.push(" 'appium:automationName': 'UiAutomator2',");
223
+ lines.push(" 'appium:deviceName': process.env.ANDROID_DEVICE_NAME || 'Android Emulator',");
160
224
  lines.push(` 'appium:appPackage': process.env.ANDROID_APP_PACKAGE${appId ? ` || ${JSON.stringify(appId)}` : ''},`);
161
- lines.push(' \'appium:appActivity\': process.env.ANDROID_APP_ACTIVITY,');
162
- lines.push(' \'appium:app\': process.env.ANDROID_APP_PATH,');
163
- lines.push(' \'appium:udid\': process.env.ANDROID_UDID,');
164
- lines.push(' \'appium:noReset\': process.env.SWIPIUM_NO_RESET === \'true\',');
225
+ lines.push(" 'appium:appActivity': process.env.ANDROID_APP_ACTIVITY,");
226
+ lines.push(" 'appium:app': process.env.ANDROID_APP_PATH,");
227
+ lines.push(" 'appium:udid': process.env.ANDROID_UDID,");
228
+ lines.push(" 'appium:noReset': process.env.SWIPIUM_NO_RESET === 'true',");
165
229
  lines.push(' };');
166
230
  lines.push('}');
167
231
  lines.push('');
@@ -169,7 +233,7 @@ function capabilities(input, ts) {
169
233
  }
170
234
  function locatorsUtil(ts) {
171
235
  const typeBlock = ts
172
- ? `export interface PlatformLocator {\n android?: string;\n ios?: string;\n /** "x,y" coordinate fallback — non-release-grade. */\n coordinate?: string;\n}\n\n`
236
+ ? `export interface PlatformLocator {\n android?: string;\n ios?: string;\n /** "x,y" coordinate fallback, non-release-grade. */\n coordinate?: string;\n}\n\n`
173
237
  : '';
174
238
  const param = ts ? 'loc: PlatformLocator' : 'loc';
175
239
  const ret = ts ? ': string' : '';
@@ -179,7 +243,7 @@ ${typeBlock}export function resolveSelector(${param})${ret} {
179
243
  const android = typeof driver !== 'undefined' ? driver.isAndroid : true;
180
244
  const selector = android ? loc.android : loc.ios;
181
245
  if (!selector) {
182
- throw new Error('No durable selector for this platform — element relies on a coordinate fallback (non-release-grade). Add an accessibility id / resource-id / testID.');
246
+ throw new Error('No durable selector for this platform. Element relies on a coordinate fallback (non-release-grade). Add an accessibility id / resource-id / testID.');
183
247
  }
184
248
  return selector;
185
249
  }
@@ -194,7 +258,7 @@ export function coordinateOf(${param})${ts ? ': [number, number] | undefined' :
194
258
  function waitsUtil(ts) {
195
259
  const elAnno = ts ? ': WebdriverIO.Element' : '';
196
260
  const numAnno = ts ? ': number' : '';
197
- return `// Generated by Swipium. Structured waits — never a bare sleep. Auto-wait before interaction
261
+ return `// Generated by Swipium. Structured waits, never a bare sleep. Auto-wait before interaction
198
262
  // (Playwright-style resilience, applied to mobile Appium).
199
263
  export const DEFAULT_TIMEOUT = ${DEFAULT_TIMEOUT};
200
264
 
@@ -212,36 +276,92 @@ export async function waitEnabled(el${elAnno}, timeout${numAnno} = DEFAULT_TIMEO
212
276
  function baseScreen(ts) {
213
277
  const locImport = ts ? `import type { PlatformLocator } from '../utils/locators.js';\n` : '';
214
278
  const p = (name, type) => (ts ? `${name}: ${type}` : name);
279
+ // H3: TS-only modifiers/annotations are gated on the TypeScript flag so the .js output parses.
280
+ const prot = ts ? 'protected ' : '';
281
+ const dirType = ts ? `: 'up' | 'down' | 'left' | 'right'` : '';
215
282
  return `// Generated by Swipium. BaseScreen centralizes all element interaction so selectors are
216
283
  // resolved per-platform in ONE place and tests never touch raw selectors.
217
- ${locImport}import { resolveSelector, coordinateOf } from '../utils/locators.js';
218
- import { waitVisible, waitEnabled, DEFAULT_TIMEOUT } from '../utils/waits.js';
284
+ ${locImport}import { resolveSelector } from '../utils/locators.js';
285
+ import { waitVisible, waitEnabled } from '../utils/waits.js';
286
+
287
+ /** Upper bound on scroll gestures while looking for an element, so never an endless loop. */
288
+ export const MAX_SCROLLS = ${MAX_SCROLLS};
219
289
 
220
290
  export class BaseScreen {
221
- protected async element(${p('loc', 'PlatformLocator')}) {
291
+ ${prot}async element(${p('loc', 'PlatformLocator')}) {
222
292
  // @ts-ignore wdio global
223
293
  return $(resolveSelector(loc));
224
294
  }
225
295
 
226
- protected async tap(${p('loc', 'PlatformLocator')}) {
296
+ ${prot}async tap(${p('loc', 'PlatformLocator')}) {
227
297
  const el = await this.element(loc);
228
298
  await waitEnabled(await waitVisible(el));
229
299
  await el.click();
230
300
  }
231
301
 
232
- protected async type(${p('loc', 'PlatformLocator')}, ${p('value', 'string')}) {
302
+ ${prot}async type(${p('loc', 'PlatformLocator')}, ${p('value', 'string')}) {
233
303
  const el = await this.element(loc);
234
304
  await waitVisible(el);
235
305
  await el.setValue(value);
236
306
  }
237
307
 
238
- protected async scrollTo(${p('loc', 'PlatformLocator')}) {
239
- const el = await this.element(loc);
240
- await el.scrollIntoView();
241
- await waitVisible(el);
308
+ /** Type into whichever field currently has focus (the recording had no locator for it). */
309
+ async typeFocused(${p('value', 'string')}) {
310
+ // @ts-ignore wdio global
311
+ const active = await $(await driver.getActiveElement());
312
+ await active.addValue(value);
313
+ }
314
+
315
+ /**
316
+ * One real content-scroll gesture. direction is the CONTENT direction: 'down' reveals what is below.
317
+ * Android: UiAutomator2 \`mobile: scrollGesture\` (returns false once the end is reached).
318
+ * iOS: XCUITest \`mobile: scroll\`.
319
+ */
320
+ async scrollOnce(${p('direction', 'string')} = 'down')${ts ? ': Promise<boolean>' : ''} {
321
+ // @ts-ignore wdio global
322
+ if (driver.isAndroid) {
323
+ // @ts-ignore wdio global
324
+ const { width, height } = await driver.getWindowSize();
325
+ // @ts-ignore wdio global
326
+ const canScrollMore = await driver.execute('mobile: scrollGesture', {
327
+ left: Math.round(width * 0.1),
328
+ top: Math.round(height * 0.2),
329
+ width: Math.round(width * 0.8),
330
+ height: Math.round(height * 0.6),
331
+ direction,
332
+ percent: 0.75,
333
+ });
334
+ return canScrollMore !== false;
335
+ }
336
+ // @ts-ignore wdio global
337
+ await driver.execute('mobile: scroll', { direction });
338
+ return true;
339
+ }
340
+
341
+ /** Scroll (bounded by maxScrolls) until the element is displayed; throws if it never appears. */
342
+ ${prot}async scrollTo(${p('loc', 'PlatformLocator')}, ${p('direction', 'string')} = 'down', ${p('maxScrolls', 'number')} = MAX_SCROLLS) {
343
+ let atEnd = false;
344
+ for (let i = 0; i <= maxScrolls; i++) {
345
+ if (await this.isVisible(loc)) return;
346
+ if (i === maxScrolls || atEnd) break;
347
+ atEnd = !(await this.scrollOnce(direction));
348
+ }
349
+ throw new Error('Element not visible after scrolling ' + direction + ' (max ' + maxScrolls + ' scrolls)');
242
350
  }
243
351
 
244
- protected async isVisible(${p('loc', 'PlatformLocator')})${ts ? ': Promise<boolean>' : ''} {
352
+ /** Scroll (bounded) until an element whose text/label contains \`text\` is displayed. */
353
+ async scrollToText(${p('text', 'string')}, ${p('direction', 'string')} = 'down', ${p('maxScrolls', 'number')} = MAX_SCROLLS) {
354
+ let atEnd = false;
355
+ for (let i = 0; i <= maxScrolls; i++) {
356
+ // @ts-ignore wdio global
357
+ if (await $(this.textSelector(text)).isDisplayed()) return;
358
+ if (i === maxScrolls || atEnd) break;
359
+ atEnd = !(await this.scrollOnce(direction));
360
+ }
361
+ throw new Error('Text ' + JSON.stringify(text) + ' not visible after scrolling ' + direction + ' (max ' + maxScrolls + ' scrolls)');
362
+ }
363
+
364
+ ${prot}async isVisible(${p('loc', 'PlatformLocator')})${ts ? ': Promise<boolean>' : ''} {
245
365
  const el = await this.element(loc);
246
366
  return el.isDisplayed();
247
367
  }
@@ -251,28 +371,60 @@ export class BaseScreen {
251
371
  await waitVisible(el);
252
372
  }
253
373
 
374
+ /** Platform-appropriate "text contains" selector. JSON.stringify escapes backslashes and quotes. */
375
+ textSelector(${p('text', 'string')})${ts ? ': string' : ''} {
376
+ // @ts-ignore wdio global
377
+ if (driver.isAndroid) return 'android=new UiSelector().textContains(' + JSON.stringify(text) + ')';
378
+ return '-ios predicate string:label CONTAINS ' + JSON.stringify(text) + ' OR name CONTAINS ' + JSON.stringify(text);
379
+ }
380
+
254
381
  async assertTextVisible(${p('text', 'string')}) {
255
- // Non-release-grade text/OCR assertion — prefer a structured locator.
382
+ // Non-release-grade text/OCR assertion. Prefer a structured locator.
256
383
  // @ts-ignore wdio global
257
- const el = await $('android=new UiSelector().textContains(' + JSON.stringify(text) + ')');
384
+ const el = await $(this.textSelector(text));
258
385
  await waitVisible(el);
259
386
  }
260
387
 
261
- // Coordinate tap — brittle, non-release-grade fallback. Only used when no durable locator exists.
388
+ // Coordinate tap: brittle, non-release-grade fallback. Only used when no durable locator exists.
262
389
  async tapAt(${p('x', 'number')}, ${p('y', 'number')}) {
263
390
  // @ts-ignore wdio global
264
- await driver.action('pointer')
391
+ await driver.action('pointer', { parameters: { pointerType: 'touch' } })
265
392
  .move({ x, y }).down().pause(50).up().perform();
266
393
  }
267
394
 
268
- async pressKey(${p('key', 'string')}) {
269
- // @ts-ignore wdio global
270
- if (driver.isAndroid && key === 'back') { await driver.back(); return; }
395
+ async pressKey(${p('key', `'back' | 'home' | 'enter'`)}) {
271
396
  // @ts-ignore wdio global
272
- await driver.keys([key]);
397
+ const android = driver.isAndroid;
398
+ if (key === 'back') {
399
+ // @ts-ignore wdio global
400
+ if (android) { await driver.back(); return; }
401
+ // iOS has no system back key: swipe in from the left edge (UINavigationController back gesture).
402
+ // @ts-ignore wdio global
403
+ const { width, height } = await driver.getWindowSize();
404
+ // @ts-ignore wdio global
405
+ await driver.action('pointer', { parameters: { pointerType: 'touch' } })
406
+ .move({ x: 2, y: Math.round(height / 2) }).down().pause(50)
407
+ .move({ duration: 400, x: Math.round(width * 0.7), y: Math.round(height / 2) }).up().perform();
408
+ return;
409
+ }
410
+ if (key === 'home') {
411
+ // @ts-ignore wdio global
412
+ if (android) await driver.execute('mobile: pressKey', { keycode: 3 });
413
+ // @ts-ignore wdio global
414
+ else await driver.execute('mobile: pressButton', { name: 'home' });
415
+ return;
416
+ }
417
+ if (key === 'enter') {
418
+ // @ts-ignore wdio global
419
+ if (android) { await driver.execute('mobile: pressKey', { keycode: 66 }); return; }
420
+ await this.typeFocused('\\n');
421
+ return;
422
+ }
423
+ throw new Error('Unsupported key: ' + key);
273
424
  }
274
425
 
275
- async swipe(${p('direction', 'string')}) {
426
+ /** Real window-size-relative swipe. direction is the FINGER direction ('up' drags from bottom to top). */
427
+ async swipe(${p('direction', dirType.slice(2) || 'string')}) {
276
428
  // @ts-ignore wdio global
277
429
  const { width, height } = await driver.getWindowSize();
278
430
  const cx = Math.round(width / 2), cy = Math.round(height / 2);
@@ -282,9 +434,11 @@ export class BaseScreen {
282
434
  left: [Math.round(width * 0.8), cy, Math.round(width * 0.2), cy],
283
435
  right: [Math.round(width * 0.2), cy, Math.round(width * 0.8), cy],
284
436
  };
285
- const [x1, y1, x2, y2] = map[${ts ? 'direction as keyof typeof map' : 'direction'}] || map.up;
437
+ const vec = map[direction];
438
+ if (!vec) throw new Error('Unsupported swipe direction: ' + direction);
439
+ const [x1, y1, x2, y2] = vec;
286
440
  // @ts-ignore wdio global
287
- await driver.action('pointer')
441
+ await driver.action('pointer', { parameters: { pointerType: 'touch' } })
288
442
  .move({ x: x1, y: y1 }).down().pause(100).move({ duration: 600, x: x2, y: y2 }).up().perform();
289
443
  }
290
444
 
@@ -295,9 +449,8 @@ export class BaseScreen {
295
449
  }
296
450
  `;
297
451
  }
298
- /** Per-screen action method name for an (element, action) pair. */
299
- function methodName(action, element) {
300
- const E = pascal(element);
452
+ /** Base (pre-dedupe) action-method name for an (action, element) pair. */
453
+ function methodBase(action, E) {
301
454
  switch (action) {
302
455
  case 'tap':
303
456
  return `tap${E}`;
@@ -308,22 +461,23 @@ function methodName(action, element) {
308
461
  case 'assertVisible':
309
462
  return `assert${E}Visible`;
310
463
  default:
311
- return `act${E}`;
464
+ throw new UnemittableStepError(`cannot emit a ${action} step bound to an element: no generated action exists for it`);
312
465
  }
313
466
  }
314
- function screenClass(screen, steps, ts, t) {
315
- const locImport = ts ? `import type { PlatformLocator } from '../utils/locators.js';\n` : '';
467
+ function screenClass(screen, names, steps, ts) {
316
468
  const lines = [];
317
- lines.push(`// Generated by Swipium. Screen object for ${screen.pageName}${screen.screenSignature ? ` (${screen.screenSignature})` : ''}.`);
469
+ lines.push(`// Generated by Swipium. Screen object for ${commentSafe(screen.pageName)}${screen.screenSignature ? ` (${commentSafe(screen.screenSignature)})` : ''}.`);
318
470
  lines.push('// Selectors are centralized here; tests call the action methods below.');
319
- lines.push(locImport.trimEnd());
471
+ if (ts)
472
+ lines.push(`import type { PlatformLocator } from '../utils/locators.js';`);
320
473
  lines.push(`import { BaseScreen } from './BaseScreen.js';`);
321
474
  lines.push('');
322
- lines.push(`export class ${screen.className} extends BaseScreen {`);
323
- // Locators (centralized).
475
+ lines.push(`export class ${names.cls} extends BaseScreen {`);
476
+ // Locators (centralized). H3: `readonly` + the type annotation are TypeScript-only.
324
477
  for (const el of screen.elements) {
325
- const durabilityNote = el.durability === 'durable' ? '' : ` // ${el.durability} locator${el.remediation ? ` — ${el.remediation}` : ''}`;
326
- lines.push(` readonly ${el.name}${t(': PlatformLocator')} = ${platformLocatorLiteral(el)};${durabilityNote}`);
478
+ const field = names.fields.get(el.name);
479
+ const durabilityNote = el.durability === 'durable' ? '' : ` // ${el.durability} locator${el.remediation ? `: ${commentSafe(el.remediation)}` : ''}`;
480
+ lines.push(` ${ts ? 'readonly ' : ''}${field}${ts ? ': PlatformLocator' : ''} = ${platformLocatorLiteral(el)};${durabilityNote}`);
327
481
  }
328
482
  lines.push('');
329
483
  // Action methods for each (element, action) used in steps on this screen.
@@ -331,47 +485,47 @@ function screenClass(screen, steps, ts, t) {
331
485
  for (const s of steps) {
332
486
  if (s.screen !== screen.className || !s.element)
333
487
  continue;
334
- const el = screen.elements.find((e) => e.name === s.element);
335
- if (!el)
488
+ const key = methodKey(s.action, s.element);
489
+ if (seen.has(key))
336
490
  continue;
337
- const name = methodName(s.action, s.element);
338
- if (seen.has(name))
339
- continue;
340
- seen.add(name);
491
+ seen.add(key);
492
+ const name = names.methods.get(key);
493
+ const field = `this.${names.fields.get(s.element)}`;
341
494
  if (s.action === 'inputText') {
342
495
  lines.push(` async ${name}(${ts ? 'value: string' : 'value'}) {`);
343
- lines.push(` await this.type(this.${s.element}, value);`);
344
- lines.push(' }');
496
+ lines.push(` await this.type(${field}, value);`);
345
497
  }
346
498
  else if (s.action === 'tap') {
347
499
  lines.push(` async ${name}() {`);
348
- lines.push(` await this.tap(this.${s.element});`);
349
- lines.push(' }');
500
+ lines.push(` await this.tap(${field});`);
350
501
  }
351
502
  else if (s.action === 'scrollTo') {
352
- lines.push(` async ${name}() {`);
353
- lines.push(` await this.scrollTo(this.${s.element});`);
354
- lines.push(' }');
503
+ lines.push(` async ${name}(${ts ? "direction: string = 'down'" : "direction = 'down'"}) {`);
504
+ lines.push(` await this.scrollTo(${field}, direction);`);
355
505
  }
356
- else if (s.action === 'assertVisible') {
506
+ else {
357
507
  lines.push(` async ${name}() {`);
358
- lines.push(` await this.assertVisible(this.${s.element});`);
359
- lines.push(' }');
508
+ lines.push(` await this.assertVisible(${field});`);
360
509
  }
510
+ lines.push(' }');
361
511
  }
362
512
  lines.push('}');
363
513
  lines.push('');
364
514
  return lines.join('\n');
365
515
  }
516
+ function envRef(v) {
517
+ return jsProp('process.env', v);
518
+ }
366
519
  function testData(model, ts) {
367
520
  const lines = [];
368
- lines.push('// Generated by Swipium. Test data comes from the ENVIRONMENT — secrets are never inlined.');
521
+ const key = (v) => (isJsIdentifier(varToKey(v)) ? varToKey(v) : JSON.stringify(varToKey(v)));
522
+ lines.push('// Generated by Swipium. Test data comes from the ENVIRONMENT. Secrets are never inlined.');
369
523
  lines.push('export const testData = {');
370
524
  for (const v of model.variables) {
371
- lines.push(` ${varToKey(v)}: process.env.${v} || '',`);
525
+ lines.push(` ${key(v)}: ${envRef(v)} || '',`);
372
526
  }
373
527
  for (const v of model.secrets) {
374
- lines.push(` ${varToKey(v)}: process.env.${v} || '', // secret — provide via environment only`);
528
+ lines.push(` ${key(v)}: ${envRef(v)} || '', // secret, provide via environment only`);
375
529
  }
376
530
  lines.push('};');
377
531
  lines.push('');
@@ -380,61 +534,74 @@ function testData(model, ts) {
380
534
  lines.push('');
381
535
  return lines.join('\n');
382
536
  }
383
- function smokeTest(model, ts) {
537
+ function smokeTest(model, names) {
384
538
  const classes = [...new Set(model.steps.map((s) => s.screen))];
385
539
  const lines = [];
386
540
  lines.push('// Generated by Swipium. Smoke test driving the recorded flow through the screen objects.');
387
541
  for (const c of classes) {
388
- lines.push(`import { ${c} } from '../src/screens/${c}.js';`);
542
+ const cls = names.get(c).cls;
543
+ lines.push(`import { ${cls} } from '../src/screens/${cls}.js';`);
389
544
  }
390
545
  lines.push(`import { testData } from '../src/data/testData.js';`);
391
546
  lines.push('');
392
- lines.push(`describe('${model.testName} smoke', () => {`);
393
- // instantiate one screen object per class.
394
- const inst = new Map();
395
- for (const c of classes) {
396
- const v = c[0].toLowerCase() + c.slice(1);
397
- inst.set(c, v);
398
- }
399
- lines.push(` it('completes the recorded flow', async () => {`);
400
- for (const [c, v] of inst)
401
- lines.push(` const ${v} = new ${c}();`);
547
+ // H3: titles come from recorded data, so always a JSON-escaped literal, never raw interpolation.
548
+ lines.push(`describe(${JSON.stringify(`${model.testName} smoke`)}, () => {`);
549
+ lines.push(` it(${JSON.stringify('completes the recorded flow')}, async () => {`);
550
+ for (const c of classes)
551
+ lines.push(` const ${names.get(c).instance} = new ${names.get(c).cls}();`);
402
552
  for (const s of model.steps) {
403
- const v = inst.get(s.screen);
404
- lines.push(' ' + stepCall(v, s));
553
+ lines.push(' ' + stepCall(names.get(s.screen), s));
405
554
  }
406
555
  lines.push(' });');
407
556
  lines.push('});');
408
557
  lines.push('');
409
558
  return lines.join('\n');
410
559
  }
411
- function stepCall(screenVar, s) {
560
+ function dataArg(s) {
561
+ return s.varName ? jsProp('testData', varToKey(s.varName)) : JSON.stringify(s.text ?? '');
562
+ }
563
+ function num(n) {
564
+ return Number.isFinite(n) ? Math.round(n) : 0;
565
+ }
566
+ function stepCall(n, s) {
567
+ const v = n.instance;
412
568
  if (s.element) {
413
- const name = methodName(s.action, s.element);
414
- if (s.action === 'inputText') {
415
- const arg = s.varName ? `testData.${varToKey(s.varName)}` : JSON.stringify(s.text ?? '');
416
- return `await ${screenVar}.${name}(${arg});`;
417
- }
418
- return `await ${screenVar}.${name}();`;
569
+ const name = n.methods.get(methodKey(s.action, s.element));
570
+ if (s.action === 'inputText')
571
+ return `await ${v}.${name}(${dataArg(s)});`;
572
+ if (s.action === 'scrollTo')
573
+ return `await ${v}.${name}(${JSON.stringify(checkDirection(s.direction ?? 'down', 'scrollTo step'))});`;
574
+ return `await ${v}.${name}();`;
419
575
  }
420
576
  // No element: generic BaseScreen helpers.
421
577
  switch (s.action) {
422
578
  case 'tapAt':
423
- return `await ${screenVar}.tapAt(${s.coords?.[0] ?? 0}, ${s.coords?.[1] ?? 0}); // coordinate fallback — non-release-grade`;
424
- case 'inputText': {
425
- const arg = s.varName ? `testData.${varToKey(s.varName)}` : JSON.stringify(s.text ?? '');
426
- return `await ${screenVar}.type({ android: '' }, ${arg}); // focused-field input`;
427
- }
579
+ if (!s.coords)
580
+ throw new UnemittableStepError('cannot emit a coordinate tap without coordinates');
581
+ return `await ${v}.tapAt(${num(s.coords[0])}, ${num(s.coords[1])}); // coordinate fallback, non-release-grade`;
582
+ case 'inputText':
583
+ return `await ${v}.typeFocused(${dataArg(s)}); // focused-field input`;
428
584
  case 'press':
429
- return `await ${screenVar}.pressKey(${JSON.stringify(s.key ?? 'back')});`;
585
+ return `await ${v}.pressKey(${JSON.stringify(checkKey(s.key))});`;
430
586
  case 'swipe':
431
- return `await ${screenVar}.swipe(${JSON.stringify(s.direction ?? 'up')});`;
587
+ return `await ${v}.swipe(${JSON.stringify(checkDirection(s.direction ?? 'up', 'swipe step'))});`;
588
+ case 'scrollTo':
589
+ if (!s.text)
590
+ throw new UnemittableStepError('cannot emit a scrollTo step with neither an element nor a target text');
591
+ return `await ${v}.scrollToText(${JSON.stringify(s.text)}, ${JSON.stringify(checkDirection(s.direction ?? 'down', 'scrollTo step'))});`;
432
592
  case 'openUrl':
433
- return `await ${screenVar}.openUrl(${JSON.stringify(s.url ?? '')});`;
593
+ if (!s.url)
594
+ throw new UnemittableStepError('cannot emit an openUrl step without a URL');
595
+ return `await ${v}.openUrl(${JSON.stringify(s.url)});`;
434
596
  case 'assertVisible':
435
- return `await ${screenVar}.assertTextVisible(${JSON.stringify(s.text ?? '')});`;
597
+ if (!s.text)
598
+ throw new UnemittableStepError('cannot emit a text assertion without text');
599
+ return `await ${v}.assertTextVisible(${JSON.stringify(s.text)});`;
600
+ case 'visualCheck':
601
+ // Visual judgement prose is not on-screen text. It's a manual checkpoint, never a failing text check.
602
+ return `// TODO(manual visual check, not automated): ${commentSafe(s.text ?? 'visual checkpoint')}`;
436
603
  default:
437
- return `// unsupported step: ${s.action}`;
604
+ throw new UnemittableStepError(`cannot emit step action ${JSON.stringify(s.action)}: no WebdriverIO equivalent is generated for it`);
438
605
  }
439
606
  }
440
607
  //# sourceMappingURL=jsEmitter.js.map