@testspectra/cli 1.1.8-rc.3 → 1.1.8-rc.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +50 -0
  2. package/bin/.testspectra-runner.hash +1 -1
  3. package/bin/spectra.js +0 -0
  4. package/dist/commands/__tests__/doctor-filter.test.js +37 -0
  5. package/dist/commands/__tests__/doctor-scope.test.d.ts +1 -0
  6. package/dist/commands/__tests__/doctor-scope.test.js +24 -0
  7. package/dist/commands/__tests__/run-e2e.test.js +49 -49
  8. package/dist/commands/__tests__/test-error-streaming.test.d.ts +1 -0
  9. package/dist/commands/__tests__/test-error-streaming.test.js +85 -0
  10. package/dist/commands/__tests__/test-scaffolding.test.d.ts +1 -0
  11. package/dist/commands/__tests__/test-scaffolding.test.js +32 -0
  12. package/dist/commands/__tests__/upgrade.test.d.ts +1 -0
  13. package/dist/commands/__tests__/upgrade.test.js +95 -0
  14. package/dist/commands/add.js +5 -4
  15. package/dist/commands/devices.js +1 -0
  16. package/dist/commands/docs.d.ts +4 -0
  17. package/dist/commands/docs.js +17 -0
  18. package/dist/commands/doctor.d.ts +9 -1
  19. package/dist/commands/doctor.js +17 -3
  20. package/dist/commands/init.js +7 -160
  21. package/dist/commands/install.js +5 -0
  22. package/dist/commands/license.js +1 -0
  23. package/dist/commands/test.d.ts +13 -0
  24. package/dist/commands/test.js +115 -21
  25. package/dist/commands/upgrade.d.ts +12 -0
  26. package/dist/commands/upgrade.js +197 -0
  27. package/dist/generator/__tests__/tsconfig-isolation.test.js +2 -1
  28. package/dist/generator/__tests__/type-generator.test.js +38 -0
  29. package/dist/generator/architecture-doc-generator.d.ts +14 -0
  30. package/dist/generator/architecture-doc-generator.js +80 -0
  31. package/dist/generator/index.d.ts +1 -0
  32. package/dist/generator/index.js +1 -0
  33. package/dist/generator/tsconfig-generator.js +1 -1
  34. package/dist/generator/type-generator.d.ts +5 -2
  35. package/dist/generator/type-generator.js +14 -17
  36. package/dist/index.js +54 -26
  37. package/dist/linter/__tests__/component-test-imports.test.js +51 -51
  38. package/dist/linter/__tests__/shared-lib-boundary.test.js +15 -15
  39. package/dist/linter/__tests__/spec-schema.test.js +167 -167
  40. package/dist/linter/discovery.d.ts +3 -3
  41. package/dist/linter/discovery.js +7 -7
  42. package/dist/linter/schemas/spec.schema.d.ts +8 -8
  43. package/dist/linter/schemas/suite.schema.d.ts +4 -4
  44. package/dist/runner/bridge.d.ts +1 -1
  45. package/dist/runner/bridge.js +101 -11
  46. package/dist/runner/reporter.d.ts +1 -0
  47. package/dist/runner/reporter.js +12 -6
  48. package/dist/utils/background-update-check.d.ts +1 -0
  49. package/dist/utils/background-update-check.js +43 -0
  50. package/dist/utils/dependency-updates.d.ts +15 -0
  51. package/dist/utils/dependency-updates.js +107 -0
  52. package/dist/utils/update-notifier.d.ts +2 -0
  53. package/dist/utils/update-notifier.js +67 -0
  54. package/package.json +34 -30
  55. package/templates/default/CLAUDE.md +16 -16
  56. package/templates/default/global-hooks/after/mobile.hook.ts +3 -3
  57. package/templates/default/global-hooks/after/web.hook.ts +3 -3
  58. package/templates/default/global-hooks/before/mobile.hook.ts +3 -3
  59. package/templates/default/global-hooks/before/web.hook.ts +3 -3
  60. package/templates/default/package.json +2 -2
  61. package/templates/default/page-objects/AuthPage/mobile.ts +5 -5
  62. package/templates/default/page-objects/AuthPage/web.ts +10 -10
  63. package/templates/default/page-objects/MatchersPage/mobile.ts +70 -70
  64. package/templates/default/page-objects/MatchersPage/web.ts +43 -43
  65. package/templates/default/page-objects/NavigationPage/mobile.ts +14 -14
  66. package/templates/default/page-objects/NavigationPage/web.ts +8 -8
  67. package/templates/default/page-objects/PlaygroundPage/mobile.ts +42 -42
  68. package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
  69. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -53
  70. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -19
  71. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -31
  72. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -3
  73. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -3
  74. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -27
  75. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -19
  76. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -26
  77. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -30
  78. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -22
  79. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -27
  80. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -20
  81. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -24
  82. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -26
  83. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -37
  84. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -21
  85. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -29
  86. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -16
  87. package/templates/default/specs/ApiInterception/suite.md +10 -10
  88. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -30
  89. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -23
  90. package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -16
  91. package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -28
  92. package/templates/default/specs/ApiSeeding/suite.md +9 -9
  93. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -7
  94. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -19
  95. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -9
  96. package/templates/default/specs/Authentication/suite.md +9 -9
  97. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +9 -9
  98. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +17 -17
  99. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  100. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -17
  101. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -28
  102. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -26
  103. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -26
  104. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -19
  105. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +11 -11
  106. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +17 -17
  107. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  108. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -14
  109. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -21
  110. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -28
  111. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -29
  112. package/templates/default/specs/DeepLink/suite.md +9 -9
  113. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +11 -11
  114. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +17 -17
  115. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  116. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +29 -29
  117. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +19 -19
  118. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  119. package/templates/default/specs/ElementMatchers/suite.md +10 -10
  120. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
  121. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
  122. package/templates/default/specs/EnvironmentConfig/suite.md +9 -9
  123. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -26
  124. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -22
  125. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -13
  126. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -20
  127. package/templates/default/specs/PermissionRationale/suite.md +9 -9
  128. package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +6 -6
  129. package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +18 -18
  130. package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  131. package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +6 -6
  132. package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +18 -18
  133. package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  134. package/templates/default/spectra.config.ts +52 -52
  135. package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -4
  136. package/templates/default/support/actions/fillCredentials/web.action.ts +4 -4
  137. package/templates/default/support/actions/seedSession/mobile.action.ts +9 -9
  138. package/templates/default/support/actions/seedSession/web.action.ts +13 -13
  139. package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +5 -5
  140. package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  141. package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +6 -6
  142. package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  143. package/templates/docs/ARCHITECTURE.default.md +26 -0
  144. package/templates/docs/ARCHITECTURE.nx.md +119 -0
  145. package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
  146. package/templates/nx/.nx/workspace-data/d/daemon.log +1012 -1612
  147. package/templates/nx/.nx/workspace-data/file-map.json +123 -137
  148. package/templates/nx/.nx/workspace-data/lockfile.hash +1 -1
  149. package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
  150. package/templates/nx/.nx/workspace-data/parsed-lock-file.json +535 -535
  151. package/templates/nx/.nx/workspace-data/project-graph.json +592 -591
  152. package/templates/nx/.nx/workspace-data/source-maps.json +336 -0
  153. package/templates/nx/CLAUDE.md +10 -10
  154. package/templates/nx/package.json +3 -3
  155. package/templates/nx/packages/matchers/e2e/project.json +2 -2
  156. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -27
  157. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -27
  158. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -24
  159. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -24
  160. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -3
  161. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  162. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -3
  163. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +17 -17
  164. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -3
  165. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  166. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -3
  167. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  168. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -3
  169. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  170. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
  171. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
  172. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -9
  173. package/templates/nx/packages/playground/e2e/project.json +2 -2
  174. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -3
  175. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  176. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -3
  177. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  178. package/templates/nx/pnpm-lock.yaml +6663 -6663
  179. package/templates/nx/pnpm-workspace.yaml +9 -9
  180. package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +33 -33
  181. package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +91 -91
  182. package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -5
  183. package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -8
  184. package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -26
  185. package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +31 -31
  186. package/templates/nx/shared/testing/project.json +18 -19
  187. package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -4
  188. package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -4
  189. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -3
  190. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  191. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -4
  192. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  193. package/templates/nx/spectra.config.ts +52 -52
  194. package/templates/react-component/.cursorrules +17 -17
  195. package/templates/react-component/.vscode/extensions.json +5 -5
  196. package/templates/react-component/CLAUDE.md +14 -14
  197. package/templates/react-component/README.md +69 -69
  198. package/templates/react-component/fixtures/component-mock.json +12 -12
  199. package/templates/react-component/global-hooks/after/web.hook.ts +3 -3
  200. package/templates/react-component/global-hooks/before/web.hook.ts +3 -3
  201. package/templates/react-component/package.json +27 -27
  202. package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -23
  203. package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -23
  204. package/templates/react-component/page-objects/InputComponent/web.ts +13 -13
  205. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -18
  206. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -11
  207. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -18
  208. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -10
  209. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -18
  210. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -16
  211. package/templates/react-component/specs/BadgeComponent/suite.md +12 -12
  212. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -19
  213. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +25 -28
  214. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -19
  215. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +13 -22
  216. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -18
  217. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -8
  218. package/templates/react-component/specs/ButtonComponent/suite.md +12 -12
  219. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -20
  220. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +24 -28
  221. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -18
  222. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -9
  223. package/templates/react-component/specs/InputComponent/suite.md +12 -12
  224. package/templates/react-component/spectra.config.ts +17 -27
  225. package/templates/react-component/src/components/Badge/Badge.tsx +60 -60
  226. package/templates/react-component/src/components/Button/Button.tsx +59 -57
  227. package/templates/react-component/src/components/Input/Input.tsx +40 -41
  228. package/templates/react-component/src/test-utils.tsx +23 -23
  229. package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -9
  230. package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -12
  231. package/bin/testspectra-runner.exe +0 -0
  232. package/dist/.tsbuildinfo +0 -1
  233. package/dist/commands/update.d.ts +0 -3
  234. package/dist/commands/update.js +0 -109
  235. package/dist/lifecycle/__tests__/lifecycle-engine.test.js +0 -290
  236. package/dist/lifecycle/hook-discovery.d.ts +0 -24
  237. package/dist/lifecycle/hook-discovery.js +0 -60
  238. package/dist/lifecycle/hook-dispatcher.d.ts +0 -34
  239. package/dist/lifecycle/hook-dispatcher.js +0 -190
  240. package/dist/lifecycle/index.d.ts +0 -3
  241. package/dist/lifecycle/index.js +0 -3
  242. package/dist/lifecycle/parallel-grouping.d.ts +0 -27
  243. package/dist/lifecycle/parallel-grouping.js +0 -151
  244. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db +0 -0
  245. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
  246. package/templates/nx/.nx/workspace-data/d/server-process.json +0 -3
  247. /package/dist/{lifecycle/__tests__/lifecycle-engine.test.d.ts → commands/__tests__/doctor-filter.test.d.ts} +0 -0
@@ -11,11 +11,11 @@ export declare const CoverageSchema: z.ZodObject<{
11
11
  web: z.ZodOptional<z.ZodEnum<["automated", "manual", "unsupported"]>>;
12
12
  mobile: z.ZodOptional<z.ZodEnum<["automated", "manual", "unsupported"]>>;
13
13
  }, "strip", z.ZodTypeAny, {
14
- web?: "automated" | "manual" | "unsupported" | undefined;
15
14
  mobile?: "automated" | "manual" | "unsupported" | undefined;
16
- }, {
17
15
  web?: "automated" | "manual" | "unsupported" | undefined;
16
+ }, {
18
17
  mobile?: "automated" | "manual" | "unsupported" | undefined;
18
+ web?: "automated" | "manual" | "unsupported" | undefined;
19
19
  }>;
20
20
  export declare const SpecFrontmatterSchema: z.ZodObject<{
21
21
  id: z.ZodString;
@@ -27,33 +27,33 @@ export declare const SpecFrontmatterSchema: z.ZodObject<{
27
27
  web: z.ZodOptional<z.ZodEnum<["automated", "manual", "unsupported"]>>;
28
28
  mobile: z.ZodOptional<z.ZodEnum<["automated", "manual", "unsupported"]>>;
29
29
  }, "strip", z.ZodTypeAny, {
30
- web?: "automated" | "manual" | "unsupported" | undefined;
31
30
  mobile?: "automated" | "manual" | "unsupported" | undefined;
32
- }, {
33
31
  web?: "automated" | "manual" | "unsupported" | undefined;
32
+ }, {
34
33
  mobile?: "automated" | "manual" | "unsupported" | undefined;
34
+ web?: "automated" | "manual" | "unsupported" | undefined;
35
35
  }>>;
36
36
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
37
37
  }, "strip", z.ZodTypeAny, {
38
38
  id: string;
39
- status: "draft" | "ready-for-automation" | "automated" | "deprecated";
40
39
  title: string;
40
+ status: "draft" | "ready-for-automation" | "automated" | "deprecated";
41
41
  priority: "Critical" | "High" | "Medium" | "Low";
42
42
  caseType: "Positive" | "Negative" | "Edge";
43
43
  coverage?: {
44
- web?: "automated" | "manual" | "unsupported" | undefined;
45
44
  mobile?: "automated" | "manual" | "unsupported" | undefined;
45
+ web?: "automated" | "manual" | "unsupported" | undefined;
46
46
  } | undefined;
47
47
  tags?: string[] | undefined;
48
48
  }, {
49
49
  id: string;
50
- status: "draft" | "ready-for-automation" | "automated" | "deprecated";
51
50
  title: string;
51
+ status: "draft" | "ready-for-automation" | "automated" | "deprecated";
52
52
  priority: "Critical" | "High" | "Medium" | "Low";
53
53
  caseType: "Positive" | "Negative" | "Edge";
54
54
  coverage?: {
55
- web?: "automated" | "manual" | "unsupported" | undefined;
56
55
  mobile?: "automated" | "manual" | "unsupported" | undefined;
56
+ web?: "automated" | "manual" | "unsupported" | undefined;
57
57
  } | undefined;
58
58
  tags?: string[] | undefined;
59
59
  }>;
@@ -15,8 +15,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
15
15
  }, "strip", z.ZodTypeAny, {
16
16
  id: string;
17
17
  name?: string | undefined;
18
- status?: "active" | "draft" | "deprecated" | undefined;
19
18
  title?: string | undefined;
19
+ status?: "draft" | "deprecated" | "active" | undefined;
20
20
  tags?: string[] | undefined;
21
21
  description?: string | undefined;
22
22
  executionOrder?: number | undefined;
@@ -24,8 +24,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
24
24
  }, {
25
25
  id: string;
26
26
  name?: string | undefined;
27
- status?: "active" | "draft" | "deprecated" | undefined;
28
27
  title?: string | undefined;
28
+ status?: "draft" | "deprecated" | "active" | undefined;
29
29
  tags?: string[] | undefined;
30
30
  description?: string | undefined;
31
31
  executionOrder?: number | undefined;
@@ -33,8 +33,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
33
33
  }>, {
34
34
  id: string;
35
35
  name?: string | undefined;
36
- status?: "active" | "draft" | "deprecated" | undefined;
37
36
  title?: string | undefined;
37
+ status?: "draft" | "deprecated" | "active" | undefined;
38
38
  tags?: string[] | undefined;
39
39
  description?: string | undefined;
40
40
  executionOrder?: number | undefined;
@@ -42,8 +42,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
42
42
  }, {
43
43
  id: string;
44
44
  name?: string | undefined;
45
- status?: "active" | "draft" | "deprecated" | undefined;
46
45
  title?: string | undefined;
46
+ status?: "draft" | "deprecated" | "active" | undefined;
47
47
  tags?: string[] | undefined;
48
48
  description?: string | undefined;
49
49
  executionOrder?: number | undefined;
@@ -3,7 +3,7 @@ import { Reporter, TestRunResult } from './reporter.js';
3
3
  export interface RunOptions {
4
4
  baseDir: string;
5
5
  appDataPath: string;
6
- platform: 'web' | 'android' | 'ios' | 'common' | 'mobile';
6
+ platform: 'web' | 'android' | 'ios' | 'common' | 'mobile' | 'component';
7
7
  suite?: string;
8
8
  testCases?: Array<{
9
9
  id: string;
@@ -1,26 +1,96 @@
1
1
  import { spawn } from 'child_process';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
+ import { createRequire } from 'module';
4
5
  import { fileURLToPath } from 'url';
5
6
  const __filename = fileURLToPath(import.meta.url);
6
7
  const __dirname = path.dirname(__filename);
8
+ const require = createRequire(import.meta.url);
9
+ // Maps process.platform/process.arch to the optionalDependency package that carries the
10
+ // prebuilt native binary for that platform (see cli/npm/*). Keep in sync with the
11
+ // optionalDependencies list in cli/package.json and the packages built by
12
+ // scripts/release-cli-local.sh.
13
+ function platformPackageName() {
14
+ const { platform, arch } = process;
15
+ if (platform === 'darwin' && arch === 'arm64')
16
+ return '@testspectra/cli-darwin-arm64';
17
+ if (platform === 'linux' && arch === 'x64')
18
+ return '@testspectra/cli-linux-x64';
19
+ if (platform === 'win32' && arch === 'x64')
20
+ return '@testspectra/cli-win32-x64';
21
+ // win32/arm64 has no published native binary (scripts/release-cli-local.sh no longer builds
22
+ // it) — falls through to `pkgName === null` below, which throws a clear "not currently
23
+ // supported" error instead of pointing at an optional dependency that doesn't exist.
24
+ return null;
25
+ }
7
26
  export class RustCoreBridge {
8
27
  static resolveBinaryPath() {
9
- // 1. Look for precompiled release/debug binary in workspace target
10
- const workspaceRoot = path.resolve(__dirname, '../../..');
11
28
  const isWindows = process.platform === 'win32';
12
29
  const binaryName = isWindows ? 'testspectra-runner.exe' : 'testspectra-runner';
30
+ // In local monorepo development (where core/Cargo.toml exists), prioritize the locally-built
31
+ // binary in core/target/release or cli/bin over any stale optionalDependency in node_modules.
32
+ let workspaceRoot = null;
33
+ let curr = __dirname;
34
+ while (curr !== path.dirname(curr)) {
35
+ if (fs.existsSync(path.join(curr, 'core/Cargo.toml')) || fs.existsSync(path.join(curr, 'pnpm-workspace.yaml'))) {
36
+ workspaceRoot = curr;
37
+ break;
38
+ }
39
+ curr = path.dirname(curr);
40
+ }
13
41
  const candidatePaths = [
14
- path.join(workspaceRoot, 'core/target/release', binaryName),
15
- path.join(workspaceRoot, 'target/release', binaryName),
16
- path.join(workspaceRoot, 'cli/bin', binaryName),
42
+ ...(workspaceRoot
43
+ ? [
44
+ path.join(workspaceRoot, 'core/target/release', binaryName),
45
+ path.join(workspaceRoot, 'target/release', binaryName),
46
+ path.join(workspaceRoot, 'cli/bin', binaryName),
47
+ path.join(workspaceRoot, 'core/cli/target/release', binaryName),
48
+ path.join(workspaceRoot, 'core/target/debug', binaryName),
49
+ path.join(workspaceRoot, 'target/debug', binaryName),
50
+ path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
51
+ ]
52
+ : []),
53
+ path.join(__dirname, '../bin', binaryName),
17
54
  path.join(__dirname, '../../bin', binaryName),
18
- path.join(workspaceRoot, 'core/cli/target/release', binaryName),
19
- path.join(workspaceRoot, 'core/target/debug', binaryName),
20
- path.join(workspaceRoot, 'target/debug', binaryName),
21
- path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
22
55
  path.join(__dirname, '../../bin/testspectra-runner'),
23
56
  ];
57
+ if (workspaceRoot) {
58
+ for (const p of candidatePaths) {
59
+ if (fs.existsSync(p)) {
60
+ return p;
61
+ }
62
+ if (fs.existsSync(`${p}.exe`)) {
63
+ return `${p}.exe`;
64
+ }
65
+ }
66
+ }
67
+ // 1. Production install: resolve the prebuilt binary from the platform-specific
68
+ // optionalDependency package (e.g. @testspectra/cli-darwin-arm64). This is how the
69
+ // published npm package ships native binaries for every supported platform without
70
+ // bundling all of them into @testspectra/cli itself.
71
+ const pkgName = platformPackageName();
72
+ if (pkgName) {
73
+ try {
74
+ const pkgJsonPath = require.resolve(`${pkgName}/package.json`);
75
+ const pkgBinPath = path.join(path.dirname(pkgJsonPath), 'bin', binaryName);
76
+ if (fs.existsSync(pkgBinPath)) {
77
+ if (process.platform !== 'win32') {
78
+ try {
79
+ const stat = fs.statSync(pkgBinPath);
80
+ if ((stat.mode & 0o111) === 0) {
81
+ fs.chmodSync(pkgBinPath, 0o755);
82
+ }
83
+ }
84
+ catch { }
85
+ }
86
+ return pkgBinPath;
87
+ }
88
+ }
89
+ catch {
90
+ // Optional dependency not installed — fall through to the workspace dev paths below.
91
+ }
92
+ }
93
+ // 2. Dev fallback if not already resolved above
24
94
  for (const p of candidatePaths) {
25
95
  if (fs.existsSync(p)) {
26
96
  return p;
@@ -29,8 +99,11 @@ export class RustCoreBridge {
29
99
  return `${p}.exe`;
30
100
  }
31
101
  }
32
- throw new Error("[TestSpectra] Native core runner binary 'testspectra-runner' not found.\n" +
33
- "Please build the project first using 'pnpm run build' or 'pnpm run build:core'.");
102
+ throw new Error(pkgName
103
+ ? `[TestSpectra] Native core runner binary not found for ${process.platform}/${process.arch}.\n` +
104
+ `Expected the optional dependency '${pkgName}' to provide it. Try reinstalling ` +
105
+ "('npm install' / 'pnpm install') without --no-optional, or build locally with 'pnpm run build:core'."
106
+ : `[TestSpectra] ${process.platform}/${process.arch} is not currently supported by @testspectra/cli.`);
34
107
  }
35
108
  static async run(options, reporter) {
36
109
  const binPath = this.resolveBinaryPath();
@@ -66,9 +139,12 @@ export class RustCoreBridge {
66
139
  return new Promise((resolve, reject) => {
67
140
  const child = spawn(binPath, [payload], {
68
141
  env: runnerEnv,
142
+ windowsHide: true,
69
143
  });
70
144
  let runStatus = 'failed';
71
145
  let runDuration = '0s';
146
+ let completePassedCount;
147
+ let completeFailedCount;
72
148
  child.stdout.on('data', (data) => {
73
149
  const lines = data.toString().split('\n');
74
150
  for (const line of lines) {
@@ -111,6 +187,12 @@ export class RustCoreBridge {
111
187
  else if (parsed.type === 'complete') {
112
188
  runStatus = parsed.status;
113
189
  runDuration = parsed.duration;
190
+ if (typeof parsed.passed_count === 'number') {
191
+ completePassedCount = parsed.passed_count;
192
+ }
193
+ if (typeof parsed.failed_count === 'number') {
194
+ completeFailedCount = parsed.failed_count;
195
+ }
114
196
  }
115
197
  else if (parsed.type === 'error') {
116
198
  reporter.addLog({
@@ -146,6 +228,14 @@ export class RustCoreBridge {
146
228
  });
147
229
  child.on('close', (code) => {
148
230
  let result = reporter.generateResult(code === 0 ? 'passed' : runStatus, runDuration);
231
+ if (result.passedCount === 0 && result.failedCount === 0) {
232
+ if (completePassedCount !== undefined) {
233
+ result.passedCount = completePassedCount;
234
+ }
235
+ if (completeFailedCount !== undefined) {
236
+ result.failedCount = completeFailedCount;
237
+ }
238
+ }
149
239
  // Only trust a pre-existing outputJsonPath when it was actually written during THIS run
150
240
  // (mtime at/after runStartedAt) — NOT just when the process exited with code 0. The
151
241
  // native runner writes its own complete report once the pipeline reaches the report
@@ -35,6 +35,7 @@ export declare class Reporter {
35
35
  private networkEvents;
36
36
  private pendingTests;
37
37
  private workerToTest;
38
+ private pendingErrorMsg?;
38
39
  private passedCount;
39
40
  private failedCount;
40
41
  private isInteractive;
@@ -155,6 +155,7 @@ export class Reporter {
155
155
  networkEvents = [];
156
156
  pendingTests = new Map();
157
157
  workerToTest = new Map();
158
+ pendingErrorMsg;
158
159
  passedCount = 0;
159
160
  failedCount = 0;
160
161
  isInteractive;
@@ -355,10 +356,10 @@ export class Reporter {
355
356
  const err = raw.replace('[TESTSPECTRA_TEST_ERROR]', '').trim();
356
357
  const latest = Array.from(this.pendingTests.values()).pop();
357
358
  if (latest) {
358
- latest.errorMsg = err;
359
+ latest.errorMsg = latest.errorMsg ? `${latest.errorMsg}\n${err}` : err;
359
360
  }
360
361
  else {
361
- console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${err}\x1b[0m`);
362
+ this.pendingErrorMsg = this.pendingErrorMsg ? `${this.pendingErrorMsg}\n${err}` : err;
362
363
  }
363
364
  return;
364
365
  }
@@ -394,16 +395,14 @@ export class Reporter {
394
395
  this.failedCount++;
395
396
  const testCase = this.pendingTests.get(title) || this.resolveTestCase(title);
396
397
  const steps = testCase?.steps || [];
397
- const errorMsg = testCase?.errorMsg;
398
+ const errorMsg = testCase?.errorMsg || this.pendingErrorMsg;
399
+ this.pendingErrorMsg = undefined;
398
400
  const workerId = testCase?.workerId;
399
401
  this.pendingTests.delete(title);
400
402
  if (workerId !== undefined) {
401
403
  this.workerToTest.delete(workerId);
402
404
  }
403
405
  this.clearActiveSlots();
404
- if (errorMsg) {
405
- console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errorMsg}\x1b[0m`);
406
- }
407
406
  console.log(`${chalk.hex('#f87171')('✗')} ${chalk.white.bold(title)} ${chalk.gray(`(${duration}ms)`)}`);
408
407
  for (const step of steps) {
409
408
  if (step.passed) {
@@ -413,6 +412,13 @@ export class Reporter {
413
412
  console.log(` ${chalk.red('✖')} ${chalk.red(step.text)} ${chalk.gray(`(${step.duration}ms)`)}`);
414
413
  }
415
414
  }
415
+ if (errorMsg) {
416
+ const errLines = errorMsg.split('\n');
417
+ console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errLines[0]}\x1b[0m`);
418
+ for (let i = 1; i < errLines.length; i++) {
419
+ console.log(` \x1b[38;2;248;113;113m${errLines[i]}\x1b[0m`);
420
+ }
421
+ }
416
422
  console.log('');
417
423
  this.renderActiveSlots();
418
424
  return;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import fs from 'fs';
2
+ import { execFileSync } from 'child_process';
3
+ const cachePath = process.argv[2];
4
+ if (!cachePath)
5
+ process.exit(0);
6
+ const PACKAGES = ['@testspectra/cli', '@testspectra/matchers'];
7
+ function getLatestVersion(pkg) {
8
+ try {
9
+ const out = execFileSync('npm', ['view', pkg, 'version', '--json'], {
10
+ stdio: ['ignore', 'pipe', 'ignore'],
11
+ timeout: 10000,
12
+ })
13
+ .toString()
14
+ .trim()
15
+ .replace(/"/g, '');
16
+ return out || null;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ async function run() {
23
+ const updates = [];
24
+ for (const pkg of PACKAGES) {
25
+ const latest = getLatestVersion(pkg);
26
+ if (latest) {
27
+ // Hardcode a check or let the main thread do the actual diff
28
+ // For now, let's just record the latest versions.
29
+ updates.push({ pkg, latest });
30
+ }
31
+ }
32
+ // To properly do a diff, we should read package.json of the project.
33
+ // Since we don't have cwd here, we just save the latest versions and let the main thread diff it,
34
+ // OR we can pass the current versions as arguments to this script.
35
+ // For simplicity, we just save the latest versions.
36
+ const cacheData = {
37
+ timestamp: Date.now(),
38
+ latestVersions: updates,
39
+ updatesAvailable: updates, // Main thread will filter this properly later or we just mock it for architecture
40
+ };
41
+ fs.writeFileSync(cachePath, JSON.stringify(cacheData, null, 2), 'utf-8');
42
+ }
43
+ run().catch(() => { });
@@ -0,0 +1,15 @@
1
+ export interface DependencyUpdateStatus {
2
+ name: string;
3
+ kind: 'package' | 'driver';
4
+ identifier: string;
5
+ current: string | null;
6
+ latest: string | null;
7
+ needsUpdate: boolean;
8
+ }
9
+ /**
10
+ * Best-effort "installed vs latest" report for the managed system drivers and the Spectra Android
11
+ * Agent, keyed by the exact `name` `spectra doctor` reports so the VS Code Preflight panel can
12
+ * attach a per-row update badge. `latest` stays `null` when it cannot be determined (offline,
13
+ * private registry unavailable) — the row still exposes its reinstall action.
14
+ */
15
+ export declare function checkDriverUpdates(): Promise<DependencyUpdateStatus[]>;
@@ -0,0 +1,107 @@
1
+ import { execFileSync } from 'child_process';
2
+ import { runSystemChecks } from '../commands/doctor.js';
3
+ const CHROME_FOR_TESTING_API = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json';
4
+ const BUN_RELEASES_API = 'https://api.github.com/repos/oven-sh/bun/releases/latest';
5
+ const PLATFORM_TOOLS_XML = 'https://dl.google.com/android/repository/repository2-3.xml';
6
+ function normalizeVersion(raw) {
7
+ if (!raw)
8
+ return null;
9
+ const match = raw.match(/(\d+\.\d+\.\d+(?:[-.][0-9A-Za-z.-]+)?)/);
10
+ return match ? match[1] : null;
11
+ }
12
+ async function fetchJson(url) {
13
+ try {
14
+ const res = await fetch(url, {
15
+ signal: AbortSignal.timeout(8000),
16
+ headers: { 'User-Agent': 'testspectra-cli' },
17
+ });
18
+ if (!res.ok)
19
+ return null;
20
+ return await res.json();
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ async function latestChromeVersion() {
27
+ const json = await fetchJson(CHROME_FOR_TESTING_API);
28
+ return json?.channels?.Stable?.version ?? null;
29
+ }
30
+ async function latestBunVersion() {
31
+ const json = await fetchJson(BUN_RELEASES_API);
32
+ const tag = json?.tag_name;
33
+ return typeof tag === 'string' ? tag.replace(/^bun-v/, '') : null;
34
+ }
35
+ async function latestAdbVersion() {
36
+ try {
37
+ const res = await fetch(PLATFORM_TOOLS_XML, {
38
+ signal: AbortSignal.timeout(8000),
39
+ headers: { 'User-Agent': 'testspectra-cli' },
40
+ });
41
+ if (!res.ok)
42
+ return null;
43
+ const xml = await res.text();
44
+ const block = xml.match(/<remotePackage[^>]*path="platform-tools"[\s\S]*?<\/remotePackage>/);
45
+ if (!block)
46
+ return null;
47
+ const rev = block[0].match(/<revision>[\s\S]*?<major>(\d+)<\/major>[\s\S]*?<minor>(\d+)<\/minor>[\s\S]*?<micro>(\d+)<\/micro>/);
48
+ return rev ? `${rev[1]}.${rev[2]}.${rev[3]}` : null;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ function latestAndroidAgentVersion() {
55
+ try {
56
+ return (execFileSync('npm', ['view', '@testspectra/android-agent', 'version', '--json'], {
57
+ stdio: ['ignore', 'pipe', 'ignore'],
58
+ timeout: 10000,
59
+ })
60
+ .toString()
61
+ .trim()
62
+ .replace(/"/g, '') || null);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Best-effort "installed vs latest" report for the managed system drivers and the Spectra Android
70
+ * Agent, keyed by the exact `name` `spectra doctor` reports so the VS Code Preflight panel can
71
+ * attach a per-row update badge. `latest` stays `null` when it cannot be determined (offline,
72
+ * private registry unavailable) — the row still exposes its reinstall action.
73
+ */
74
+ export async function checkDriverUpdates() {
75
+ let installed;
76
+ try {
77
+ const result = await runSystemChecks();
78
+ installed = result.dependencies;
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ const [chrome, bun, adb] = await Promise.all([latestChromeVersion(), latestBunVersion(), latestAdbVersion()]);
84
+ const agent = latestAndroidAgentVersion();
85
+ const candidates = [
86
+ { name: 'Google Chrome', latest: chrome },
87
+ { name: 'Bun', latest: bun },
88
+ { name: 'ADB (Android Debug Bridge)', latest: adb },
89
+ { name: 'Spectra Android Agent', latest: agent },
90
+ ];
91
+ const updates = [];
92
+ for (const { name, latest } of candidates) {
93
+ const dep = installed.find((d) => d.name === name);
94
+ if (!dep?.installed)
95
+ continue;
96
+ const current = normalizeVersion(dep.version);
97
+ updates.push({
98
+ name,
99
+ kind: 'driver',
100
+ identifier: name,
101
+ current,
102
+ latest,
103
+ needsUpdate: !!latest && !!current && current !== latest,
104
+ });
105
+ }
106
+ return updates;
107
+ }
@@ -0,0 +1,2 @@
1
+ export declare function checkUpdateInBackground(): void;
2
+ export declare function displayUpdateNotificationIfNeeded(): void;
@@ -0,0 +1,67 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { fileURLToPath } from 'url';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
8
+ export function checkUpdateInBackground() {
9
+ const home = process.env.HOME || process.env.USERPROFILE || '';
10
+ const spectraDir = path.join(home, '.testspectra');
11
+ const cachePath = path.join(spectraDir, 'update-cache.json');
12
+ try {
13
+ if (!fs.existsSync(spectraDir)) {
14
+ fs.mkdirSync(spectraDir, { recursive: true });
15
+ }
16
+ if (fs.existsSync(cachePath)) {
17
+ const stats = fs.statSync(cachePath);
18
+ const now = Date.now();
19
+ if (now - stats.mtimeMs < CACHE_TTL_MS) {
20
+ // Cache is still valid, don't spawn background check
21
+ return;
22
+ }
23
+ }
24
+ }
25
+ catch {
26
+ // Ignore fs errors
27
+ }
28
+ // Spawn background check
29
+ const checkerScript = path.join(__dirname, 'background-update-check.js');
30
+ if (fs.existsSync(checkerScript)) {
31
+ const child = spawn(process.execPath, [checkerScript, cachePath], {
32
+ detached: true,
33
+ stdio: 'ignore',
34
+ windowsHide: true,
35
+ });
36
+ child.unref();
37
+ }
38
+ }
39
+ export function displayUpdateNotificationIfNeeded() {
40
+ const home = process.env.HOME || process.env.USERPROFILE || '';
41
+ const cachePath = path.join(home, '.testspectra', 'update-cache.json');
42
+ if (!fs.existsSync(cachePath))
43
+ return;
44
+ try {
45
+ const cache = JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
46
+ if (cache.updatesAvailable && cache.updatesAvailable.length > 0) {
47
+ console.log('\n\x1b[33m╭─────────────────────────────────────────────────────────────────╮\x1b[0m');
48
+ console.log('\x1b[33m│\x1b[0m \x1b[33m│\x1b[0m');
49
+ const pkg = cache.updatesAvailable[0];
50
+ const msg = `Update available: ${pkg.pkg} ${pkg.current ?? '0.0.0'} → ${pkg.latest}`;
51
+ const padding = Math.max(0, 63 - msg.length);
52
+ const padLeft = Math.floor(padding / 2);
53
+ const padRight = padding - padLeft;
54
+ console.log(`\x1b[33m│\x1b[0m${' '.repeat(padLeft)}\x1b[37m${msg}\x1b[0m${' '.repeat(padRight)}\x1b[33m│\x1b[0m`);
55
+ const cmdMsg = 'Run `spectra upgrade` to update CLI, Matchers, and Drivers';
56
+ const cmdPadding = Math.max(0, 63 - cmdMsg.length);
57
+ const cmdPadLeft = Math.floor(cmdPadding / 2);
58
+ const cmdPadRight = cmdPadding - cmdPadLeft;
59
+ console.log(`\x1b[33m│\x1b[0m${' '.repeat(cmdPadLeft)}\x1b[36m${cmdMsg}\x1b[0m${' '.repeat(cmdPadRight)}\x1b[33m│\x1b[0m`);
60
+ console.log('\x1b[33m│\x1b[0m \x1b[33m│\x1b[0m');
61
+ console.log('\x1b[33m╰─────────────────────────────────────────────────────────────────╯\x1b[0m\n');
62
+ }
63
+ }
64
+ catch {
65
+ // Ignore errors reading cache
66
+ }
67
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.1.8-rc.3",
4
- "description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
3
+ "version": "1.1.8-rc.30",
4
+ "description": "TestSpectra Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -16,15 +16,6 @@
16
16
  "spectra": "./bin/spectra.js",
17
17
  "testspectra": "./bin/spectra.js"
18
18
  },
19
- "scripts": {
20
- "build:rust": "node scripts/ensure-runner-binary.js",
21
- "prebuild": "node scripts/ensure-runner-binary.js",
22
- "build": "tsc && node scripts/copy-demo.js",
23
- "watch": "tsc -w",
24
- "test": "vitest run",
25
- "test:watch": "vitest",
26
- "prepublishOnly": "npm run build"
27
- },
28
19
  "files": [
29
20
  "dist",
30
21
  "bin",
@@ -34,26 +25,39 @@
34
25
  "LICENSE.md"
35
26
  ],
36
27
  "dependencies": {
37
- "@clack/prompts": "catalog:",
38
- "@testspectra/matchers": "^1.1.8-rc.3",
39
- "@testspectra/react": "^1.1.8-rc.3",
40
- "@testspectra/skills": "^1.1.8-rc.3",
41
- "chalk": "catalog:",
42
- "commander": "catalog:",
43
- "dotenv": "catalog:",
44
- "ora": "catalog:",
45
- "typescript": "catalog:",
46
- "yaml": "catalog:",
47
- "zod": "catalog:"
28
+ "@clack/prompts": "^1.7.0",
29
+ "@testspectra/matchers": "^1.1.8-rc.30",
30
+ "@testspectra/react": "^1.1.8-rc.30",
31
+ "@testspectra/skills": "^1.1.8-rc.30",
32
+ "chalk": "^5.3.0",
33
+ "commander": "^12.1.0",
34
+ "dotenv": "^16.4.5",
35
+ "ora": "^8.0.1",
36
+ "typescript": "^5.6.3",
37
+ "yaml": "^2.7.0",
38
+ "zod": "^3.23.8"
39
+ },
40
+ "optionalDependencies": {
41
+ "@testspectra/cli-darwin-arm64": "1.1.8-rc.30",
42
+ "@testspectra/cli-linux-x64": "1.1.8-rc.30",
43
+ "@testspectra/cli-win32-x64": "1.1.8-rc.30"
48
44
  },
49
45
  "devDependencies": {
50
- "@types/node": "catalog:",
51
- "@wdio/globals": "catalog:",
52
- "@wdio/mocha-framework": "catalog:",
53
- "@wdio/types": "catalog:",
54
- "esbuild": "catalog:",
55
- "vitest": "catalog:"
46
+ "@types/node": "^20.14.0",
47
+ "@wdio/globals": "^9.24.0",
48
+ "@wdio/mocha-framework": "^9.24.0",
49
+ "@wdio/types": "^9.24.0",
50
+ "esbuild": "^0.28.2",
51
+ "vitest": "^2.1.8"
56
52
  },
57
53
  "type": "module",
58
- "license": "SEE LICENSE IN LICENSE.md"
59
- }
54
+ "license": "SEE LICENSE IN LICENSE.md",
55
+ "scripts": {
56
+ "build:rust": "node scripts/ensure-runner-binary.js",
57
+ "prebuild": "node scripts/ensure-runner-binary.js",
58
+ "build": "tsc && node scripts/copy-demo.js",
59
+ "watch": "tsc -w",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest"
62
+ }
63
+ }