@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,422 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
5
+ import { runCommand } from '../run.js';
6
+ import { initCommand } from '../init.js';
7
+ import { DemoServer } from '../../utils/demo-server.js';
8
+ import { DemoApiServer } from '../../utils/api-server.js';
9
+ import { RustCoreBridge } from '../../runner/bridge.js';
10
+ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/core/e2e-testing-plan.md)', () => {
11
+ let tmpDir;
12
+ let originalCwd;
13
+ let demoServer;
14
+ let apiServer;
15
+ let consoleLogs = [];
16
+ const originalLog = console.log;
17
+ beforeAll(async () => {
18
+ // 1. BeforeAll: Start demo web app server
19
+ demoServer = new DemoServer(5173);
20
+ try {
21
+ await demoServer.start();
22
+ }
23
+ catch {
24
+ // Server may already be running on port 5173
25
+ }
26
+ // The demo API server runs separately (port 5200). Tests that don't pass --demo still need it
27
+ // reachable: the web app now fetches the API cross-origin, so a JSON POST triggers a CORS
28
+ // preflight (OPTIONS) that isn't mocked and must reach a live API server.
29
+ apiServer = new DemoApiServer();
30
+ try {
31
+ await apiServer.start();
32
+ }
33
+ catch {
34
+ // API server may already be running on port 5200
35
+ }
36
+ });
37
+ afterAll(async () => {
38
+ // 4. AfterAll: Stop demo servers
39
+ try {
40
+ await demoServer.stop();
41
+ }
42
+ catch { }
43
+ try {
44
+ await apiServer.stop();
45
+ }
46
+ catch { }
47
+ });
48
+ beforeEach(async () => {
49
+ originalCwd = process.cwd();
50
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'testspectra-e2e-'));
51
+ process.chdir(tmpDir);
52
+ // Scaffold full template project
53
+ await initCommand({ template: 'default', force: true });
54
+ consoleLogs = [];
55
+ console.log = vi.fn((...args) => {
56
+ consoleLogs.push(args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' '));
57
+ originalLog(...args);
58
+ });
59
+ });
60
+ afterEach(() => {
61
+ console.log = originalLog;
62
+ process.chdir(originalCwd);
63
+ try {
64
+ fs.rmSync(tmpDir, { recursive: true, force: true });
65
+ }
66
+ catch { }
67
+ vi.restoreAllMocks();
68
+ });
69
+ // E2E-01: Default Template Full Run
70
+ it('E2E-01: Default Template Full Run executes native runner and produces result.json', async () => {
71
+ // Ensure binary exists or mock bridge if binary is undergoing compilation
72
+ const binaryExists = (() => {
73
+ try {
74
+ RustCoreBridge.resolveBinaryPath();
75
+ return true;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ })();
81
+ expect(binaryExists).toBe(true);
82
+ // Execute testspectra run -t web on full template project
83
+ await runCommand(undefined, { target: 'web', demo: true });
84
+ // Assert reports created
85
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
86
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
87
+ const resultRaw = fs.readFileSync(resultJsonPath, 'utf-8');
88
+ const resultJson = JSON.parse(resultRaw);
89
+ // Validate result.json schema contract (Section 3.2.A)
90
+ expect(resultJson.version).toBe('2.0.0');
91
+ expect(resultJson.platform).toBe('web');
92
+ expect(resultJson.status).toBe('passed');
93
+ expect(resultJson.summary.passRate).toBe('100%');
94
+ expect(resultJson.summary.totalSuites).toBeGreaterThanOrEqual(1);
95
+ expect(resultJson.summary.totalTests).toBeGreaterThanOrEqual(1);
96
+ expect(resultJson.summary.passed).toBeGreaterThanOrEqual(1);
97
+ expect(resultJson.summary.failed).toBe(0);
98
+ // Performance RSS assertion (< 30 MB)
99
+ expect(resultJson.performance.peakMemoryRssMb).toBeLessThan(30);
100
+ expect(resultJson.suites.length).toBeGreaterThanOrEqual(1);
101
+ }, 30000);
102
+ // E2E-02: Single Suite Targeted Run
103
+ it('E2E-02: Single Suite Targeted Run executes only specified spec', async () => {
104
+ const targetSpec = 'specs/Playground/TC-0001-form-controls/web.test.ts';
105
+ expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
106
+ await runCommand([targetSpec], { target: 'web', demo: true });
107
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
108
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
109
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
110
+ expect(resultJson.version).toBe('2.0.0');
111
+ expect(resultJson.platform).toBe('web');
112
+ expect(resultJson.status).toBe('passed');
113
+ expect(resultJson.summary.totalSuites).toBe(1);
114
+ expect(resultJson.summary.totalTests).toBe(1);
115
+ expect(resultJson.summary.passed).toBe(1);
116
+ expect(resultJson.summary.failed).toBe(0);
117
+ expect(resultJson.summary.passRate).toBe('100%');
118
+ }, 30000);
119
+ // E2E-03: Network Recording Audit
120
+ it('E2E-03: Network Recording Audit creates network-resources.json with request entries', async () => {
121
+ await runCommand(undefined, { target: 'web', demo: true });
122
+ const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
123
+ expect(fs.existsSync(networkJsonPath)).toBe(true);
124
+ const networkRaw = fs.readFileSync(networkJsonPath, 'utf-8');
125
+ const networkResources = JSON.parse(networkRaw);
126
+ expect(Array.isArray(networkResources)).toBe(true);
127
+ expect(networkResources.length).toBeGreaterThanOrEqual(1);
128
+ const first = networkResources[0];
129
+ expect(first).toHaveProperty('requestId');
130
+ expect(first).toHaveProperty('url');
131
+ expect(first).toHaveProperty('method');
132
+ expect(first).toHaveProperty('status');
133
+ }, 30000);
134
+ // E2E-04: Semantic Logs Streaming & Summary Card Terminal
135
+ it('E2E-04: Semantic Logs Streaming displays natural logs and summary card', async () => {
136
+ await runCommand(undefined, { target: 'web', demo: true });
137
+ const allOutput = consoleLogs.join('\n');
138
+ expect(allOutput).toContain('TESTSPECTRA EXECUTION SUMMARY');
139
+ expect(allOutput).toContain('PASSED');
140
+ expect(allOutput).toContain('Platform');
141
+ expect(allOutput).toContain('Duration');
142
+ expect(allOutput).toContain('Report');
143
+ }, 30000);
144
+ // E2E-05: Failure Handling & Trace
145
+ it('E2E-05: Failure Handling & Trace properly records failed status and error details', async () => {
146
+ // Inject a failing step / invalid selector in a new test case
147
+ const failingDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0099-failing');
148
+ fs.mkdirSync(failingDir, { recursive: true });
149
+ fs.writeFileSync(path.join(failingDir, 'web.test.ts'), `it("should fail when element does not exist", async () => {
150
+ await Step.verifyPlaygroundState();
151
+ await $("#non-existent-button-999").shouldBeVisible();
152
+ });
153
+ `, 'utf-8');
154
+ // Mock exit to prevent process termination during unit test assertion
155
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
156
+ await runCommand(['specs/Playground/TC-0099-failing/web.test.ts'], { target: 'web', demo: true });
157
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
158
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
159
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
160
+ // If failure was triggered, status is failed and failed count >= 1
161
+ expect(resultJson.status).toBeDefined();
162
+ exitSpy.mockRestore();
163
+ }, 30000);
164
+ // E2E-06: Spectra Static API & Assertion Mismatch Failure in Real Chromium
165
+ it('E2E-06: Spectra static actions and negative assertion failures strictly fail in real Chromium', async () => {
166
+ // 1. Test a valid spec using pure Spectra static commands
167
+ const spectraStaticDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0100-spectra-static');
168
+ fs.mkdirSync(spectraStaticDir, { recursive: true });
169
+ fs.writeFileSync(path.join(spectraStaticDir, 'web.test.ts'), `it("should execute via unified Spectra static object", async () => {
170
+ await Spectra.navigate("/#playground");
171
+ await Spectra.type("#demo-email-input-pg", "automated@spectra.dev");
172
+ await Spectra.get("#demo-email-input-pg").shouldHaveValue("automated@spectra.dev");
173
+ await Spectra.wait(50);
174
+ await Spectra.getAll(".demo-product-item").shouldHaveLengthGreaterThan(0);
175
+ });
176
+ `, 'utf-8');
177
+ await runCommand(['specs/Playground/TC-0100-spectra-static/web.test.ts'], { target: 'web', demo: true });
178
+ const resultStatic = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
179
+ expect(resultStatic.status).toBe('passed');
180
+ expect(resultStatic.summary.passed).toBe(1);
181
+ // 2. Test that assertion mismatch on existing element strictly FAILS (no false positive)
182
+ const mismatchDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0101-mismatch');
183
+ fs.mkdirSync(mismatchDir, { recursive: true });
184
+ fs.writeFileSync(path.join(mismatchDir, 'web.test.ts'), `it("should strictly fail when assertion text does not match", async () => {
185
+ await Spectra.navigate("/#playground");
186
+ // Element exists, but text is wrong -> MUST throw and fail
187
+ await Spectra.get("#demo-email-input-pg").shouldHaveValue("completely_wrong_value_xyz");
188
+ });
189
+ `, 'utf-8');
190
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
191
+ await runCommand(['specs/Playground/TC-0101-mismatch/web.test.ts'], { target: 'web', demo: true });
192
+ const resultMismatch = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
193
+ expect(resultMismatch.status).toBe('failed');
194
+ expect(resultMismatch.summary.failed).toBeGreaterThanOrEqual(1);
195
+ exitSpy.mockRestore();
196
+ }, 25000);
197
+ // E2E-07: Spectra.intercept GET Mocking & Dynamic respondWith on Web in Real Chromium
198
+ it('E2E-07: Spectra.intercept GET Mocking & Dynamic respondWith on Web in real Chromium', async () => {
199
+ const targetSpec = 'specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts';
200
+ expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
201
+ await runCommand([targetSpec], { target: 'web' });
202
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
203
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
204
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
205
+ expect(resultJson.version).toBe('2.0.0');
206
+ expect(resultJson.platform).toBe('web');
207
+ expect(resultJson.status).toBe('passed');
208
+ expect(resultJson.summary.totalSuites).toBe(1);
209
+ expect(resultJson.summary.totalTests).toBe(1);
210
+ expect(resultJson.summary.passed).toBe(1);
211
+ expect(resultJson.summary.failed).toBe(0);
212
+ expect(resultJson.summary.passRate).toBe('100%');
213
+ // Assert network interception log was recorded in network-resources.json
214
+ const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
215
+ expect(fs.existsSync(networkJsonPath)).toBe(true);
216
+ const networkEntries = JSON.parse(fs.readFileSync(networkJsonPath, 'utf-8'));
217
+ const interceptedPostReq = networkEntries.find((entry) => entry.url && entry.url.includes('/api/posts'));
218
+ expect(interceptedPostReq).toBeDefined();
219
+ expect(interceptedPostReq.status).toBe(200);
220
+ }, 20000);
221
+ // E2E-08: Spectra.intercept POST Mocking with status 201 and payload verification on Web in Real Chromium
222
+ it('E2E-08: Spectra.intercept POST Mocking with status 201 and payload verification on Web', async () => {
223
+ const targetSpec = 'specs/ApiInterception/TC-0009-post-mocking/web.test.ts';
224
+ expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
225
+ await runCommand([targetSpec], { target: 'web' });
226
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
227
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
228
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
229
+ expect(resultJson.version).toBe('2.0.0');
230
+ expect(resultJson.platform).toBe('web');
231
+ expect(resultJson.status).toBe('passed');
232
+ expect(resultJson.summary.totalSuites).toBe(1);
233
+ expect(resultJson.summary.totalTests).toBe(1);
234
+ expect(resultJson.summary.passed).toBe(1);
235
+ expect(resultJson.summary.failed).toBe(0);
236
+ const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
237
+ expect(fs.existsSync(networkJsonPath)).toBe(true);
238
+ const networkEntries = JSON.parse(fs.readFileSync(networkJsonPath, 'utf-8'));
239
+ const postReq = networkEntries.find((entry) => entry.url && entry.url.includes('/api/posts') && entry.status === 201);
240
+ expect(postReq).toBeDefined();
241
+ }, 20000);
242
+ // E2E-09: Mobile API Interception & Socket Driver on Android target
243
+ it('E2E-09: Mobile API Interception and verification on Android target', async () => {
244
+ const net = await import('net');
245
+ // Start a mock Spectra Native Android Driver TCP Server on port 8200
246
+ const mockAndroidServer = net.createServer((socket) => {
247
+ let rxBuf = Buffer.alloc(0);
248
+ let fetchClickCount = 0;
249
+ socket.on('data', (chunk) => {
250
+ rxBuf = Buffer.concat([rxBuf, chunk]);
251
+ while (rxBuf.length >= 4) {
252
+ const payloadLen = rxBuf.readUInt32BE(0);
253
+ if (rxBuf.length < 4 + payloadLen)
254
+ break;
255
+ const payloadStr = rxBuf.slice(4, 4 + payloadLen).toString('utf8');
256
+ rxBuf = rxBuf.slice(4 + payloadLen);
257
+ try {
258
+ const msg = JSON.parse(payloadStr);
259
+ const cmd = msg.cmd;
260
+ const id = msg.id;
261
+ const params = msg.params || {};
262
+ let responseData = {};
263
+ if (cmd === 'FIND_ALL') {
264
+ const sel = (params.selector || '').toLowerCase();
265
+ // The driver now reads via a single full-hierarchy dump (`FIND_ALL xpath //*`) and
266
+ // resolves selectors in memory, while actions use a targeted lookup for a live
267
+ // nodeId. This mock returns the same node set for both, so either path resolves.
268
+ //
269
+ // TC-0008's mobile.test.ts drives 4 clicks in its single it() block: base mock
270
+ // (2 items) -> respondWith dynamic update (1 item) -> respondOnce queued response
271
+ // (1 item) -> reverted to base mock (2 items again).
272
+ const items = fetchClickCount === 2
273
+ ? [{ nodeId: 'item-dyn-1', text: 'Dynamic Updated Mock Title' }]
274
+ : fetchClickCount === 3
275
+ ? [{ nodeId: 'item-once-1', text: 'One-Time Queued Response' }]
276
+ : [
277
+ { nodeId: 'item-1', text: 'Mocked Post 1 - TestSpectra Matchers' },
278
+ { nodeId: 'item-2', text: 'Mocked Post 2 - Cross-Platform Automation' },
279
+ ];
280
+ const mkNode = (nodeId, text, id) => ({
281
+ nodeId,
282
+ text,
283
+ resourceId: id,
284
+ contentDesc: id,
285
+ bounds: [0, 0, 10, 10],
286
+ displayed: true,
287
+ clickable: true,
288
+ enabled: true,
289
+ checked: false,
290
+ selected: false,
291
+ focused: false,
292
+ });
293
+ const tree = [
294
+ mkNode('toggle-1', '☰', 'toggle-sidebar-btn'),
295
+ mkNode('close-1', '✕', 'close-sidebar-btn'),
296
+ mkNode('nav-1', 'API Fetch', 'nav-item-api-fetch'),
297
+ mkNode('fetch-api-btn-node', 'Fetch Data', 'fetch-api-btn'),
298
+ mkNode('badge-1', '200 OK', 'fetch-status-badge'),
299
+ ...items.map((n, i) => mkNode(n.nodeId || `item-${i}`, n.text, 'api-post-item')),
300
+ mkNode('badge-2', '201 Created', 'post-status-badge'),
301
+ mkNode('banner-1', 'Successfully created post', 'post-success-banner'),
302
+ mkNode('resp-json-1', '{"title": "TestSpectra Automated Post"}', 'post-response-json'),
303
+ ];
304
+ responseData = sel.startsWith('//')
305
+ ? tree
306
+ : tree.filter((n) => n.resourceId === sel || n.contentDesc === sel);
307
+ }
308
+ else if (cmd === 'CLICK' || cmd === 'PERFORM_ACTION') {
309
+ if (params.node_id === 'fetch-api-btn-node' || params.nodeId === 'fetch-api-btn-node') {
310
+ fetchClickCount++;
311
+ }
312
+ responseData = true;
313
+ }
314
+ else if (cmd === 'GET_ATTRIBUTE') {
315
+ const attr = params.attr;
316
+ if (attr === 'text') {
317
+ responseData = '200 OK';
318
+ }
319
+ else {
320
+ responseData = 'Mock Attribute';
321
+ }
322
+ }
323
+ const respObj = { id, ok: true, data: responseData };
324
+ const respStr = JSON.stringify(respObj);
325
+ const respLen = Buffer.byteLength(respStr, 'utf8');
326
+ const frame = Buffer.alloc(4 + respLen);
327
+ frame.writeUInt32BE(respLen, 0);
328
+ frame.write(respStr, 4, 'utf8');
329
+ socket.write(frame);
330
+ }
331
+ catch { }
332
+ }
333
+ });
334
+ });
335
+ let isMockServerRunning = false;
336
+ await new Promise((resolve) => {
337
+ mockAndroidServer.once('error', (err) => {
338
+ if (err.code === 'EADDRINUSE') {
339
+ resolve();
340
+ }
341
+ });
342
+ mockAndroidServer.listen(8200, '127.0.0.1', () => {
343
+ isMockServerRunning = true;
344
+ resolve();
345
+ });
346
+ });
347
+ try {
348
+ const configPath = path.join(tmpDir, 'spectra.config.ts');
349
+ if (fs.existsSync(configPath)) {
350
+ let conf = fs.readFileSync(configPath, 'utf-8');
351
+ conf = conf.replace(/appPackage:\s*["'][^"']*["'],/, 'appPackage: "",');
352
+ fs.writeFileSync(configPath, conf, 'utf-8');
353
+ }
354
+ const targetSpec = 'specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts';
355
+ expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
356
+ await runCommand([targetSpec], { target: 'android', device: 'mock-device' });
357
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
358
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
359
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
360
+ expect(resultJson.version).toBe('2.0.0');
361
+ expect(resultJson.platform).toBe('android');
362
+ expect(resultJson.status).toBe('passed');
363
+ expect(resultJson.summary.passed).toBe(1);
364
+ expect(resultJson.summary.failed).toBe(0);
365
+ }
366
+ finally {
367
+ if (isMockServerRunning) {
368
+ await new Promise((resolve) => {
369
+ mockAndroidServer.close(() => resolve());
370
+ });
371
+ }
372
+ }
373
+ }, 25000);
374
+ // E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation
375
+ it('E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation in real Chromium', async () => {
376
+ const fifoAbortDir = path.join(tmpDir, 'specs', 'ApiInterception', 'TC-0010-fifo-abort');
377
+ fs.mkdirSync(fifoAbortDir, { recursive: true });
378
+ fs.writeFileSync(path.join(fifoAbortDir, 'web.test.ts'), `it("should handle FIFO respondOnce queue and abort simulation", async () => {
379
+ await MatchersPage.open("api-fetch");
380
+
381
+ // Setup intercept with default fallback and FIFO queue
382
+ const mock = await Spectra.intercept("api/posts", "GET", [
383
+ { id: 999, title: "Default Fallback Post" }
384
+ ]);
385
+
386
+ await mock.respondOnce([
387
+ { id: 101, title: "FIFO Queued Post #1" }
388
+ ]);
389
+ await mock.respondOnce([
390
+ { id: 102, title: "FIFO Queued Post #2" }
391
+ ]);
392
+
393
+ // 1. First fetch triggers FIFO item #1
394
+ await MatchersPage.fetchApiButton.click();
395
+ await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #1");
396
+
397
+ // 2. Second fetch triggers FIFO item #2
398
+ await MatchersPage.fetchApiButton.click();
399
+ await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #2");
400
+
401
+ // 3. Third fetch falls back to default response
402
+ await MatchersPage.fetchApiButton.click();
403
+ await MatchersPage.fetchedItems.first().shouldContainText("Default Fallback Post");
404
+
405
+ // 4. Test abort simulation
406
+ await mock.abort("Failed");
407
+ await MatchersPage.fetchApiButton.click();
408
+ // On network failure, demo app shows fallback 200 OK from catch block
409
+ await MatchersPage.fetchStatusBadge.shouldHaveText("200 OK");
410
+ });
411
+ `, 'utf-8');
412
+ await runCommand(['specs/ApiInterception/TC-0010-fifo-abort/web.test.ts'], { target: 'web' });
413
+ const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
414
+ expect(fs.existsSync(resultJsonPath)).toBe(true);
415
+ const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
416
+ expect(resultJson.version).toBe('2.0.0');
417
+ expect(resultJson.platform).toBe('web');
418
+ expect(resultJson.status).toBe('passed');
419
+ expect(resultJson.summary.passed).toBe(1);
420
+ expect(resultJson.summary.failed).toBe(0);
421
+ }, 25000);
422
+ });
@@ -0,0 +1 @@
1
+ export {};