@testspectra/cli 1.1.7 → 1.1.8-rc.2

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 (261) hide show
  1. package/bin/.testspectra-runner.hash +1 -1
  2. package/bin/spectra.js +0 -0
  3. package/bin/testspectra-runner.exe +0 -0
  4. package/demo-app/assets/index-BhlW-eXG.css +1 -0
  5. package/demo-app/assets/index-DOtRypCa.js +154 -0
  6. package/demo-app/index.html +2 -2
  7. package/dist/.tsbuildinfo +1 -1
  8. package/dist/commands/__tests__/run-e2e.test.js +270 -29
  9. package/dist/commands/__tests__/run.test.js +92 -16
  10. package/dist/commands/demo.js +17 -7
  11. package/dist/commands/init.d.ts +1 -1
  12. package/dist/commands/init.js +154 -149
  13. package/dist/commands/lint.d.ts +5 -0
  14. package/dist/commands/lint.js +55 -0
  15. package/dist/commands/refactor.d.ts +2 -1
  16. package/dist/commands/refactor.js +129 -35
  17. package/dist/commands/run.d.ts +2 -3
  18. package/dist/commands/run.js +93 -28
  19. package/dist/commands/skills.d.ts +9 -0
  20. package/dist/commands/skills.js +28 -0
  21. package/dist/commands/sync-types.js +1 -0
  22. package/dist/commands/test.d.ts +17 -0
  23. package/dist/commands/test.js +104 -0
  24. package/dist/commands/watch.js +9 -3
  25. package/dist/config/loader.js +1 -1
  26. package/dist/config/schema.d.ts +5 -114
  27. package/dist/config/schema.js +1 -17
  28. package/dist/generator/__tests__/tsconfig-isolation.test.js +6 -2
  29. package/dist/generator/__tests__/type-generator.test.js +1 -1
  30. package/dist/generator/tsconfig-generator.js +44 -16
  31. package/dist/generator/type-generator.d.ts +24 -1
  32. package/dist/generator/type-generator.js +104 -8
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +76 -40
  35. package/dist/linter/__tests__/anonymous-export.test.js +184 -0
  36. package/dist/linter/__tests__/component-test-imports.test.d.ts +1 -0
  37. package/dist/linter/__tests__/component-test-imports.test.js +115 -0
  38. package/dist/linter/__tests__/shared-lib-boundary.test.d.ts +1 -0
  39. package/dist/linter/__tests__/shared-lib-boundary.test.js +270 -0
  40. package/dist/linter/__tests__/spec-schema.test.d.ts +1 -0
  41. package/dist/linter/__tests__/spec-schema.test.js +369 -0
  42. package/dist/linter/__tests__/step-parity.test.d.ts +1 -0
  43. package/dist/linter/__tests__/step-parity.test.js +372 -0
  44. package/dist/linter/discovery.d.ts +30 -0
  45. package/dist/linter/discovery.js +249 -0
  46. package/dist/linter/extractor.d.ts +67 -0
  47. package/dist/linter/extractor.js +245 -0
  48. package/dist/linter/index.d.ts +7 -0
  49. package/dist/linter/index.js +7 -0
  50. package/dist/linter/parser.d.ts +24 -0
  51. package/dist/linter/parser.js +219 -0
  52. package/dist/linter/schemas/spec.schema.d.ts +83 -0
  53. package/dist/linter/schemas/spec.schema.js +150 -0
  54. package/dist/linter/schemas/suite.schema.d.ts +59 -0
  55. package/dist/linter/schemas/suite.schema.js +83 -0
  56. package/dist/linter/spec-linter.d.ts +23 -0
  57. package/dist/linter/spec-linter.js +630 -0
  58. package/dist/linter/types.d.ts +28 -0
  59. package/dist/linter/types.js +1 -0
  60. package/dist/refactor/__tests__/refactor-engine.test.js +698 -0
  61. package/dist/refactor/refactor-engine.d.ts +25 -0
  62. package/dist/refactor/refactor-engine.js +374 -42
  63. package/dist/reporter/semantic-formatter.js +22 -2
  64. package/dist/reporter/types.d.ts +1 -1
  65. package/dist/runner/bridge.d.ts +1 -1
  66. package/dist/runner/bridge.js +34 -21
  67. package/dist/runner/reporter.d.ts +2 -0
  68. package/dist/runner/reporter.js +11 -3
  69. package/dist/utils/__tests__/api-server.test.d.ts +1 -0
  70. package/dist/utils/__tests__/api-server.test.js +62 -0
  71. package/dist/utils/__tests__/demo-server.test.d.ts +1 -0
  72. package/dist/utils/__tests__/demo-server.test.js +35 -0
  73. package/dist/utils/api-server.d.ts +19 -0
  74. package/dist/utils/api-server.js +179 -0
  75. package/dist/utils/demo-server.d.ts +5 -0
  76. package/dist/utils/demo-server.js +8 -1
  77. package/package.json +29 -25
  78. package/templates/default/CLAUDE.md +16 -11
  79. package/templates/default/global-hooks/after/mobile.hook.ts +3 -3
  80. package/templates/default/global-hooks/after/web.hook.ts +3 -3
  81. package/templates/default/global-hooks/before/mobile.hook.ts +3 -3
  82. package/templates/default/global-hooks/before/web.hook.ts +3 -3
  83. package/templates/default/package.json +3 -3
  84. package/templates/default/page-objects/AuthPage/mobile.ts +5 -0
  85. package/templates/default/page-objects/AuthPage/web.ts +10 -0
  86. package/templates/default/page-objects/MatchersPage/mobile.ts +70 -46
  87. package/templates/default/page-objects/MatchersPage/web.ts +43 -37
  88. package/templates/default/page-objects/NavigationPage/mobile.ts +14 -14
  89. package/templates/default/page-objects/NavigationPage/web.ts +8 -8
  90. package/templates/default/page-objects/PlaygroundPage/mobile.ts +42 -36
  91. package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
  92. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -11
  93. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -12
  94. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -27
  95. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -0
  96. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -0
  97. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -16
  98. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -12
  99. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -24
  100. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -0
  101. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -0
  102. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -0
  103. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -0
  104. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -0
  105. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -0
  106. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -0
  107. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -0
  108. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -0
  109. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -0
  110. package/templates/default/specs/ApiInterception/suite.md +10 -9
  111. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -0
  112. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -0
  113. package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -0
  114. package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -0
  115. package/templates/default/specs/ApiSeeding/suite.md +9 -0
  116. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -0
  117. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -0
  118. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -0
  119. package/templates/default/specs/Authentication/suite.md +9 -0
  120. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +9 -9
  121. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +17 -12
  122. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  123. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -0
  124. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -0
  125. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -0
  126. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -0
  127. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -12
  128. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +11 -11
  129. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +17 -12
  130. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  131. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -0
  132. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -0
  133. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -0
  134. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -0
  135. package/templates/default/specs/DeepLink/suite.md +9 -0
  136. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +11 -11
  137. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +17 -12
  138. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  139. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +29 -29
  140. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +19 -12
  141. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  142. package/templates/default/specs/ElementMatchers/suite.md +10 -9
  143. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
  144. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
  145. package/templates/default/specs/EnvironmentConfig/suite.md +9 -0
  146. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -0
  147. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -0
  148. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -0
  149. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -0
  150. package/templates/default/specs/PermissionRationale/suite.md +9 -0
  151. package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +6 -6
  152. package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +18 -12
  153. package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  154. package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +6 -6
  155. package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +18 -12
  156. package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  157. package/templates/default/spectra.config.ts +52 -68
  158. package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -4
  159. package/templates/default/support/actions/fillCredentials/web.action.ts +4 -4
  160. package/templates/default/support/actions/seedSession/mobile.action.ts +9 -0
  161. package/templates/default/support/actions/seedSession/web.action.ts +13 -0
  162. package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +5 -5
  163. package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  164. package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +6 -6
  165. package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  166. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db +0 -0
  167. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm +0 -0
  168. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
  169. package/templates/nx/.nx/workspace-data/d/daemon.log +1577 -168
  170. package/templates/nx/.nx/workspace-data/d/server-process.json +3 -0
  171. package/templates/nx/.nx/workspace-data/file-map.json +464 -0
  172. package/templates/nx/.nx/workspace-data/lockfile.hash +1 -0
  173. package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
  174. package/templates/nx/.nx/workspace-data/parsed-lock-file.json +13485 -0
  175. package/templates/nx/.nx/workspace-data/project-graph.json +14389 -0
  176. package/templates/nx/CLAUDE.md +10 -10
  177. package/templates/nx/package.json +3 -3
  178. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -3
  179. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -27
  180. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -3
  181. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -24
  182. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -3
  183. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  184. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -3
  185. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +17 -17
  186. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -3
  187. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  188. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -3
  189. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  190. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -3
  191. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  192. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
  193. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
  194. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -0
  195. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -3
  196. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  197. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -3
  198. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  199. package/templates/nx/pnpm-lock.yaml +6663 -6663
  200. package/templates/nx/pnpm-workspace.yaml +9 -9
  201. package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +33 -33
  202. package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +91 -37
  203. package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -5
  204. package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -8
  205. package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -26
  206. package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +31 -31
  207. package/templates/nx/shared/testing/project.json +19 -19
  208. package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -4
  209. package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -4
  210. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -3
  211. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  212. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -4
  213. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  214. package/templates/nx/spectra.config.ts +52 -68
  215. package/templates/react-component/.cursorrules +17 -0
  216. package/templates/react-component/.vscode/extensions.json +5 -0
  217. package/templates/react-component/CLAUDE.md +14 -0
  218. package/templates/react-component/README.md +69 -0
  219. package/templates/react-component/fixtures/component-mock.json +12 -0
  220. package/templates/react-component/global-hooks/after/web.hook.ts +3 -0
  221. package/templates/react-component/global-hooks/before/web.hook.ts +3 -0
  222. package/templates/react-component/package.json +27 -0
  223. package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -0
  224. package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -0
  225. package/templates/react-component/page-objects/InputComponent/web.ts +13 -0
  226. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -0
  227. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -0
  228. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -0
  229. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -0
  230. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -0
  231. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -0
  232. package/templates/react-component/specs/BadgeComponent/suite.md +12 -0
  233. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -0
  234. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -0
  235. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -0
  236. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -0
  237. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -0
  238. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -0
  239. package/templates/react-component/specs/ButtonComponent/suite.md +12 -0
  240. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -0
  241. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -0
  242. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -0
  243. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -0
  244. package/templates/react-component/specs/InputComponent/suite.md +12 -0
  245. package/templates/react-component/spectra.config.ts +27 -0
  246. package/templates/react-component/src/components/Badge/Badge.tsx +60 -0
  247. package/templates/react-component/src/components/Button/Button.tsx +57 -0
  248. package/templates/react-component/src/components/Input/Input.tsx +41 -0
  249. package/templates/react-component/src/test-utils.tsx +23 -0
  250. package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -0
  251. package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -0
  252. package/templates/react-component/tsconfig.json +8 -0
  253. package/bin/testspectra-runner +0 -0
  254. package/demo-app/assets/index-DPfv5_EF.js +0 -116
  255. package/demo-app/assets/index-gIwu2EMV.css +0 -1
  256. package/dist/commands/__tests__/cloud-run.test.js +0 -93
  257. package/dist/commands/cloud-run.d.ts +0 -13
  258. package/dist/commands/cloud-run.js +0 -120
  259. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -17
  260. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -17
  261. /package/dist/{commands/__tests__/cloud-run.test.d.ts → linter/__tests__/anonymous-export.test.d.ts} +0 -0
@@ -12,7 +12,6 @@ export interface RunCommandOptions {
12
12
  workdir?: string;
13
13
  output?: string;
14
14
  demo?: boolean;
15
- baseUrl?: string;
16
15
  failed?: boolean | string;
17
16
  concurrency?: string | number;
18
17
  licenseKey?: string;
@@ -28,5 +27,5 @@ export declare function renderTimingBreakdown(performance: {
28
27
  wallClockDurationMs?: number;
29
28
  totalTests?: number;
30
29
  duration?: string;
31
- }): void;
32
- export declare function runCommand(specPath?: string, options?: RunCommandOptions): Promise<void>;
30
+ }, platform?: string): void;
31
+ export declare function runCommand(specPaths?: string[], options?: RunCommandOptions): Promise<void>;
@@ -5,6 +5,7 @@ import { RustCoreBridge } from '../runner/bridge.js';
5
5
  import { Reporter } from '../runner/reporter.js';
6
6
  import { TypeGenerator } from '../types/generator.js';
7
7
  import { DemoServer } from '../utils/demo-server.js';
8
+ import { DemoApiServer } from '../utils/api-server.js';
8
9
  function renderSummaryCard(options) {
9
10
  const width = 64;
10
11
  const borderColor = options.isPassed ? '\x1b[38;2;52;211;153m' : '\x1b[38;2;248;113;113m';
@@ -46,6 +47,11 @@ function renderSummaryCard(options) {
46
47
  const statusBadge = options.isPassed ? `${green}✓ PASSED${reset}` : `${red}✗ FAILED${reset}`;
47
48
  const passedSuites = options.passedSuites ?? (options.isPassed ? options.suiteCount : 0);
48
49
  const failedSuites = options.failedSuites ?? (options.isPassed ? 0 : options.suiteCount);
50
+ const testsPassedText = options.carriedPassed
51
+ ? `${options.passedCount > 0 ? `${green}${options.passedCount} passed${reset}` : `${options.passedCount} passed`} ${dim}(${options.carriedPassed} carried over)${reset}`
52
+ : options.passedCount > 0
53
+ ? `${green}${options.passedCount} passed${reset}`
54
+ : `${options.passedCount} passed`;
49
55
  console.log(`\n${top}`);
50
56
  console.log(headerRow(`${boldWhite}✨ TESTSPECTRA EXECUTION SUMMARY${reset}`));
51
57
  console.log(mid);
@@ -53,7 +59,7 @@ function renderSummaryCard(options) {
53
59
  console.log(row(`${dim}Suites${reset}`, `${passedSuites > 0 ? `${green}${passedSuites} passed${reset}` : `${passedSuites} passed`}, ` +
54
60
  `${failedSuites > 0 ? `${red}${failedSuites} failed${reset}` : `${failedSuites} failed`} ` +
55
61
  `${dim}(${options.suiteCount} total)${reset}`));
56
- console.log(row(`${dim}Tests${reset}`, `${options.passedCount > 0 ? `${green}${options.passedCount} passed${reset}` : `${options.passedCount} passed`}, ` +
62
+ console.log(row(`${dim}Tests${reset}`, `${testsPassedText}, ` +
57
63
  `${options.failedCount > 0 ? `${red}${options.failedCount} failed${reset}` : `${options.failedCount} failed`} ` +
58
64
  `${dim}(${options.testCount} total)${reset}`));
59
65
  console.log(row(`${dim}Platform${reset}`, `${cyan}${options.platform}${reset}`));
@@ -64,7 +70,13 @@ function renderSummaryCard(options) {
64
70
  }
65
71
  console.log(`${bot}\n`);
66
72
  }
67
- export function renderTimingBreakdown(performance) {
73
+ const TIMING_PHASE_LABELS = {
74
+ web: { boot: '1. Master Chromium Process Boot', handshake: '3. Incognito Context & CDP Handshake' },
75
+ android: { boot: '1. Android Driver Bootstrap (ADB + Agent)', handshake: '3. Device Session Handshake' },
76
+ ios: { boot: '1. iOS Driver Bootstrap (Appium)', handshake: '3. WDA Session Handshake' },
77
+ };
78
+ export function renderTimingBreakdown(performance, platform = 'web') {
79
+ const phaseLabels = TIMING_PHASE_LABELS[platform] ?? TIMING_PHASE_LABELS.web;
68
80
  const yellowBold = '\x1b[1;38;2;251;191;36m';
69
81
  const reset = '\x1b[0m';
70
82
  const borderColor = '\x1b[38;2;75;85;99m';
@@ -89,20 +101,31 @@ export function renderTimingBreakdown(performance) {
89
101
  console.log(`${borderColor}┌──────────────────────────────────────────┬───────────────────┐${reset}`);
90
102
  console.log(`${borderColor}│${reset} Execution Phase ${borderColor}│${reset} Duration / Detail ${borderColor}│${reset}`);
91
103
  console.log(`${borderColor}├──────────────────────────────────────────┼───────────────────┤${reset}`);
92
- console.log(`${borderColor}│${reset} 1. Master Chromium Process Boot ${borderColor}│${reset} ${pad(performance.masterBootLatencyMs ?? 0, 13)} ms ${borderColor}│${reset}`);
93
- console.log(`${borderColor}│${reset} 2. Worker Spawning & Runtime Overhead ${borderColor}│${reset} ${pad(performance.runnerOverheadMs ?? 0, 13)} ms ${borderColor}│${reset}`);
94
- console.log(`${borderColor}│${reset} 3. Incognito Context & CDP Handshake ${borderColor}│${reset} ${pad(contextHandshakeMs, 10)} ms (avg)${borderColor}│${reset}`);
104
+ console.log(`${borderColor}│${reset} ${padRight(phaseLabels.boot, 40)} ${borderColor}│${reset} ${pad(performance.masterBootLatencyMs ?? 0, 13)} ms ${borderColor}│${reset}`);
105
+ console.log(`${borderColor}│${reset} ${padRight('2. Worker Spawning & Runtime Overhead', 40)} ${borderColor}│${reset} ${pad(performance.runnerOverheadMs ?? 0, 13)} ms ${borderColor}│${reset}`);
106
+ console.log(`${borderColor}│${reset} ${padRight(phaseLabels.handshake, 40)} ${borderColor}│${reset} ${pad(contextHandshakeMs, 10)} ms (avg)${borderColor}│${reset}`);
95
107
  console.log(`${borderColor}│${reset} ${padRight(testLabel, 40)} ${borderColor}│${reset} ${pad(testDetail, 17)} ${borderColor}│${reset}`);
96
- console.log(`${borderColor}│${reset} 5. Context Teardown / Cleanup ${borderColor}│${reset} ${pad(performance.avgTeardownMs ?? 0, 10)} ms (avg)${borderColor}│${reset}`);
108
+ console.log(`${borderColor}│${reset} ${padRight('5. Context Teardown / Cleanup', 40)} ${borderColor}│${reset} ${pad(performance.avgTeardownMs ?? 0, 10)} ms (avg)${borderColor}│${reset}`);
97
109
  console.log(`${borderColor}├──────────────────────────────────────────┼───────────────────┤${reset}`);
98
110
  console.log(`${borderColor}│${reset} Total Wall-Clock Execution Time ${borderColor}│${reset} ${pad(wallClock, 15)} ${borderColor}│${reset}`);
99
111
  console.log(`${borderColor}└──────────────────────────────────────────┴───────────────────┘${reset}\n`);
100
112
  }
101
- export async function runCommand(specPath, options = {}) {
113
+ export async function runCommand(specPaths, options = {}) {
102
114
  const cwd = process.cwd();
103
- const effectiveSpecPath = specPath || options.spec;
115
+ // Opt-in duration logging (TESTSPECTRA_DURATION_LOG): resolve to one absolute JSONL path that
116
+ // both this host process (Rust orchestrator) and every spawned Bun worker append to, and stamp a
117
+ // run id so repeated runs can be told apart. Off by default; toggled purely via the env var.
118
+ if (process.env.TESTSPECTRA_DURATION_LOG) {
119
+ const v = process.env.TESTSPECTRA_DURATION_LOG;
120
+ const abs = v === '1' || v === 'true' ? path.join(cwd, '.testspectra', 'logs', 'duration.jsonl') : path.resolve(cwd, v);
121
+ process.env.TESTSPECTRA_DURATION_LOG = abs;
122
+ if (!process.env.TESTSPECTRA_RUN_ID)
123
+ process.env.TESTSPECTRA_RUN_ID = String(Date.now());
124
+ }
125
+ const effectiveSpecPaths = specPaths && specPaths.length > 0 ? specPaths : options.spec ? [options.spec] : undefined;
104
126
  // Ensure latest ambient types are up-to-date
105
127
  TypeGenerator.writeDeclarationFiles(cwd);
128
+ await TypeGenerator.syncEnvDeclaration(cwd);
106
129
  const home = process.env.HOME || process.env.USERPROFILE || '';
107
130
  const globalAppDataPath = path.join(home, '.testspectra');
108
131
  const appDataPath = options.workdir ? path.resolve(options.workdir) : globalAppDataPath;
@@ -135,6 +158,7 @@ export async function runCommand(specPath, options = {}) {
135
158
  }
136
159
  const config = await ConfigLoader.loadConfig(cwd);
137
160
  let demoServer = null;
161
+ let apiServer = null;
138
162
  try {
139
163
  if (options.demo) {
140
164
  demoServer = new DemoServer(5173);
@@ -146,9 +170,14 @@ export async function runCommand(specPath, options = {}) {
146
170
  catch (err) {
147
171
  console.warn(`\x1b[33m[TestSpectra] Warning: Could not start built-in demo server: ${err.message}\x1b[0m`);
148
172
  }
149
- }
150
- if (options.baseUrl) {
151
- config.webConfig.baseUrl = options.baseUrl;
173
+ apiServer = new DemoApiServer();
174
+ try {
175
+ const { url, port } = await apiServer.start();
176
+ console.log(`\x1b[36m[TestSpectra]\x1b[0m 🌐 Demo API server automatically started at: \x1b[33m${url}\x1b[0m (port ${port}, self-hosted /api/*)`);
177
+ }
178
+ catch (err) {
179
+ console.warn(`\x1b[33m[TestSpectra] Warning: Could not start built-in demo API server: ${err.message}\x1b[0m`);
180
+ }
152
181
  }
153
182
  if (options.headed) {
154
183
  config.webConfig.headless = false;
@@ -207,7 +236,7 @@ export async function runCommand(specPath, options = {}) {
207
236
  targetDevice,
208
237
  licenseKey: options.licenseKey,
209
238
  outputJsonPath,
210
- spec: effectiveSpecPath,
239
+ specs: effectiveSpecPaths,
211
240
  concurrency: userConcurrency,
212
241
  failedOnly: isFailedMode,
213
242
  failedTarget,
@@ -216,16 +245,41 @@ export async function runCommand(specPath, options = {}) {
216
245
  const relReport = path.relative(cwd, outputJsonPath);
217
246
  const networkReportFile = path.join(path.dirname(outputJsonPath), 'network-resources.json');
218
247
  const relNetworkReport = fs.existsSync(networkReportFile) ? path.relative(cwd, networkReportFile) : undefined;
248
+ let carriedOver;
249
+ if (process.env.TESTSPECTRA_CARRIED_OVER) {
250
+ try {
251
+ carriedOver = JSON.parse(process.env.TESTSPECTRA_CARRIED_OVER);
252
+ }
253
+ catch { }
254
+ }
255
+ const carriedPassed = carriedOver?.passed || 0;
256
+ const carriedDurationMs = carriedOver?.durationMs || 0;
219
257
  let suiteCount = 1;
220
258
  let passedSuites = isPassed ? 1 : 0;
221
259
  let failedSuites = isPassed ? 0 : 1;
222
- let testCount = (result.passedCount ?? 0) + (result.failedCount ?? 0);
260
+ let basePassed = result.passedCount ?? (isPassed ? 1 : 0);
261
+ let baseFailed = result.failedCount ?? (isPassed ? 0 : 1);
262
+ const totalPassed = basePassed + carriedPassed;
263
+ const totalFailed = baseFailed;
264
+ const totalTests = basePassed + baseFailed + carriedPassed;
265
+ let finalDuration = result.duration;
266
+ if (carriedDurationMs > 0) {
267
+ const match = result.duration.match(/([\d.]+)\s*(ms|s|m)/);
268
+ let runMs = 0;
269
+ if (match) {
270
+ const val = parseFloat(match[1]);
271
+ const unit = match[2];
272
+ runMs = unit === 's' ? val * 1000 : unit === 'm' ? val * 60000 : val;
273
+ }
274
+ const totalMs = runMs + carriedDurationMs;
275
+ finalDuration = totalMs < 1000 ? `${Math.round(totalMs)}ms` : `${(totalMs / 1000).toFixed(1)}s`;
276
+ }
277
+ const overallPassed = isPassed && totalFailed === 0;
223
278
  if (fs.existsSync(outputJsonPath)) {
224
279
  try {
225
280
  const fullReport = JSON.parse(fs.readFileSync(outputJsonPath, 'utf8'));
226
281
  if (fullReport.summary) {
227
282
  suiteCount = fullReport.summary.totalSuites ?? 1;
228
- testCount = fullReport.summary.totalTests ?? testCount;
229
283
  }
230
284
  if (fullReport.suites && Array.isArray(fullReport.suites)) {
231
285
  failedSuites = fullReport.suites.filter((s) => (typeof s.failed === 'number' && s.failed > 0) ||
@@ -235,16 +289,17 @@ export async function runCommand(specPath, options = {}) {
235
289
  }
236
290
  if (fullReport.performance) {
237
291
  renderSummaryCard({
238
- isPassed,
239
- statusText: isPassed ? 'PASSED' : 'FAILED',
240
- duration: result.duration,
292
+ isPassed: overallPassed,
293
+ statusText: overallPassed ? 'PASSED' : 'FAILED',
294
+ duration: finalDuration,
241
295
  platform,
242
296
  suiteCount,
243
297
  passedSuites,
244
298
  failedSuites,
245
- testCount,
246
- passedCount: result.passedCount ?? (isPassed ? testCount : 0),
247
- failedCount: result.failedCount ?? (isPassed ? 0 : testCount),
299
+ testCount: totalTests,
300
+ passedCount: totalPassed,
301
+ failedCount: totalFailed,
302
+ carriedPassed,
248
303
  reportPath: relReport,
249
304
  networkReportPath: relNetworkReport,
250
305
  });
@@ -252,8 +307,11 @@ export async function runCommand(specPath, options = {}) {
252
307
  ...fullReport.performance,
253
308
  wallClockDurationMs: fullReport.wallClockDurationMs,
254
309
  duration: fullReport.duration,
255
- totalTests: fullReport.summary?.totalTests ?? testCount,
256
- });
310
+ totalTests: fullReport.summary?.totalTests ?? totalTests,
311
+ }, platform);
312
+ if (!overallPassed || totalFailed > 0) {
313
+ process.exitCode = 1;
314
+ }
257
315
  return;
258
316
  }
259
317
  }
@@ -262,19 +320,23 @@ export async function runCommand(specPath, options = {}) {
262
320
  }
263
321
  }
264
322
  renderSummaryCard({
265
- isPassed,
266
- statusText: isPassed ? 'PASSED' : 'FAILED',
267
- duration: result.duration,
323
+ isPassed: overallPassed,
324
+ statusText: overallPassed ? 'PASSED' : 'FAILED',
325
+ duration: finalDuration,
268
326
  platform,
269
327
  suiteCount,
270
328
  passedSuites,
271
329
  failedSuites,
272
- testCount,
273
- passedCount: result.passedCount ?? (isPassed ? testCount : 0),
274
- failedCount: result.failedCount ?? (isPassed ? 0 : testCount),
330
+ testCount: totalTests,
331
+ passedCount: totalPassed,
332
+ failedCount: totalFailed,
333
+ carriedPassed,
275
334
  reportPath: relReport,
276
335
  networkReportPath: relNetworkReport,
277
336
  });
337
+ if (!overallPassed || totalFailed > 0) {
338
+ process.exitCode = 1;
339
+ }
278
340
  }
279
341
  catch (e) {
280
342
  console.error(`\x1b[31m[TestSpectra] Execution error:\x1b[0m`, e);
@@ -284,5 +346,8 @@ export async function runCommand(specPath, options = {}) {
284
346
  if (demoServer) {
285
347
  await demoServer.stop();
286
348
  }
349
+ if (apiServer) {
350
+ await apiServer.stop();
351
+ }
287
352
  }
288
353
  }
@@ -0,0 +1,9 @@
1
+ export interface SkillsCommandOptions {
2
+ cwd?: string;
3
+ agent?: string;
4
+ }
5
+ export declare function skillsInitCommand(options?: SkillsCommandOptions): Promise<void>;
6
+ export declare function skillsListCommand(options?: SkillsCommandOptions): void;
7
+ export declare function skillsAddCommand(ids?: string[], options?: SkillsCommandOptions): Promise<void>;
8
+ export declare function skillsUpdateCommand(ids?: string[], options?: SkillsCommandOptions): void;
9
+ export declare function skillsRemoveCommand(ids?: string[], options?: SkillsCommandOptions): void;
@@ -0,0 +1,28 @@
1
+ import path from 'path';
2
+ import chalk from 'chalk';
3
+ import { initSkills, listSkills, addSkills, updateSkills, removeSkills, getKnownAgentIds, resolveAgentSelection, } from '@testspectra/skills';
4
+ function resolveBaseDir(options = {}) {
5
+ return options.cwd ? path.resolve(options.cwd) : process.cwd();
6
+ }
7
+ function resolveAgentIds(options = {}) {
8
+ const { ids, unknown } = resolveAgentSelection(options.agent);
9
+ if (unknown.length > 0) {
10
+ console.log(chalk.yellow(`⚠️ Unknown --agent value(s): ${unknown.join(', ')}. Known ids: ${getKnownAgentIds().join(', ')}`));
11
+ }
12
+ return ids.length > 0 ? ids : undefined;
13
+ }
14
+ export async function skillsInitCommand(options = {}) {
15
+ await initSkills(resolveBaseDir(options));
16
+ }
17
+ export function skillsListCommand(options = {}) {
18
+ listSkills(resolveBaseDir(options), resolveAgentIds(options));
19
+ }
20
+ export async function skillsAddCommand(ids = [], options = {}) {
21
+ await addSkills(ids, resolveBaseDir(options), resolveAgentIds(options));
22
+ }
23
+ export function skillsUpdateCommand(ids = [], options = {}) {
24
+ updateSkills(ids, resolveBaseDir(options), resolveAgentIds(options));
25
+ }
26
+ export function skillsRemoveCommand(ids = [], options = {}) {
27
+ removeSkills(ids, resolveBaseDir(options), resolveAgentIds(options));
28
+ }
@@ -5,6 +5,7 @@ export async function syncTypesCommand(options = {}) {
5
5
  const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
6
6
  try {
7
7
  TypeGenerator.generateAll(cwd);
8
+ await TypeGenerator.syncEnvDeclaration(cwd);
8
9
  console.log(chalk.green('✔ Successfully synchronized TestSpectra ambient type declarations in .testspectra/types/'));
9
10
  }
10
11
  catch (err) {
@@ -0,0 +1,17 @@
1
+ export interface TestCommandOptions {
2
+ cwd?: string;
3
+ headed?: boolean;
4
+ headless?: boolean;
5
+ stepDelay?: string | number;
6
+ delay?: string | number;
7
+ spec?: string;
8
+ output?: string;
9
+ concurrency?: string | number;
10
+ }
11
+ /**
12
+ * `spectra test` — delegates to `@testspectra/react`'s bundled Vitest browser-mode runner for
13
+ * component testing. Deliberately separate from `spectra run` (E2E via core/orchestrator): see
14
+ * docs/v2/features/component-testing/react/README.md §2 for why these are two independent
15
+ * runners rather than one extended pipeline.
16
+ */
17
+ export declare function testCommand(options?: TestCommandOptions): Promise<void>;
@@ -0,0 +1,104 @@
1
+ import path from 'node:path';
2
+ import chalk from 'chalk';
3
+ import * as p from '@clack/prompts';
4
+ import { renderSummaryView } from '../reporter/summary-view.js';
5
+ import { Reporter } from '../runner/reporter.js';
6
+ import { runSystemChecks } from './doctor.js';
7
+ /**
8
+ * Finds the Chrome-for-Testing binary that `spectra doctor` already installs/manages for the
9
+ * E2E CDP driver (`core/driver-cdp`) — same install, same binary. `@testspectra/react` must not
10
+ * contain any browser-discovery logic of its own (see the package-level comment in
11
+ * tools/react/vitest.config.ts): this is the one place that resolves the path, passed down to
12
+ * the bundled Vitest/Playwright runner as a plain argument (env var), so component testing never
13
+ * requires a separate `playwright install` download alongside TestSpectra's own browser.
14
+ */
15
+ async function resolveBrowserExecutable(cwd) {
16
+ try {
17
+ const { dependencies } = await runSystemChecks(cwd);
18
+ const chrome = dependencies.find((d) => d.category === 'web' && (d.name.includes('Chrome') || d.name.includes('Chromium')));
19
+ return chrome?.installed && chrome.location ? chrome.location : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ /**
26
+ * `spectra test` — delegates to `@testspectra/react`'s bundled Vitest browser-mode runner for
27
+ * component testing. Deliberately separate from `spectra run` (E2E via core/orchestrator): see
28
+ * docs/v2/features/component-testing/react/README.md §2 for why these are two independent
29
+ * runners rather than one extended pipeline.
30
+ */
31
+ export async function testCommand(options = {}) {
32
+ const cwd = options.cwd ?? process.cwd();
33
+ let reactPkg;
34
+ try {
35
+ reactPkg = await import('@testspectra/react');
36
+ }
37
+ catch {
38
+ p.log.error(chalk.red('✖ @testspectra/react is not installed in this workspace.'));
39
+ p.log.info(chalk.dim('Component testing requires the @testspectra/react package.'));
40
+ p.log.info(chalk.dim(' 👉 Install it: pnpm add -D @testspectra/react'));
41
+ process.exit(1);
42
+ return;
43
+ }
44
+ const browserExecutablePath = await resolveBrowserExecutable(cwd);
45
+ if (!browserExecutablePath) {
46
+ p.log.error(chalk.red('✖ TestSpectra-managed Chrome for Testing was not found.'));
47
+ p.log.info(chalk.dim('Component testing reuses the same browser install as `spectra run` (web).'));
48
+ p.log.info(chalk.dim(' 👉 Install it: spectra doctor --fix'));
49
+ process.exit(1);
50
+ return;
51
+ }
52
+ let stepDelayMs;
53
+ const rawDelay = options.stepDelay ?? options.delay;
54
+ if (rawDelay !== undefined) {
55
+ const delayNum = parseInt(String(rawDelay), 10);
56
+ if (!isNaN(delayNum) && delayNum >= 0) {
57
+ stepDelayMs = delayNum;
58
+ }
59
+ }
60
+ let headless = true;
61
+ if (options.headed) {
62
+ headless = false;
63
+ }
64
+ else if (options.headless !== undefined) {
65
+ headless = options.headless;
66
+ }
67
+ const concurrency = options.concurrency !== undefined ? parseInt(String(options.concurrency), 10) : undefined;
68
+ const outputFile = options.output ? path.resolve(cwd, options.output) : undefined;
69
+ // Reuses the exact same semantic terminal reporter `spectra run` drives (see
70
+ // docs/v2/cli/matchers-semantic-reporter.md) — @testspectra/react emits the identical
71
+ // [TESTSPECTRA_*] protocol lines the E2E Rust orchestrator does (see tools/react/src/prelude.ts
72
+ // / setup.ts), so the same Reporter class parses them with zero changes on either side.
73
+ const reporter = new Reporter();
74
+ const runStartedAt = Date.now();
75
+ const ok = await reactPkg.runComponentTests({
76
+ cwd,
77
+ headless,
78
+ stepDelayMs,
79
+ spec: options.spec,
80
+ outputFile,
81
+ concurrency: concurrency !== undefined && !isNaN(concurrency) ? concurrency : undefined,
82
+ browserExecutablePath,
83
+ onLine: (line) => {
84
+ if (!line.startsWith('[TESTSPECTRA_'))
85
+ return;
86
+ reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message: line });
87
+ if (!reporter.isInteractiveMode()) {
88
+ console.log(line);
89
+ }
90
+ },
91
+ });
92
+ const durationMs = Date.now() - runStartedAt;
93
+ const result = reporter.generateResult(ok ? 'passed' : 'failed', `${(durationMs / 1000).toFixed(1)}s`);
94
+ const totalTests = result.passedCount + result.failedCount;
95
+ renderSummaryView({
96
+ totalSuites: totalTests,
97
+ totalTests,
98
+ passedCount: result.passedCount,
99
+ failedCount: result.failedCount,
100
+ durationMs,
101
+ reportLocation: outputFile ? path.relative(cwd, outputFile) : '(use -o/--output to save a report)',
102
+ });
103
+ process.exit(ok ? 0 : 1);
104
+ }
@@ -3,15 +3,22 @@ import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { TypeGenerator } from '../types/generator.js';
5
5
  export function startWatcher(cwd, debounceMs = 60) {
6
- // Initial generation
6
+ // Initial generation. TypeGenerator.generateAll itself stays synchronous (reading
7
+ // spectra.config.ts is unavoidably async, so that part is handled by the separate
8
+ // syncEnvDeclaration call below); startWatcher stays sync so it can return the watcher handle
9
+ // immediately, with the async env sync fired-and-forgotten.
7
10
  TypeGenerator.generateAll(cwd);
11
+ TypeGenerator.syncEnvDeclaration(cwd).catch((err) => {
12
+ console.error(chalk.red(`✖ Error generating initial env.d.ts: ${err?.message}`));
13
+ });
8
14
  let debounceTimer = null;
9
15
  const triggerRegen = (filePath, eventType) => {
10
16
  if (debounceTimer)
11
17
  clearTimeout(debounceTimer);
12
- debounceTimer = setTimeout(() => {
18
+ debounceTimer = setTimeout(async () => {
13
19
  try {
14
20
  TypeGenerator.generateAll(cwd);
21
+ await TypeGenerator.syncEnvDeclaration(cwd);
15
22
  const time = new Date().toLocaleTimeString();
16
23
  const rel = path.relative(cwd, filePath) || filePath;
17
24
  console.log(chalk.gray(`[${time}] `) + chalk.cyan(`✨ Updated ambient types `) + chalk.gray(`(${eventType}: ${rel})`));
@@ -34,7 +41,6 @@ export function startWatcher(cwd, debounceMs = 60) {
34
41
  }
35
42
  if (fn.includes('fixtures') ||
36
43
  fn.includes('page-objects') ||
37
- fn.includes('pageobjects') ||
38
44
  fn.includes('actions') ||
39
45
  fn.includes('steps') ||
40
46
  fn.includes('hooks') ||
@@ -89,7 +89,7 @@ export class ConfigLoader {
89
89
  environmentVariables: overrides?.executionConfig?.environmentVariables ??
90
90
  loaded.executionConfig?.environmentVariables ??
91
91
  DEFAULT_CONFIG_DATA.executionConfig?.environmentVariables ??
92
- [],
92
+ {},
93
93
  },
94
94
  };
95
95
  }
@@ -20,19 +20,11 @@ export interface WebConfig {
20
20
  headless: boolean | 'new';
21
21
  /**
22
22
  * Implicit wait timeout (in milliseconds) for finding elements before throwing an error.
23
+ * Also the ceiling auto-waiting element assertions/actions poll against (capped at 2000ms for
24
+ * assertions — see `getAssertionTimeoutMs()`).
23
25
  * @default "5000"
24
26
  */
25
27
  implicitWait: string;
26
- /**
27
- * Maximum time (in milliseconds) allowed for a webpage to complete navigation and initial load.
28
- * @default "30000"
29
- */
30
- pageLoadTimeout: string;
31
- /**
32
- * Maximum time (in milliseconds) allowed for asynchronous scripts to finish executing.
33
- * @default "30000"
34
- */
35
- scriptTimeout: string;
36
28
  /**
37
29
  * Delay between consecutive actions and commands in milliseconds (slow motion).
38
30
  * @default 0
@@ -46,37 +38,6 @@ export interface WebConfig {
46
38
  */
47
39
  parallelizationMode: 'testcase' | 'suite';
48
40
  }
49
- /**
50
- * Browser target definition for web test execution.
51
- */
52
- export interface Browser {
53
- /**
54
- * Unique identifier for this browser configuration.
55
- */
56
- id: string;
57
- /**
58
- * Browser type/engine: `"chrome"`, `"firefox"`, `"safari"`, `"edge"`.
59
- * @default "chrome"
60
- */
61
- type: string;
62
- /**
63
- * Whether to simulate mobile viewport and touch metrics.
64
- * @default false
65
- */
66
- mobileEmulation: boolean;
67
- /**
68
- * Emulated device name when `mobileEmulation` is true (e.g. `"iPhone 14"`, `"Pixel 7"`).
69
- */
70
- deviceName?: string;
71
- /**
72
- * Custom browser window width in pixels.
73
- */
74
- width?: string;
75
- /**
76
- * Custom browser window height in pixels.
77
- */
78
- height?: string;
79
- }
80
41
  /**
81
42
  * Target Android device specification for multi-device parallel execution.
82
43
  */
@@ -156,11 +117,6 @@ export interface AndroidConfig {
156
117
  * Main activity name launched when starting the app.
157
118
  */
158
119
  appActivity: string;
159
- /**
160
- * Whether to automatically grant Android runtime permissions upon installation.
161
- * @default true
162
- */
163
- autoGrantPermissions: boolean;
164
120
  /**
165
121
  * Whether to preserve app state and prevent clearing app data/cache between sessions.
166
122
  * @default false
@@ -265,40 +221,6 @@ export interface IosConfig {
265
221
  */
266
222
  parallelizationMode: 'testcase' | 'suite';
267
223
  }
268
- /**
269
- * Load testing stage ramp-up profile.
270
- */
271
- export interface LoadStage {
272
- /**
273
- * Stage identifier.
274
- */
275
- id: string;
276
- /**
277
- * Duration for this load stage (e.g. `"30s"`, `"2m"`).
278
- */
279
- duration: string;
280
- /**
281
- * Target virtual users (VUs) to scale to during this stage.
282
- */
283
- targetVUs: string;
284
- }
285
- /**
286
- * Performance and pass/fail metric threshold definition.
287
- */
288
- export interface SuccessThreshold {
289
- /**
290
- * Unique threshold identifier.
291
- */
292
- id: string;
293
- /**
294
- * Performance metric type (e.g. `"http_req_duration"`, `"http_req_failed"`).
295
- */
296
- metricType: string;
297
- /**
298
- * Maximum acceptable value (e.g. `"500ms"`, `"0.01"`).
299
- */
300
- maxValue: string;
301
- }
302
224
  /**
303
225
  * Specific domain filter for real-time network interception and recording.
304
226
  */
@@ -312,19 +234,6 @@ export interface MonitoredDomain {
312
234
  */
313
235
  enabled: boolean;
314
236
  }
315
- /**
316
- * Custom environment variable key-value pair injected into the test execution sandbox.
317
- */
318
- export interface EnvironmentVariable {
319
- /**
320
- * Environment variable name (e.g. `"API_SECRET_KEY"`).
321
- */
322
- key: string;
323
- /**
324
- * Environment variable value.
325
- */
326
- value: string;
327
- }
328
237
  /**
329
238
  * Test execution, CDP network monitoring, and sandbox environment configuration.
330
239
  */
@@ -349,9 +258,10 @@ export interface ExecutionConfig {
349
258
  */
350
259
  monitoredDomains?: MonitoredDomain[];
351
260
  /**
352
- * Custom environment variables injected into the test process runtime.
261
+ * Custom environment variables injected into the test process runtime
262
+ * (e.g. `{ API_SECRET_KEY: "..." }`).
353
263
  */
354
- environmentVariables?: EnvironmentVariable[];
264
+ environmentVariables?: Record<string, string>;
355
265
  }
356
266
  /**
357
267
  * Root configuration data structure matching TestSpectra project schema.
@@ -361,10 +271,6 @@ export interface ConfigData {
361
271
  * Web browser execution settings.
362
272
  */
363
273
  webConfig: WebConfig;
364
- /**
365
- * List of target browsers for web automation runs.
366
- */
367
- browsers: Browser[];
368
274
  /**
369
275
  * Android Appium driver and device options.
370
276
  */
@@ -373,21 +279,6 @@ export interface ConfigData {
373
279
  * iOS Appium driver and device options.
374
280
  */
375
281
  iosConfig: IosConfig;
376
- /**
377
- * Load testing parameters.
378
- */
379
- loadConfig: {
380
- virtualUsers: string;
381
- duration: string;
382
- };
383
- /**
384
- * Load testing stages.
385
- */
386
- loadStages: LoadStage[];
387
- /**
388
- * Pass/fail metric thresholds.
389
- */
390
- thresholds: SuccessThreshold[];
391
282
  /**
392
283
  * Advanced execution and network recording settings.
393
284
  */