@testspectra/cli 1.0.70 → 1.1.0-rc.1

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 (427) hide show
  1. package/LICENSE.md +48 -0
  2. package/README.md +36 -270
  3. package/bin/.testspectra-runner.hash +1 -0
  4. package/bin/spectra.js +1 -1
  5. package/bin/testspectra-runner +0 -0
  6. package/demo-app/assets/index-BhlW-eXG.css +1 -0
  7. package/demo-app/assets/index-DOtRypCa.js +154 -0
  8. package/demo-app/index.html +2 -2
  9. package/dist/.tsbuildinfo +1 -0
  10. package/dist/commands/__tests__/doctor.test.d.ts +1 -0
  11. package/dist/commands/__tests__/doctor.test.js +39 -0
  12. package/dist/commands/__tests__/init.test.d.ts +1 -0
  13. package/dist/commands/__tests__/init.test.js +148 -0
  14. package/dist/commands/__tests__/license.test.d.ts +1 -0
  15. package/dist/commands/__tests__/license.test.js +13 -0
  16. package/dist/commands/__tests__/refactor.test.d.ts +1 -0
  17. package/dist/commands/__tests__/refactor.test.js +64 -0
  18. package/dist/commands/__tests__/run-e2e.test.d.ts +1 -0
  19. package/dist/commands/__tests__/run-e2e.test.js +422 -0
  20. package/dist/commands/__tests__/run.test.d.ts +1 -0
  21. package/dist/commands/__tests__/run.test.js +273 -0
  22. package/dist/commands/__tests__/watch.test.d.ts +1 -0
  23. package/dist/commands/__tests__/watch.test.js +155 -0
  24. package/dist/commands/add.js +86 -85
  25. package/dist/commands/demo.js +21 -11
  26. package/dist/commands/devices.d.ts +14 -2
  27. package/dist/commands/devices.js +44 -34
  28. package/dist/commands/doctor.d.ts +3 -1
  29. package/dist/commands/doctor.js +43 -264
  30. package/dist/commands/init.d.ts +1 -1
  31. package/dist/commands/init.js +211 -196
  32. package/dist/commands/install.d.ts +4 -0
  33. package/dist/commands/install.js +59 -0
  34. package/dist/commands/license.d.ts +4 -0
  35. package/dist/commands/license.js +30 -0
  36. package/dist/commands/lint.d.ts +5 -0
  37. package/dist/commands/lint.js +55 -0
  38. package/dist/commands/refactor.d.ts +9 -0
  39. package/dist/commands/refactor.js +172 -0
  40. package/dist/commands/run.d.ts +23 -2
  41. package/dist/commands/run.js +322 -111
  42. package/dist/commands/skills.d.ts +9 -0
  43. package/dist/commands/skills.js +28 -0
  44. package/dist/commands/sync-types.d.ts +4 -0
  45. package/dist/commands/sync-types.js +15 -0
  46. package/dist/commands/test.d.ts +17 -0
  47. package/dist/commands/test.js +76 -0
  48. package/dist/commands/update.js +39 -43
  49. package/dist/commands/watch.d.ts +6 -0
  50. package/dist/commands/watch.js +55 -45
  51. package/dist/config/loader.d.ts +1 -1
  52. package/dist/config/loader.js +40 -25
  53. package/dist/config/schema.d.ts +104 -145
  54. package/dist/config/schema.js +36 -45
  55. package/dist/generator/__tests__/tsconfig-isolation.test.d.ts +1 -0
  56. package/dist/generator/__tests__/tsconfig-isolation.test.js +282 -0
  57. package/dist/generator/__tests__/type-generator.test.d.ts +1 -0
  58. package/dist/generator/__tests__/type-generator.test.js +386 -0
  59. package/dist/generator/index.d.ts +2 -0
  60. package/dist/generator/index.js +2 -0
  61. package/dist/generator/tsconfig-generator.d.ts +27 -0
  62. package/dist/generator/tsconfig-generator.js +419 -0
  63. package/dist/generator/type-generator.d.ts +59 -0
  64. package/dist/generator/type-generator.js +740 -0
  65. package/dist/index.d.ts +9 -5
  66. package/dist/index.js +165 -51
  67. package/dist/lifecycle/__tests__/lifecycle-engine.test.d.ts +1 -0
  68. package/dist/lifecycle/__tests__/lifecycle-engine.test.js +290 -0
  69. package/dist/lifecycle/hook-discovery.d.ts +24 -0
  70. package/dist/lifecycle/hook-discovery.js +60 -0
  71. package/dist/lifecycle/hook-dispatcher.d.ts +34 -0
  72. package/dist/lifecycle/hook-dispatcher.js +190 -0
  73. package/dist/lifecycle/index.d.ts +3 -0
  74. package/dist/lifecycle/index.js +3 -0
  75. package/dist/lifecycle/parallel-grouping.d.ts +27 -0
  76. package/dist/lifecycle/parallel-grouping.js +151 -0
  77. package/dist/linter/__tests__/anonymous-export.test.d.ts +1 -0
  78. package/dist/linter/__tests__/anonymous-export.test.js +184 -0
  79. package/dist/linter/__tests__/component-test-imports.test.d.ts +1 -0
  80. package/dist/linter/__tests__/component-test-imports.test.js +115 -0
  81. package/dist/linter/__tests__/shared-lib-boundary.test.d.ts +1 -0
  82. package/dist/linter/__tests__/shared-lib-boundary.test.js +270 -0
  83. package/dist/linter/__tests__/spec-schema.test.d.ts +1 -0
  84. package/dist/linter/__tests__/spec-schema.test.js +369 -0
  85. package/dist/linter/__tests__/step-parity.test.d.ts +1 -0
  86. package/dist/linter/__tests__/step-parity.test.js +372 -0
  87. package/dist/linter/discovery.d.ts +30 -0
  88. package/dist/linter/discovery.js +249 -0
  89. package/dist/linter/extractor.d.ts +67 -0
  90. package/dist/linter/extractor.js +245 -0
  91. package/dist/linter/index.d.ts +7 -0
  92. package/dist/linter/index.js +7 -0
  93. package/dist/linter/parser.d.ts +24 -0
  94. package/dist/linter/parser.js +219 -0
  95. package/dist/linter/schemas/spec.schema.d.ts +83 -0
  96. package/dist/linter/schemas/spec.schema.js +150 -0
  97. package/dist/linter/schemas/suite.schema.d.ts +59 -0
  98. package/dist/linter/schemas/suite.schema.js +83 -0
  99. package/dist/linter/spec-linter.d.ts +23 -0
  100. package/dist/linter/spec-linter.js +630 -0
  101. package/dist/linter/types.d.ts +28 -0
  102. package/dist/linter/types.js +1 -0
  103. package/dist/mobile/__tests__/android-device-e2e.test.d.ts +1 -0
  104. package/dist/mobile/__tests__/android-device-e2e.test.js +53 -0
  105. package/dist/mobile/__tests__/device-pool.test.d.ts +1 -0
  106. package/dist/mobile/__tests__/device-pool.test.js +97 -0
  107. package/dist/mobile/device-pool.d.ts +23 -0
  108. package/dist/mobile/device-pool.js +1 -0
  109. package/dist/mobile/index.d.ts +1 -0
  110. package/dist/mobile/index.js +1 -0
  111. package/dist/refactor/__tests__/refactor-engine.test.d.ts +1 -0
  112. package/dist/refactor/__tests__/refactor-engine.test.js +873 -0
  113. package/dist/refactor/index.d.ts +1 -0
  114. package/dist/refactor/index.js +1 -0
  115. package/dist/refactor/refactor-engine.d.ts +50 -0
  116. package/dist/refactor/refactor-engine.js +587 -0
  117. package/dist/reporter/__tests__/semantic-formatter.test.d.ts +1 -0
  118. package/dist/reporter/__tests__/semantic-formatter.test.js +73 -0
  119. package/dist/reporter/semantic-formatter.d.ts +6 -0
  120. package/dist/reporter/semantic-formatter.js +206 -0
  121. package/dist/reporter/summary-view.d.ts +12 -0
  122. package/dist/reporter/summary-view.js +19 -0
  123. package/dist/reporter/terminal-stream.d.ts +32 -0
  124. package/dist/reporter/terminal-stream.js +82 -0
  125. package/dist/reporter/types.d.ts +2 -0
  126. package/dist/reporter/types.js +1 -0
  127. package/dist/runner/bridge.d.ts +10 -5
  128. package/dist/runner/bridge.js +101 -45
  129. package/dist/runner/reporter.d.ts +32 -2
  130. package/dist/runner/reporter.js +427 -13
  131. package/dist/server/demo.js +43 -44
  132. package/dist/types/generator.d.ts +1 -17
  133. package/dist/types/generator.js +1 -443
  134. package/dist/utils/__tests__/api-server.test.d.ts +1 -0
  135. package/dist/utils/__tests__/api-server.test.js +62 -0
  136. package/dist/utils/__tests__/demo-server.test.d.ts +1 -0
  137. package/dist/utils/__tests__/demo-server.test.js +35 -0
  138. package/dist/utils/api-server.d.ts +19 -0
  139. package/dist/utils/api-server.js +179 -0
  140. package/dist/utils/demo-server.d.ts +5 -0
  141. package/dist/utils/demo-server.js +46 -36
  142. package/package.json +25 -13
  143. package/templates/default/CLAUDE.md +12 -5
  144. package/templates/default/fixtures/apiData.json +24 -0
  145. package/templates/default/package.json +12 -3
  146. package/templates/default/page-objects/AuthPage/mobile.ts +5 -0
  147. package/templates/default/page-objects/AuthPage/web.ts +10 -0
  148. package/templates/default/page-objects/MatchersPage/mobile.ts +66 -17
  149. package/templates/default/page-objects/MatchersPage/web.ts +22 -2
  150. package/templates/default/page-objects/NavigationPage/mobile.ts +11 -2
  151. package/templates/default/page-objects/NavigationPage/web.ts +1 -1
  152. package/templates/default/page-objects/PlaygroundPage/mobile.ts +34 -18
  153. package/templates/default/page-objects/PlaygroundPage/web.ts +4 -2
  154. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -0
  155. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -0
  156. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -0
  157. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -0
  158. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -0
  159. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -0
  160. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -0
  161. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -0
  162. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -0
  163. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -0
  164. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -0
  165. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -0
  166. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -0
  167. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -0
  168. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -0
  169. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -0
  170. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -0
  171. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -0
  172. package/templates/default/specs/ApiInterception/suite.md +10 -0
  173. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -0
  174. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -0
  175. package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -0
  176. package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -0
  177. package/templates/default/specs/ApiSeeding/suite.md +9 -0
  178. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -0
  179. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -0
  180. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -0
  181. package/templates/default/specs/Authentication/suite.md +9 -0
  182. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +7 -1
  183. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +5 -0
  184. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +17 -3
  185. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -0
  186. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -0
  187. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -0
  188. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -0
  189. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +7 -0
  190. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +9 -1
  191. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +5 -0
  192. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +9 -4
  193. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -0
  194. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -0
  195. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -0
  196. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -0
  197. package/templates/default/specs/DeepLink/suite.md +9 -0
  198. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +9 -1
  199. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +5 -0
  200. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +9 -3
  201. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +27 -1
  202. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +7 -0
  203. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +27 -6
  204. package/templates/default/specs/ElementMatchers/suite.md +2 -1
  205. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
  206. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
  207. package/templates/default/specs/EnvironmentConfig/suite.md +9 -0
  208. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -0
  209. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -0
  210. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -0
  211. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -0
  212. package/templates/default/specs/PermissionRationale/suite.md +9 -0
  213. package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +4 -1
  214. package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +6 -0
  215. package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +4 -4
  216. package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +4 -1
  217. package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +6 -0
  218. package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +3 -3
  219. package/templates/default/spectra.config.ts +22 -31
  220. package/templates/default/support/actions/fillCredentials/mobile.action.ts +3 -3
  221. package/templates/default/support/actions/fillCredentials/web.action.ts +1 -1
  222. package/templates/default/support/actions/seedSession/mobile.action.ts +9 -0
  223. package/templates/default/support/actions/seedSession/web.action.ts +13 -0
  224. package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +3 -1
  225. package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +3 -3
  226. package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +4 -2
  227. package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +4 -4
  228. package/templates/default/tsconfig.json +3 -1
  229. package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db +0 -0
  230. package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-shm +0 -0
  231. package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal +0 -0
  232. package/templates/nx/.nx/workspace-data/d/daemon.log +5366 -0
  233. package/templates/nx/.nx/workspace-data/d/server-process.json +3 -0
  234. package/templates/nx/.nx/workspace-data/file-map.json +464 -0
  235. package/templates/nx/.nx/workspace-data/lockfile.hash +1 -0
  236. package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
  237. package/templates/nx/.nx/workspace-data/parsed-lock-file.json +13485 -0
  238. package/templates/nx/.nx/workspace-data/project-graph.json +14389 -0
  239. package/templates/nx/CLAUDE.md +2 -2
  240. package/templates/nx/package.json +6 -4
  241. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -0
  242. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +12 -0
  243. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -0
  244. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -0
  245. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/spec.md +12 -0
  246. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -0
  247. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/suite.md +9 -0
  248. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +1 -1
  249. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +17 -3
  250. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +1 -1
  251. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +15 -5
  252. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +1 -1
  253. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +9 -4
  254. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +1 -1
  255. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +9 -3
  256. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +1 -1
  257. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +27 -6
  258. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
  259. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
  260. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -0
  261. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +1 -1
  262. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +4 -4
  263. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +1 -1
  264. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +3 -3
  265. package/templates/nx/pnpm-lock.yaml +6663 -0
  266. package/templates/nx/pnpm-workspace.yaml +2 -1
  267. package/templates/nx/shared/testing/fixtures/apiData.json +24 -0
  268. package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +13 -1
  269. package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +85 -17
  270. package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +1 -1
  271. package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +1 -1
  272. package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +1 -1
  273. package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +4 -2
  274. package/templates/nx/shared/testing/project.json +19 -0
  275. package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +1 -1
  276. package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +1 -1
  277. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +1 -1
  278. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +3 -3
  279. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +2 -2
  280. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +4 -4
  281. package/templates/nx/spectra.config.ts +20 -29
  282. package/templates/nx/tsconfig.json +3 -1
  283. package/templates/react-component/.cursorrules +17 -0
  284. package/templates/react-component/.vscode/extensions.json +5 -0
  285. package/templates/react-component/CLAUDE.md +14 -0
  286. package/templates/react-component/README.md +69 -0
  287. package/templates/react-component/fixtures/component-mock.json +12 -0
  288. package/templates/react-component/global-hooks/after/web.hook.ts +3 -0
  289. package/templates/react-component/global-hooks/before/web.hook.ts +3 -0
  290. package/templates/react-component/package.json +27 -0
  291. package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -0
  292. package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -0
  293. package/templates/react-component/page-objects/InputComponent/web.ts +13 -0
  294. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -0
  295. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -0
  296. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -0
  297. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -0
  298. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -0
  299. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -0
  300. package/templates/react-component/specs/BadgeComponent/suite.md +12 -0
  301. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -0
  302. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -0
  303. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -0
  304. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -0
  305. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -0
  306. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -0
  307. package/templates/react-component/specs/ButtonComponent/suite.md +12 -0
  308. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -0
  309. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -0
  310. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -0
  311. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -0
  312. package/templates/react-component/specs/InputComponent/suite.md +12 -0
  313. package/templates/react-component/spectra.config.ts +27 -0
  314. package/templates/react-component/src/components/Badge/Badge.tsx +60 -0
  315. package/templates/react-component/src/components/Button/Button.tsx +57 -0
  316. package/templates/react-component/src/components/Input/Input.tsx +41 -0
  317. package/templates/react-component/src/test-utils.tsx +23 -0
  318. package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -0
  319. package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -0
  320. package/templates/react-component/tsconfig.json +8 -0
  321. package/demo-app/assets/index-DAUlrHTr.css +0 -1
  322. package/demo-app/assets/index-DN2Kb0A8.js +0 -100
  323. package/dist/index.cjs +0 -101
  324. package/dist/plugin.cjs +0 -575
  325. package/dist/plugin.d.ts +0 -16
  326. package/dist/plugin.js +0 -184
  327. package/dist/step/index.d.ts +0 -6
  328. package/dist/step/index.js +0 -6
  329. package/dist/step/matchers.d.ts +0 -14
  330. package/dist/step/matchers.js +0 -114
  331. package/dist/step/proto.d.ts +0 -46
  332. package/dist/step/proto.js +0 -114
  333. package/dist/step/runner/collection.d.ts +0 -13
  334. package/dist/step/runner/collection.js +0 -51
  335. package/dist/step/runner/single.d.ts +0 -21
  336. package/dist/step/runner/single.js +0 -138
  337. package/dist/step/spectra.d.ts +0 -34
  338. package/dist/step/spectra.js +0 -156
  339. package/dist/step/types.d.ts +0 -32
  340. package/dist/step/types.js +0 -5
  341. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -3
  342. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -7
  343. package/templates/default/tsconfig.spectra.android.json +0 -46
  344. package/templates/default/tsconfig.spectra.ios.json +0 -46
  345. package/templates/default/tsconfig.spectra.json +0 -22
  346. package/templates/default/tsconfig.spectra.web.json +0 -53
  347. package/templates/nx/fixtures/playgroundData.json +0 -7
  348. package/templates/nx/global-hooks/after/mobile.hook.ts +0 -3
  349. package/templates/nx/global-hooks/after/web.hook.ts +0 -3
  350. package/templates/nx/global-hooks/before/mobile.hook.ts +0 -3
  351. package/templates/nx/global-hooks/before/web.hook.ts +0 -3
  352. package/templates/nx/page-objects/MatchersPage/mobile.ts +0 -21
  353. package/templates/nx/page-objects/MatchersPage/web.ts +0 -23
  354. package/templates/nx/page-objects/NavigationPage/mobile.ts +0 -5
  355. package/templates/nx/page-objects/NavigationPage/web.ts +0 -8
  356. package/templates/nx/page-objects/PlaygroundPage/mobile.ts +0 -26
  357. package/templates/nx/page-objects/PlaygroundPage/web.ts +0 -29
  358. package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +0 -3
  359. package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +0 -12
  360. package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +0 -5
  361. package/templates/nx/specs/BrowserContext/suite.md +0 -9
  362. package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -3
  363. package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/spec.md +0 -12
  364. package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -7
  365. package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +0 -3
  366. package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +0 -12
  367. package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +0 -6
  368. package/templates/nx/specs/ContentMatchers/suite.md +0 -9
  369. package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +0 -3
  370. package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +0 -12
  371. package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +0 -5
  372. package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +0 -3
  373. package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +0 -12
  374. package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +0 -8
  375. package/templates/nx/specs/ElementMatchers/hooks/after/mobile.hook.ts +0 -1
  376. package/templates/nx/specs/ElementMatchers/hooks/after/web.hook.ts +0 -1
  377. package/templates/nx/specs/ElementMatchers/hooks/afterEach/mobile.hook.ts +0 -1
  378. package/templates/nx/specs/ElementMatchers/hooks/afterEach/web.hook.ts +0 -1
  379. package/templates/nx/specs/ElementMatchers/hooks/before/mobile.hook.ts +0 -1
  380. package/templates/nx/specs/ElementMatchers/hooks/before/web.hook.ts +0 -1
  381. package/templates/nx/specs/ElementMatchers/hooks/beforeEach/mobile.hook.ts +0 -1
  382. package/templates/nx/specs/ElementMatchers/hooks/beforeEach/web.hook.ts +0 -1
  383. package/templates/nx/specs/ElementMatchers/suite.md +0 -9
  384. package/templates/nx/specs/Playground/TC-0001-form-controls/mobile.test.ts +0 -3
  385. package/templates/nx/specs/Playground/TC-0001-form-controls/spec.md +0 -12
  386. package/templates/nx/specs/Playground/TC-0001-form-controls/web.test.ts +0 -6
  387. package/templates/nx/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +0 -3
  388. package/templates/nx/specs/Playground/TC-0002-toggle-elements/spec.md +0 -12
  389. package/templates/nx/specs/Playground/TC-0002-toggle-elements/web.test.ts +0 -6
  390. package/templates/nx/specs/Playground/hooks/after/mobile.hook.ts +0 -1
  391. package/templates/nx/specs/Playground/hooks/after/web.hook.ts +0 -1
  392. package/templates/nx/specs/Playground/hooks/afterEach/mobile.hook.ts +0 -1
  393. package/templates/nx/specs/Playground/hooks/afterEach/web.hook.ts +0 -1
  394. package/templates/nx/specs/Playground/hooks/before/mobile.hook.ts +0 -1
  395. package/templates/nx/specs/Playground/hooks/before/web.hook.ts +0 -1
  396. package/templates/nx/specs/Playground/hooks/beforeEach/mobile.hook.ts +0 -1
  397. package/templates/nx/specs/Playground/hooks/beforeEach/web.hook.ts +0 -1
  398. package/templates/nx/specs/Playground/suite.md +0 -9
  399. package/templates/nx/support/actions/fillCredentials/mobile.action.ts +0 -4
  400. package/templates/nx/support/actions/fillCredentials/web.action.ts +0 -4
  401. package/templates/nx/support/steps/togglePlaygroundStates/mobile.step.ts +0 -3
  402. package/templates/nx/support/steps/togglePlaygroundStates/step.md +0 -7
  403. package/templates/nx/support/steps/togglePlaygroundStates/web.step.ts +0 -5
  404. package/templates/nx/support/steps/verifyPlaygroundState/mobile.step.ts +0 -4
  405. package/templates/nx/support/steps/verifyPlaygroundState/step.md +0 -7
  406. package/templates/nx/support/steps/verifyPlaygroundState/web.step.ts +0 -6
  407. package/templates/nx/tsconfig.spectra.android.json +0 -39
  408. package/templates/nx/tsconfig.spectra.ios.json +0 -39
  409. package/templates/nx/tsconfig.spectra.json +0 -25
  410. package/templates/nx/tsconfig.spectra.shared.json +0 -25
  411. package/templates/nx/tsconfig.spectra.web.json +0 -45
  412. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/after/mobile.hook.ts +0 -0
  413. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/after/web.hook.ts +0 -0
  414. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/afterEach/mobile.hook.ts +0 -0
  415. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/afterEach/web.hook.ts +0 -0
  416. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/before/mobile.hook.ts +0 -0
  417. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/before/web.hook.ts +0 -0
  418. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/beforeEach/mobile.hook.ts +0 -0
  419. /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/beforeEach/web.hook.ts +0 -0
  420. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/after/mobile.hook.ts +0 -0
  421. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/after/web.hook.ts +0 -0
  422. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/afterEach/mobile.hook.ts +0 -0
  423. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/afterEach/web.hook.ts +0 -0
  424. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/before/mobile.hook.ts +0 -0
  425. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/before/web.hook.ts +0 -0
  426. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/beforeEach/mobile.hook.ts +0 -0
  427. /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/beforeEach/web.hook.ts +0 -0
@@ -0,0 +1,28 @@
1
+ // Suite-level `before` hook — runs once before this suite's tests. Hooks run in the same worker
2
+ // process/global scope as the test files, so data seeded here is handed to tests via a plain
3
+ // `globalThis` namespace rather than a return value (hooks don't have a return channel).
4
+ //
5
+ // Seeds against the demo server's own self-hosted API (started with `--demo`) instead of an
6
+ // external public API, so the round trip is deterministic and offline-safe — same seed-in-
7
+ // `before`, clean-up-in-`after` hook mechanics, with a REAL network round trip, just against a
8
+ // server we control. Because the hook runs on the HOST (not the device), this suite needs the
9
+ // demo server up on the host: run it with `--demo` (e.g. `spectra run ... -t android --demo`).
10
+ // Swap the base URL for a real backend's seeding endpoint in an actual project.
11
+ export default async function () {
12
+ const res = await fetch("http://localhost:5200/api/posts", {
13
+ method: "POST",
14
+ headers: { "Content-Type": "application/json; charset=UTF-8" },
15
+ body: JSON.stringify({
16
+ title: "TestSpectra seeded post",
17
+ body: "Created by ApiSeeding's before hook for TC-0014.",
18
+ userId: 1,
19
+ }),
20
+ });
21
+
22
+ if (!res.ok) {
23
+ throw new Error(`ApiSeeding before hook: failed to seed post (${res.status} ${res.statusText})`);
24
+ }
25
+
26
+ const seeded = await res.json();
27
+ (globalThis as any).__apiSeeding = { seededPost: seeded, cleanedUp: false };
28
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: suite-api-seeding
3
+ name: ApiSeeding
4
+ description: Seeding and cleaning up test data via a real free public API in before/after hooks
5
+ executionOrder: 2.5
6
+ parallel: true
7
+ ---
8
+
9
+ # Suite: ApiSeeding
@@ -0,0 +1,7 @@
1
+ it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
2
+ // The deep link asks the app to log itself in against a known test account and persist the
3
+ // result itself — no login form was ever touched.
4
+ await Spectra.seedSession();
5
+
6
+ await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
7
+ });
@@ -0,0 +1,19 @@
1
+ ---
2
+ id: TC-0021-seed-authenticated-session
3
+ title: Seed an authenticated session via Spectra.seedSession() instead of the real login UI
4
+ priority: High
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: automated
9
+ mobile: automated
10
+ ---
11
+
12
+ # Seed an authenticated session via Spectra.seedSession() instead of the real login UI
13
+
14
+ ## Test Steps
15
+ 1. Call the `seedSession` support action — logs in via a direct API call instead of walking
16
+ through a login form (web: host-side `fetch()` + `Spectra.browser.setCookies()` applies the
17
+ resulting `HttpOnly` cookie to the browser; mobile: a deep link asks the app to log itself in
18
+ against a known test account and persist the result itself)
19
+ 2. Assert the app reflects an authenticated state
@@ -0,0 +1,9 @@
1
+ it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
2
+ // Logs in via a direct host-side API call and applies the resulting HttpOnly cookie straight
3
+ // to the browser — no login form was ever touched.
4
+ await Spectra.seedSession();
5
+
6
+ await AuthPage.open();
7
+ await AuthPage.checkStatusButton.click();
8
+ await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
9
+ });
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: suite-authentication
3
+ name: Authentication
4
+ description: Skipping the real login UI by seeding an authenticated session via a direct API call
5
+ executionOrder: 2.6
6
+ parallel: true
7
+ ---
8
+
9
+ # Suite: Authentication
@@ -1,3 +1,9 @@
1
- it("should verify title on Mobile", async () => {
1
+ it("Verify page title and sidebar navigation links", async () => {
2
2
  await MatchersPage.open();
3
+
4
+ // Navigation sidebar collection verification
5
+ await NavigationPage.sidebarLinks.shouldHaveLengthGreaterThan(0);
6
+
7
+ // Close sidebar drawer
8
+ await NavigationPage.closeSidebarButton.click();
3
9
  });
@@ -10,3 +10,8 @@ coverage:
10
10
  ---
11
11
 
12
12
  # Verify page title and sidebar navigation links
13
+
14
+ ## Test Steps
15
+ 1. Open matchers page
16
+ 2. Verify browser page title contains TestSpectra
17
+ 3. Verify sidebar navigation links length is greater than 0
@@ -1,5 +1,19 @@
1
- it("should verify browser page title and navigation links on Web", async () => {
1
+ it("Verify page title and sidebar navigation links", async () => {
2
2
  await MatchersPage.open();
3
- await browser.shouldContainTitle("TestSpectra");
4
- await NavigationPage.sidebarLinks.should("have.length.greaterThan", 0);
3
+
4
+ // Browser title assertions
5
+ await Spectra.browser.shouldHaveTitle("TestSpectra — Matchers API Reference");
6
+ await Spectra.browser.shouldContainTitle("TestSpectra");
7
+
8
+ // Browser URL assertions
9
+ const currentUrl = await browser.getUrl();
10
+ await Spectra.browser.shouldHaveUrl(currentUrl);
11
+ await Spectra.browser.shouldContainUrl("localhost:5173");
12
+
13
+ // Page lifecycle and console error diagnostics
14
+ await Spectra.browser.shouldBeLoaded();
15
+ await Spectra.browser.shouldHaveNoConsoleErrors();
16
+
17
+ // Navigation sidebar collection verification
18
+ await NavigationPage.sidebarLinks.shouldHaveLengthGreaterThan(0);
5
19
  });
@@ -0,0 +1,17 @@
1
+ it("Navigate browser history with Spectra.back(), forward(), and refresh()", async () => {
2
+ // Same cold-start race as DeepLink/TC-0018 — wait for the home screen before firing the deep
3
+ // link so it isn't racing the app's own still-in-progress initial boot.
4
+ await Spectra.get("~toggle-sidebar-btn").waitForElement(30000);
5
+
6
+ await Spectra.navigate("testspectra-demo://deeplink-target");
7
+ await Spectra.get("~deeplink-target-marker").waitForElement(10000);
8
+
9
+ // Spectra.back() on Android presses the hardware back key — distinct from tapping the screen's
10
+ // own "‹ Back" button (already covered by DeepLink/TC-0018) — and should pop the nav stack back
11
+ // to Home exactly the same way.
12
+ await Spectra.back();
13
+ await Spectra.get("~toggle-sidebar-btn").waitForElement(10000);
14
+
15
+ // Spectra.forward()/Spectra.refresh() have no native-app equivalent (no browser history, no
16
+ // page reload) — they're intentional no-ops on Android, so there's nothing to assert here.
17
+ });
@@ -0,0 +1,28 @@
1
+ ---
2
+ id: TC-0022-history-navigation
3
+ title: Navigate browser/app history with Spectra.back(), forward(), and refresh()
4
+ priority: Medium
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: automated
9
+ mobile: automated
10
+ ---
11
+
12
+ # Navigate browser/app history with Spectra.back(), forward(), and refresh()
13
+
14
+ ## Test Steps
15
+
16
+ ### Web
17
+ 1. Navigate to two distinct real URLs in sequence, each pushing its own genuine browser history
18
+ entry (this demo app's in-app hash navigation does not — see the test's own comment)
19
+ 2. Verify `Spectra.back()` returns to the previously visited URL
20
+ 3. Verify `Spectra.forward()` replays the entry just undone
21
+ 4. Verify `Spectra.refresh()` reloads the document while the current URL survives
22
+
23
+ ### Mobile
24
+ 1. Navigate to a native route via deep link
25
+ 2. Verify `Spectra.back()` (Android hardware back key) pops back to the Home screen
26
+
27
+ `Spectra.forward()`/`Spectra.refresh()` have no native-app equivalent (no browser history, no page
28
+ reload) and are intentional no-ops on Android — only `Spectra.back()` is exercised on mobile.
@@ -0,0 +1,26 @@
1
+ it("Navigate browser history with Spectra.back(), forward(), and refresh()", async () => {
2
+ await MatchersPage.open();
3
+
4
+ // This demo app is single-page — its in-app "navigation" (Spectra.navigate("/#section")) only
5
+ // ever sets window.location.hash, which this browser does not push as a traversable history
6
+ // entry (history.length never grows), so there is nothing for back()/forward() to jump between.
7
+ // Two distinct real paths give them something real: Vite's dev server serves the same SPA shell
8
+ // for any path, but the browser still treats each as its own document and pushes a genuine
9
+ // history entry — exactly like a real multi-page site would.
10
+ await Spectra.navigate("/history-nav-a");
11
+ await Spectra.browser.shouldContainUrl("/history-nav-a");
12
+
13
+ await Spectra.navigate("/history-nav-b");
14
+ await Spectra.browser.shouldContainUrl("/history-nav-b");
15
+
16
+ await Spectra.back();
17
+ await Spectra.browser.shouldContainUrl("/history-nav-a");
18
+
19
+ await Spectra.forward();
20
+ await Spectra.browser.shouldContainUrl("/history-nav-b");
21
+
22
+ // The document reloads, but the current URL survives it.
23
+ await Spectra.refresh();
24
+ await Spectra.browser.shouldBeLoaded();
25
+ await Spectra.browser.shouldContainUrl("/history-nav-b");
26
+ });
@@ -0,0 +1,26 @@
1
+ // A single common.test.ts, not a web.test.ts/mobile.test.ts pair — the orchestrator's test-case
2
+ // discovery recognizes "common" as a platform stem for every target (web, android, ios, mobile),
3
+ // so this one file runs unmodified on all of them. This works here because every Spectra.*
4
+ // command below is genuinely platform-agnostic (the DSL layer has zero platform branching by
5
+ // design) and MatchersPage's selectors are already abstracted per-platform behind identically-
6
+ // named getters — nothing in this specific test depends on a page origin, a native-only gesture,
7
+ // or a browser-chrome-only assertion (see TC-0013's real-network test for a case that CAN'T be
8
+ // shared this way: Android has no implicit page origin to resolve a relative fetch URL against,
9
+ // so the URL itself has to differ per platform even though the commands stay identical).
10
+ it("Verify exact text, substring text, and input values", async () => {
11
+ await MatchersPage.open("text");
12
+
13
+ // Text assertions (positive and negative)
14
+ await MatchersPage.exactText.shouldHaveText(Fixture.playgroundData.welcomeText);
15
+ await MatchersPage.exactText.shouldNotHaveText("Unexpected Text");
16
+
17
+ await MatchersPage.partialText.shouldContainText("Welcome");
18
+ await MatchersPage.partialText.shouldNotContainText("Goodbye");
19
+
20
+ // Value assertions (positive and negative)
21
+ await MatchersPage.emailInput.shouldHaveValue(Fixture.playgroundData.defaultEmail);
22
+ await MatchersPage.emailInput.shouldNotHaveValue("wrong.user@testspectra.dev");
23
+
24
+ await MatchersPage.tokenInput.shouldContainValue("JWT_");
25
+ await MatchersPage.tokenInput.shouldNotContainValue("EXPIRED_SESSION");
26
+ });
@@ -10,3 +10,10 @@ coverage:
10
10
  ---
11
11
 
12
12
  # Verify exact text, substring text, and input values
13
+
14
+ ## Test Steps
15
+ 1. Open text matchers section in matchers page
16
+ 2. Verify exact text matches playground welcome text
17
+ 3. Verify partial text contains welcome keyword
18
+ 4. Verify email input value matches default email
19
+ 5. Verify token input value contains JWT prefix
@@ -1,3 +1,11 @@
1
- it("should verify collection length on Mobile", async () => {
1
+ it("Verify collection lengths, counts, and empty lists", async () => {
2
2
  await MatchersPage.open("length");
3
+
4
+ // Receiver assertion methods
5
+ await MatchersPage.productCards.shouldHaveLength(4);
6
+ await MatchersPage.productCards.shouldNotHaveLength(0);
7
+ await MatchersPage.productCards.shouldHaveLengthGreaterThan(0);
8
+ await MatchersPage.productCards.shouldHaveLengthLessThan(10);
9
+ await MatchersPage.emptyList.shouldBeEmpty();
10
+ await MatchersPage.productCards.shouldNotBeEmpty();
3
11
  });
@@ -10,3 +10,8 @@ coverage:
10
10
  ---
11
11
 
12
12
  # Verify collection lengths, counts, and empty lists
13
+
14
+ ## Test Steps
15
+ 1. Open collection length section in matchers page
16
+ 2. Assert product cards count equals 4 and is greater than 0
17
+ 3. Assert empty list container is empty
@@ -1,6 +1,11 @@
1
- it("should verify collection lengths and empty lists on Web", async () => {
1
+ it("Verify collection lengths, counts, and empty lists", async () => {
2
2
  await MatchersPage.open("length");
3
- await MatchersPage.productCards.should("have.length", 4);
4
- await MatchersPage.productCards.should("have.length.greaterThan", 0);
5
- await MatchersPage.emptyList.should("be.empty");
3
+
4
+ // Receiver assertion methods
5
+ await MatchersPage.productCards.shouldHaveLength(4);
6
+ await MatchersPage.productCards.shouldNotHaveLength(0);
7
+ await MatchersPage.productCards.shouldHaveLengthGreaterThan(0);
8
+ await MatchersPage.productCards.shouldHaveLengthLessThan(10);
9
+ await MatchersPage.emptyList.shouldBeEmpty();
10
+ await MatchersPage.productCards.shouldNotBeEmpty();
6
11
  });
@@ -0,0 +1,14 @@
1
+ it("element.get()/getAll() resolves a child scoped to its parent's own subtree", async () => {
2
+ await MatchersPage.open();
3
+ await MatchersPage.overviewSection.shouldBeVisible();
4
+
5
+ // Positive: the subtitle is a real descendant of the overview section, so the scoped lookup
6
+ // resolves it.
7
+ await MatchersPage.overviewSubtitle().shouldExist();
8
+
9
+ // Negative (the actual scoping proof): the submit button lives in a different section
10
+ // entirely. If .get() weren't genuinely scoped (e.g. silently fell back to a page-wide
11
+ // lookup), this would incorrectly resolve to the globally-existing element instead of
12
+ // correctly not finding it.
13
+ await MatchersPage.overviewOutsideElement().shouldNotExist();
14
+ });
@@ -0,0 +1,21 @@
1
+ ---
2
+ id: TC-0020-scoped-element-access
3
+ title: element.get()/getAll() resolves a child scoped to its parent's own subtree
4
+ priority: High
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: automated
9
+ mobile: automated
10
+ ---
11
+
12
+ # element.get()/getAll() resolves a child scoped to its parent's own subtree
13
+
14
+ ## Test Steps
15
+ 1. Open the matchers page and assert the overview section is visible
16
+ 2. From the overview section, resolve the page subtitle via `.get()` and assert it exists — the
17
+ subtitle is a real descendant, so the scoped lookup finds it
18
+ 3. From the same overview section, resolve the submit button via `.get()` and assert it does NOT
19
+ exist — the submit button lives in a different section entirely, so this only passes if the
20
+ lookup is genuinely scoped to overview's own subtree rather than silently falling back to a
21
+ page-wide match
@@ -0,0 +1,28 @@
1
+ it("Navigate directly to a native route via Spectra.navigate() and an Android deep link", async () => {
2
+ // The orchestrator's own bootstrap launches the app (fire-and-forget, ~1.5s wait) before any
3
+ // test code runs at all — other tests never notice because their first action is a click()
4
+ // with its own generous poll budget, which absorbs whatever's left of the cold start. This
5
+ // test's first action is Spectra.navigate() itself, which is also fire-and-forget and has no
6
+ // such budget, so without this it races the deep-link intent against the app's own still-in-
7
+ // progress initial boot. Waiting for the home screen to actually be up first avoids that race.
8
+ await Spectra.get("~toggle-sidebar-btn").waitForElement(30000);
9
+
10
+ // No prior navigation, no button clicks — this jumps straight to the route via
11
+ // `adb shell am start`, exactly like opening the app from a push notification or an
12
+ // external link would.
13
+ await Spectra.navigate("testspectra-demo://deeplink-target");
14
+ await Spectra.get("~deeplink-target-marker").waitForElement(10000);
15
+
16
+ await Spectra.get("~deeplink-target-marker").shouldHaveText("deeplink-target");
17
+ await Spectra.get("~deeplink-received-message").shouldHaveText("(none)");
18
+
19
+ await Spectra.get("~back-to-home-btn").click();
20
+
21
+ // Deep links can carry query params — assert they survive the intent all the way to the screen.
22
+ await Spectra.navigate("testspectra-demo://deeplink-target?message=hello-from-deep-link");
23
+
24
+ await Spectra.get("~deeplink-received-message").waitForElement(10000);
25
+ await Spectra.get("~deeplink-received-message").shouldHaveText("hello-from-deep-link");
26
+
27
+ await Spectra.get("~back-to-home-btn").click();
28
+ });
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: TC-0018-navigate-via-deep-link
3
+ title: Navigate directly to a native route via Spectra.navigate() and an Android deep link
4
+ priority: High
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: unsupported
9
+ mobile: automated
10
+ ---
11
+
12
+ # Navigate directly to a native route via Spectra.navigate() and an Android deep link
13
+
14
+ On Android, `Spectra.navigate(uri)` runs `adb shell am start -a android.intent.action.VIEW -d
15
+ <uri>` against the app's own registered scheme (`testspectra-demo://`, declared in
16
+ `app.json`/`AndroidManifest.xml`) — jumping straight to a route without walking through the UI
17
+ first, the same way a push notification or an external link would open the app in production.
18
+
19
+ Web coverage is `unsupported`: on web, `Spectra.navigate()` already does the equivalent thing
20
+ via a normal URL (see any other TC's `browser.url(...)`/`Spectra.navigate(...)` call) — there's no
21
+ separate "deep link" concept to demonstrate there.
22
+
23
+ ## Test Steps
24
+ 1. Wait for the app home screen to finish booting
25
+ 2. Navigate via a deep link to the target route without query params
26
+ 3. Assert the target marker is shown and the received message is "(none)"
27
+ 4. Navigate via a deep link carrying a query-param message
28
+ 5. Assert the received message matches the deep-link param
29
+ 6. Return to the home screen
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: suite-deep-link
3
+ name: DeepLink
4
+ description: Navigating directly to a native route via Spectra.navigate() and an adb deep link
5
+ executionOrder: 2.2
6
+ parallel: true
7
+ ---
8
+
9
+ # Suite: DeepLink
@@ -1,3 +1,11 @@
1
- it("should verify visibility on Mobile", async () => {
1
+ it("Verify element visibility and existence matchers", async () => {
2
2
  await MatchersPage.open("visibility");
3
+
4
+ // Element visibility (positive and negative)
5
+ await MatchersPage.visibleElement.shouldBeVisible();
6
+ await MatchersPage.hiddenElement.shouldNotBeVisible();
7
+
8
+ // DOM existence (positive and negative)
9
+ await MatchersPage.existingElement.shouldExist();
10
+ await Spectra.get("~non-existent-element").shouldNotExist();
3
11
  });
@@ -10,3 +10,8 @@ coverage:
10
10
  ---
11
11
 
12
12
  # Verify element visibility and existence matchers
13
+
14
+ ## Test Steps
15
+ 1. Open visibility section in matchers page
16
+ 2. Assert visible element has be.visible state
17
+ 3. Assert existing element exists in DOM
@@ -1,5 +1,11 @@
1
- it("should verify be.visible and exist matchers on Web", async () => {
1
+ it("Verify element visibility and existence matchers", async () => {
2
2
  await MatchersPage.open("visibility");
3
- await MatchersPage.visibleElement.should("be.visible");
4
- await MatchersPage.existingElement.should("exist");
3
+
4
+ // Element visibility (positive and negative)
5
+ await MatchersPage.visibleElement.shouldBeVisible();
6
+ await MatchersPage.hiddenElement.shouldNotBeVisible();
7
+
8
+ // DOM existence (positive and negative)
9
+ await MatchersPage.existingElement.shouldExist();
10
+ await Spectra.get("#demo-non-existent-el").shouldNotExist();
5
11
  });
@@ -1,3 +1,29 @@
1
- it("should verify button state on Mobile", async () => {
1
+ it("Verify button state, checkbox, radio and attributes", async () => {
2
2
  await MatchersPage.open("state");
3
+
4
+ // Interactivity & state assertions
5
+ await MatchersPage.submitButton.shouldBeEnabled();
6
+ await MatchersPage.submitButton.shouldBeClickable();
7
+
8
+ await MatchersPage.disabledButton.shouldBeDisabled();
9
+ await MatchersPage.disabledButton.shouldNotBeClickable();
10
+
11
+ // Checked states
12
+ await MatchersPage.checkbox.shouldNotBeChecked();
13
+ await MatchersPage.radio.shouldBeChecked();
14
+
15
+ // Focus states
16
+ await MatchersPage.focusInput.shouldNotBeFocused();
17
+ await MatchersPage.focusInput.click();
18
+ await MatchersPage.focusInput.shouldBeFocused();
19
+
20
+ // Class, Attribute, and CSS assertions
21
+ await MatchersPage.activeTab.shouldHaveClass("is-active");
22
+ await MatchersPage.activeTab.shouldNotHaveClass("is-hidden");
23
+
24
+ await MatchersPage.navLink.shouldHaveAttribute("accessibilityLabel", "nav-link");
25
+ await MatchersPage.navLink.shouldNotHaveAttribute("disabled");
26
+
27
+ await MatchersPage.styledElement.shouldHaveCss("color", "rgba(239,68,68,1)");
28
+ await MatchersPage.styledElement.shouldNotHaveCss("color", "rgba(0,0,255,1)");
3
29
  });
@@ -10,3 +10,10 @@ coverage:
10
10
  ---
11
11
 
12
12
  # Verify button state, checkbox, radio and attributes
13
+
14
+ ## Test Steps
15
+ 1. Open element state section in matchers page
16
+ 2. Assert submit button is enabled and disabled button is disabled
17
+ 3. Assert terms checkbox is not checked
18
+ 4. Assert active tab has is-active class
19
+ 5. Assert navigation link has correct href attribute
@@ -1,8 +1,29 @@
1
- it("should verify enabled/disabled states and HTML attributes on Web", async () => {
1
+ it("Verify button state, checkbox, radio and attributes", async () => {
2
2
  await MatchersPage.open("state");
3
- await MatchersPage.submitButton.should("be.enabled");
4
- await MatchersPage.disabledButton.should("be.disabled");
5
- await MatchersPage.checkbox.should("not.be.checked");
6
- await MatchersPage.activeTab.should("have.class", "is-active");
7
- await MatchersPage.navLink.should("have.attr", "href", Fixture.playgroundData.dashboardHref);
3
+
4
+ // Interactivity & state assertions
5
+ await MatchersPage.submitButton.shouldBeEnabled();
6
+ await MatchersPage.submitButton.shouldBeClickable();
7
+
8
+ await MatchersPage.disabledButton.shouldBeDisabled();
9
+ await MatchersPage.disabledButton.shouldNotBeClickable();
10
+
11
+ // Checked states
12
+ await MatchersPage.checkbox.shouldNotBeChecked();
13
+ await MatchersPage.radio.shouldBeChecked();
14
+
15
+ // Focus states
16
+ await MatchersPage.focusInput.shouldNotBeFocused();
17
+ await MatchersPage.focusInput.click();
18
+ await MatchersPage.focusInput.shouldBeFocused();
19
+
20
+ // Class, Attribute, and CSS assertions
21
+ await MatchersPage.activeTab.shouldHaveClass("is-active");
22
+ await MatchersPage.activeTab.shouldNotHaveClass("is-hidden");
23
+
24
+ await MatchersPage.navLink.shouldHaveAttribute("href", Fixture.playgroundData.dashboardHref);
25
+ await MatchersPage.navLink.shouldNotHaveAttribute("disabled");
26
+
27
+ await MatchersPage.styledElement.shouldHaveCss("color", "rgba(239,68,68,1)");
28
+ await MatchersPage.styledElement.shouldNotHaveCss("color", "rgba(0,0,255,1)");
8
29
  });
@@ -1,6 +1,7 @@
1
1
  ---
2
+ name: Untitled Suite
2
3
  id: suite-element-matchers
3
- name: ElementMatchers
4
+
4
5
  description: Element visibility, existence, form states and attributes
5
6
  executionOrder: 1.0
6
7
  parallel: true
@@ -0,0 +1,5 @@
1
+ it("Verify a custom environmentVariables entry is readable via Spectra.env", async () => {
2
+ if (Spectra.env.DEMO_API_MODE !== "sandbox") {
3
+ throw new Error(`Expected Spectra.env.DEMO_API_MODE to be "sandbox", got "${Spectra.env.DEMO_API_MODE}"`);
4
+ }
5
+ });
@@ -0,0 +1,16 @@
1
+ ---
2
+ id: TC-0019-read-custom-environment-variable
3
+ title: Verify a custom environmentVariables entry is readable via Spectra.env
4
+ priority: Medium
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: automated
9
+ mobile: automated
10
+ ---
11
+
12
+ # Verify a custom environmentVariables entry is readable via Spectra.env
13
+
14
+ ## Test Steps
15
+ 1. Read the DEMO_API_MODE variable via the typed Spectra.env
16
+ 2. Assert it matches the value configured in spectra.config.ts's executionConfig.environmentVariables
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: suite-environment-config
3
+ name: EnvironmentConfig
4
+ description: Reading custom executionConfig.environmentVariables values via Spectra.env
5
+ executionOrder: 2.3
6
+ parallel: true
7
+ ---
8
+
9
+ # Suite: EnvironmentConfig
@@ -0,0 +1,26 @@
1
+ it("Confirm custom rationale dialog Allow triggers real permission request and grants camera access", async () => {
2
+ // Navigate to the dedicated permission-rationale route (real router path, not a hash/scroll
3
+ // section) via the header link.
4
+ await Spectra.get("~nav-to-permission-demo").click();
5
+
6
+ await Spectra.get("~permission-status-text").shouldHaveText("Not requested");
7
+
8
+ await Spectra.get("~request-camera-permission-btn").click();
9
+
10
+ // The custom rationale dialog appears first — no real permission dialog yet.
11
+ await Spectra.get("~rationale-allow-btn").shouldBeVisible();
12
+
13
+ await Spectra.get("~rationale-allow-btn").click();
14
+
15
+ // Confirming the custom dialog triggers the real Android permission request. Grant it
16
+ // on-demand right now, exactly like a real device automation flow would — the app itself
17
+ // is resilient to the resulting race (see permission-rationale.tsx's recheck loop), so the
18
+ // status still lands on "Granted" below regardless of exactly when this call resolves
19
+ // relative to the OS dialog.
20
+ await Spectra.grantPermission("android.permission.CAMERA");
21
+
22
+ await Spectra.get("~permission-status-text").shouldHaveText("Granted");
23
+
24
+ // Return to the home screen so later suites in the same batch don't inherit this route.
25
+ await Spectra.get("~back-to-home-btn").click();
26
+ });
@@ -0,0 +1,22 @@
1
+ ---
2
+ id: TC-0010-camera-permission-allow
3
+ title: Confirm custom rationale dialog Allow triggers real permission request and grants camera access
4
+ priority: High
5
+ caseType: Positive
6
+ status: ready-for-automation
7
+ coverage:
8
+ web: unsupported
9
+ mobile: automated
10
+ ---
11
+
12
+ # Confirm custom rationale dialog Allow triggers real permission request and grants camera access
13
+
14
+ ## Test Steps
15
+ 1. Navigate to the permission rationale demo route
16
+ 2. Assert the initial status is "Not requested"
17
+ 3. Click the request camera permission button
18
+ 4. Assert the custom rationale dialog appears
19
+ 5. Click Allow
20
+ 6. Grant the real camera permission on demand
21
+ 7. Assert the status becomes "Granted"
22
+ 8. Return to the home screen
@@ -0,0 +1,13 @@
1
+ it("Confirm custom rationale dialog Deny never triggers the real permission request", async () => {
2
+ await Spectra.get("~nav-to-permission-demo").click();
3
+
4
+ await Spectra.get("~request-camera-permission-btn").click();
5
+ await Spectra.get("~rationale-deny-btn").shouldBeVisible();
6
+ await Spectra.get("~rationale-deny-btn").click();
7
+
8
+ // Deliberately never calls the real permission API — the status must reflect the custom
9
+ // dialog's own decision, not any OS-level request.
10
+ await Spectra.get("~permission-status-text").shouldHaveText("Declined in rationale dialog");
11
+
12
+ await Spectra.get("~back-to-home-btn").click();
13
+ });