@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
@@ -25,22 +25,12 @@ export const DEFAULT_CONFIG_DATA = {
25
25
  maxConcurrentSessions: '1',
26
26
  headless: true,
27
27
  implicitWait: '5000',
28
- pageLoadTimeout: '30000',
29
- scriptTimeout: '30000',
30
28
  parallelizationMode: 'testcase',
31
29
  },
32
- browsers: [
33
- {
34
- id: 'chrome-desktop',
35
- type: 'chrome',
36
- mobileEmulation: false,
37
- },
38
- ],
39
30
  androidConfig: {
40
31
  driverServer: 'http://127.0.0.1:4800',
41
32
  appPackage: '',
42
33
  appActivity: '',
43
- autoGrantPermissions: true,
44
34
  noReset: false,
45
35
  implicitWait: 10000,
46
36
  parallelizationMode: 'suite',
@@ -71,17 +61,11 @@ export const DEFAULT_CONFIG_DATA = {
71
61
  },
72
62
  ],
73
63
  },
74
- loadConfig: {
75
- virtualUsers: '10',
76
- duration: '1m',
77
- },
78
- loadStages: [],
79
- thresholds: [],
80
64
  executionConfig: {
81
65
  networkMonitoringEnabled: true,
82
66
  fastResponseTime: '200',
83
67
  normalResponseTime: '1000',
84
68
  monitoredDomains: [],
85
- environmentVariables: [],
69
+ environmentVariables: {},
86
70
  },
87
71
  };
@@ -261,7 +261,7 @@ describe('Platform TSConfigs & Type Isolation (docs/v2/cli/tsconfig-platform-iso
261
261
  expect(threw).toBe(true);
262
262
  expect(errorMessage).toContain("Cannot find name 'AuthLoginPage'");
263
263
  });
264
- it('E2E: compiles the full Nx monorepo template via tsc -b with 0 errors', () => {
264
+ it('E2E: compiles the full Nx monorepo template via tsc -b with 0 errors', async () => {
265
265
  let root = process.cwd();
266
266
  while (root && root !== path.dirname(root)) {
267
267
  if (fs.existsSync(path.join(root, 'pnpm-workspace.yaml')))
@@ -269,8 +269,12 @@ describe('Platform TSConfigs & Type Isolation (docs/v2/cli/tsconfig-platform-iso
269
269
  root = path.dirname(root);
270
270
  }
271
271
  const nxTemplateDir = path.join(root, 'cli', 'templates', 'nx');
272
- // Generate latest ambient types for the nx template
272
+ // Generate latest ambient types for the nx template, including env.d.ts (a separate async
273
+ // step — see TypeGenerator.syncEnvDeclaration — since spectra.config.ts's
274
+ // executionConfig.environmentVariables is what the template's EnvironmentConfig suite
275
+ // exercises via the typed Env global).
273
276
  TypeGenerator.generateAll(nxTemplateDir);
277
+ await TypeGenerator.syncEnvDeclaration(nxTemplateDir);
274
278
  expect(() => {
275
279
  execSync(`npx tsc -b ${nxTemplateDir}`, { stdio: 'pipe' });
276
280
  }).not.toThrow();
@@ -52,7 +52,7 @@ describe('TypeGenerator Engine (docs/v2/cli/ambient-types-generator.md)', () =>
52
52
  fs.writeFileSync(path.join(poDir, 'web.ts'), 'export default class LoginPage {\n static username = "#user";\n static async login() {}\n}');
53
53
  const stepDir = path.join(tmpDir, 'support', 'steps', 'loginAsAdmin');
54
54
  fs.mkdirSync(stepDir, { recursive: true });
55
- fs.writeFileSync(path.join(stepDir, 'web.step.ts'), 'export default async function loginAsAdmin(role?: string) {}');
55
+ fs.writeFileSync(path.join(stepDir, 'web.step.ts'), 'export default async function (role?: string) {}');
56
56
  const fixDir = path.join(tmpDir, 'fixtures');
57
57
  fs.mkdirSync(fixDir, { recursive: true });
58
58
  fs.writeFileSync(path.join(fixDir, 'users.json'), JSON.stringify({ admin: { id: 101, email: 'admin@test.local' } }));
@@ -31,7 +31,7 @@ export class TsConfigGenerator {
31
31
  static getStemExcludes(prefix, stems) {
32
32
  const excludes = [];
33
33
  for (const stem of stems) {
34
- excludes.push(`${prefix}/**/page-objects/**/${stem}.ts`, `${prefix}/**/pageobjects/**/${stem}.ts`, `${prefix}/**/support/**/${stem}.*`, `${prefix}/**/global-hooks/**/${stem}.*`, `${prefix}/**/specs/**/hooks/**/${stem}.*`, `${prefix}/**/specs/**/${stem}.test.ts`, `${prefix}/**/${stem}.ts`, `${prefix}/**/${stem}.*`, `${prefix}/**/${stem}.*.ts`, `${prefix}/**/*.${stem}.ts`, `${prefix}/**/*.${stem}.*`, `${prefix}/**/*.${stem}.*.ts`);
34
+ excludes.push(`${prefix}/**/page-objects/**/${stem}.ts`, `${prefix}/**/support/**/${stem}.*`, `${prefix}/**/global-hooks/**/${stem}.*`, `${prefix}/**/specs/**/hooks/**/${stem}.*`, `${prefix}/**/specs/**/${stem}.test.ts`, `${prefix}/**/${stem}.ts`, `${prefix}/**/${stem}.*`, `${prefix}/**/${stem}.*.ts`, `${prefix}/**/*.${stem}.ts`, `${prefix}/**/*.${stem}.*`, `${prefix}/**/*.${stem}.*.ts`);
35
35
  }
36
36
  return excludes;
37
37
  }
@@ -41,21 +41,28 @@ export class TsConfigGenerator {
41
41
  target: 'ES2022',
42
42
  module: 'NodeNext',
43
43
  moduleResolution: 'NodeNext',
44
+ jsx: 'react-jsx',
44
45
  composite: true,
45
46
  noEmit: true,
46
47
  skipLibCheck: true,
47
48
  strict: true,
48
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
49
+ types: ['node'],
49
50
  },
50
51
  include: [
51
52
  '../types/web.d.ts',
52
53
  '../types/fixtures.d.ts',
53
54
  '../../**/specs/**/*.ts',
55
+ '../../**/specs/**/*.tsx',
54
56
  '../../**/page-objects/**/*.ts',
55
- '../../**/pageobjects/**/*.ts',
57
+ '../../**/page-objects/**/*.tsx',
56
58
  '../../**/support/**/*.ts',
59
+ '../../**/support/**/*.tsx',
57
60
  '../../**/global-hooks/**/*.ts',
61
+ '../../**/global-hooks/**/*.tsx',
58
62
  '../../**/fixtures/**/*.ts',
63
+ '../../**/fixtures/**/*.json',
64
+ '../../**/src/**/*.ts',
65
+ '../../**/src/**/*.tsx',
59
66
  '../../spectra.config.ts',
60
67
  ],
61
68
  exclude: [
@@ -74,21 +81,28 @@ export class TsConfigGenerator {
74
81
  target: 'ES2022',
75
82
  module: 'NodeNext',
76
83
  moduleResolution: 'NodeNext',
84
+ jsx: 'react-jsx',
77
85
  composite: true,
78
86
  noEmit: true,
79
87
  skipLibCheck: true,
80
88
  strict: true,
81
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
89
+ types: ['node'],
82
90
  },
83
91
  include: [
84
92
  '../types/mobile.d.ts',
85
93
  '../types/fixtures.d.ts',
86
94
  '../../**/specs/**/*.ts',
95
+ '../../**/specs/**/*.tsx',
87
96
  '../../**/page-objects/**/*.ts',
88
- '../../**/pageobjects/**/*.ts',
97
+ '../../**/page-objects/**/*.tsx',
89
98
  '../../**/support/**/*.ts',
99
+ '../../**/support/**/*.tsx',
90
100
  '../../**/global-hooks/**/*.ts',
101
+ '../../**/global-hooks/**/*.tsx',
91
102
  '../../**/fixtures/**/*.ts',
103
+ '../../**/fixtures/**/*.json',
104
+ '../../**/src/**/*.ts',
105
+ '../../**/src/**/*.tsx',
92
106
  '../../spectra.config.ts',
93
107
  ],
94
108
  exclude: [
@@ -107,21 +121,28 @@ export class TsConfigGenerator {
107
121
  target: 'ES2022',
108
122
  module: 'NodeNext',
109
123
  moduleResolution: 'NodeNext',
124
+ jsx: 'react-jsx',
110
125
  composite: true,
111
126
  noEmit: true,
112
127
  skipLibCheck: true,
113
128
  strict: true,
114
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
129
+ types: ['node'],
115
130
  },
116
131
  include: [
117
132
  '../types/android.d.ts',
118
133
  '../types/fixtures.d.ts',
119
134
  '../../**/specs/**/*.ts',
135
+ '../../**/specs/**/*.tsx',
120
136
  '../../**/page-objects/**/*.ts',
121
- '../../**/pageobjects/**/*.ts',
137
+ '../../**/page-objects/**/*.tsx',
122
138
  '../../**/support/**/*.ts',
139
+ '../../**/support/**/*.tsx',
123
140
  '../../**/global-hooks/**/*.ts',
141
+ '../../**/global-hooks/**/*.tsx',
124
142
  '../../**/fixtures/**/*.ts',
143
+ '../../**/fixtures/**/*.json',
144
+ '../../**/src/**/*.ts',
145
+ '../../**/src/**/*.tsx',
125
146
  '../../spectra.config.ts',
126
147
  ],
127
148
  exclude: [
@@ -140,21 +161,28 @@ export class TsConfigGenerator {
140
161
  target: 'ES2022',
141
162
  module: 'NodeNext',
142
163
  moduleResolution: 'NodeNext',
164
+ jsx: 'react-jsx',
143
165
  composite: true,
144
166
  noEmit: true,
145
167
  skipLibCheck: true,
146
168
  strict: true,
147
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
169
+ types: ['node'],
148
170
  },
149
171
  include: [
150
172
  '../types/ios.d.ts',
151
173
  '../types/fixtures.d.ts',
152
174
  '../../**/specs/**/*.ts',
175
+ '../../**/specs/**/*.tsx',
153
176
  '../../**/page-objects/**/*.ts',
154
- '../../**/pageobjects/**/*.ts',
177
+ '../../**/page-objects/**/*.tsx',
155
178
  '../../**/support/**/*.ts',
179
+ '../../**/support/**/*.tsx',
156
180
  '../../**/global-hooks/**/*.ts',
181
+ '../../**/global-hooks/**/*.tsx',
157
182
  '../../**/fixtures/**/*.ts',
183
+ '../../**/fixtures/**/*.json',
184
+ '../../**/src/**/*.ts',
185
+ '../../**/src/**/*.tsx',
158
186
  '../../spectra.config.ts',
159
187
  ],
160
188
  exclude: [
@@ -173,21 +201,23 @@ export class TsConfigGenerator {
173
201
  target: 'ES2022',
174
202
  module: 'NodeNext',
175
203
  moduleResolution: 'NodeNext',
204
+ jsx: 'react-jsx',
176
205
  composite: true,
177
206
  noEmit: true,
178
207
  skipLibCheck: true,
179
208
  strict: true,
180
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
209
+ types: ['node'],
181
210
  },
182
211
  include: [
183
212
  '../types/common.d.ts',
184
213
  '../types/fixtures.d.ts',
185
214
  '../../**/page-objects/**/common.ts',
186
- '../../**/pageobjects/**/common.ts',
215
+ '../../**/page-objects/**/common.tsx',
187
216
  '../../**/support/**/common.*',
188
217
  '../../**/global-hooks/**/common.*',
189
218
  '../../**/specs/**/hooks/**/common.*',
190
219
  '../../**/specs/**/common.test.ts',
220
+ '../../**/specs/**/common.test.tsx',
191
221
  '../../spectra.config.ts',
192
222
  ],
193
223
  exclude: [
@@ -215,7 +245,7 @@ export class TsConfigGenerator {
215
245
  const relCache = path.relative(subConfigDir, path.join(options.cwd, '.testspectra', 'cache')).replace(/\\/g, '/');
216
246
  const platformDts = `${relTypesDir}/${platform}.d.ts`;
217
247
  const fixturesDts = `${relTypesDir}/fixtures.d.ts`;
218
- const include = [platformDts, fixturesDts, `${relScope}/**/*.ts`, relSpectraConfig];
248
+ const include = [platformDts, fixturesDts, `${relScope}/**/*.ts`, `${relScope}/**/*.tsx`, relSpectraConfig];
219
249
  const baseExcludes = [relNodeModules, relDist, relReports, relRuns, relCache];
220
250
  const stemMap = {
221
251
  web: ['android', 'ios', 'mobile'],
@@ -234,7 +264,7 @@ export class TsConfigGenerator {
234
264
  noEmit: true,
235
265
  skipLibCheck: true,
236
266
  strict: true,
237
- types: ['node', '@wdio/globals/types', '@wdio/mocha-framework'],
267
+ types: ['node'],
238
268
  },
239
269
  include,
240
270
  exclude: [...baseExcludes, ...platformExcludes],
@@ -269,9 +299,7 @@ export class TsConfigGenerator {
269
299
  const sharedScope = scopes.find((s) => s.isShared);
270
300
  const featureScopes = scopes.filter((s) => !s.isShared);
271
301
  const hasScopes = Boolean(sharedScope || featureScopes.length > 0);
272
- const hasRootEntities = fs.existsSync(path.join(cwd, 'specs')) ||
273
- fs.existsSync(path.join(cwd, 'page-objects')) ||
274
- fs.existsSync(path.join(cwd, 'pageobjects'));
302
+ const hasRootEntities = fs.existsSync(path.join(cwd, 'specs')) || fs.existsSync(path.join(cwd, 'page-objects'));
275
303
  if (!hasScopes || hasRootEntities) {
276
304
  for (const filename of Object.keys(platformConfigs)) {
277
305
  references.push({ path: `./tsconfig/${filename}` });
@@ -18,11 +18,23 @@ export interface EntityScope {
18
18
  * - Directory with page-objects/support/fixtures and NO specs => Shared Scope
19
19
  */
20
20
  export declare function discoverEntityScopes(cwd: string): EntityScope[];
21
+ /**
22
+ * Detects a component-testing project (`@testspectra/react`'s Vitest browser-mode runner), via
23
+ * the same signal `cli/src/commands/test.ts` uses to gate `spectra test` itself. Used to augment
24
+ * the generated `Spectra` ambient type with `.mount()`, which only exists in that runtime.
25
+ */
26
+ export declare function isComponentTestingProject(cwd: string): boolean;
21
27
  export declare class TypeGenerator {
22
28
  /**
23
29
  * Main entry point to generate all ambient type declarations.
24
30
  */
25
31
  static generateAll(cwd: string): void;
32
+ /**
33
+ * Reloads `spectra.config.ts` and refreshes `.testspectra/types/env.d.ts` with the current
34
+ * `executionConfig.environmentVariables`. Separate from `writeDeclarationFiles`/`generateAll`
35
+ * (which must stay synchronous) because reading the config file is unavoidably async.
36
+ */
37
+ static syncEnvDeclaration(cwd: string): Promise<void>;
26
38
  /**
27
39
  * Discovers all feature directories and shared library directories.
28
40
  * Returns a map of scope -> base directory
@@ -30,7 +42,18 @@ export declare class TypeGenerator {
30
42
  static getEntityScopes(cwd: string): EntityScope[];
31
43
  static getPageObjectsDirsForScope(scopeDir: string, sharedDir?: string): string[];
32
44
  static getEntityDirsForScope(entityType: 'actions' | 'steps' | 'fixtures', scopeDir: string, sharedDir?: string): string[];
33
- static generateDeclarationsForScope(cwd: string, outputTypesDir: string, scopeDir: string, sharedDir?: string): Record<PlatformTarget, string>;
45
+ static generateDeclarationsForScope(cwd: string, outputTypesDir: string, scopeDir: string, sharedDir?: string, rootTypesDir?: string): Record<PlatformTarget, string>;
34
46
  static generateFixturesDeclarationForScope(cwd: string, outputTypesDir: string, scopeDir: string, sharedDir?: string): string;
47
+ /**
48
+ * Generates the typed `Spectra.env` ambient declaration from `spectra.config.ts`'s
49
+ * `executionConfig.environmentVariables` — mirrors `generateFixturesDeclarationForScope`, but
50
+ * env vars come from a single project-level config rather than a scanned directory.
51
+ *
52
+ * Emits a bare global `interface SpectraEnv { readonly KEY: string; ... }`, which TypeScript
53
+ * interface-merges with the empty `SpectraEnv` shipped by `@testspectra/matchers` (the same
54
+ * mechanism `@types/node` uses for projects to augment `NodeJS.ProcessEnv`) — so `Spectra.env.KEY`
55
+ * resolves to `string`, never `string | undefined` like raw `process.env.KEY`.
56
+ */
57
+ static generateEnvDeclaration(environmentVariables: Record<string, string>): string;
35
58
  static writeDeclarationFiles(cwd: string): void;
36
59
  }
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { ConfigLoader } from '../config/loader.js';
3
4
  import { TsConfigGenerator } from './tsconfig-generator.js';
4
5
  export const PLATFORM_HIERARCHY = {
5
6
  web: ['web', 'common'],
@@ -180,6 +181,23 @@ export function discoverEntityScopes(cwd) {
180
181
  fallbackScan(cwd, 5);
181
182
  return scopes;
182
183
  }
184
+ /**
185
+ * Detects a component-testing project (`@testspectra/react`'s Vitest browser-mode runner), via
186
+ * the same signal `cli/src/commands/test.ts` uses to gate `spectra test` itself. Used to augment
187
+ * the generated `Spectra` ambient type with `.mount()`, which only exists in that runtime.
188
+ */
189
+ export function isComponentTestingProject(cwd) {
190
+ try {
191
+ const pkgPath = path.join(cwd, 'package.json');
192
+ if (!fs.existsSync(pkgPath))
193
+ return false;
194
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
195
+ return Boolean(pkg.dependencies?.['@testspectra/react'] || pkg.devDependencies?.['@testspectra/react']);
196
+ }
197
+ catch {
198
+ return false;
199
+ }
200
+ }
183
201
  export class TypeGenerator {
184
202
  /**
185
203
  * Main entry point to generate all ambient type declarations.
@@ -188,6 +206,24 @@ export class TypeGenerator {
188
206
  this.writeDeclarationFiles(cwd);
189
207
  TsConfigGenerator.scaffoldConfigs(cwd);
190
208
  }
209
+ /**
210
+ * Reloads `spectra.config.ts` and refreshes `.testspectra/types/env.d.ts` with the current
211
+ * `executionConfig.environmentVariables`. Separate from `writeDeclarationFiles`/`generateAll`
212
+ * (which must stay synchronous) because reading the config file is unavoidably async.
213
+ */
214
+ static async syncEnvDeclaration(cwd) {
215
+ const rootTypesDir = path.join(cwd, '.testspectra', 'types');
216
+ if (!fs.existsSync(rootTypesDir)) {
217
+ fs.mkdirSync(rootTypesDir, { recursive: true });
218
+ }
219
+ let environmentVariables = {};
220
+ try {
221
+ const config = await ConfigLoader.loadConfig(cwd);
222
+ environmentVariables = config.executionConfig?.environmentVariables ?? {};
223
+ }
224
+ catch { }
225
+ fs.writeFileSync(path.join(rootTypesDir, 'env.d.ts'), this.generateEnvDeclaration(environmentVariables), 'utf-8');
226
+ }
191
227
  /**
192
228
  * Discovers all feature directories and shared library directories.
193
229
  * Returns a map of scope -> base directory
@@ -267,10 +303,11 @@ export class TypeGenerator {
267
303
  }
268
304
  return dirs;
269
305
  }
270
- static generateDeclarationsForScope(cwd, outputTypesDir, scopeDir, sharedDir) {
306
+ static generateDeclarationsForScope(cwd, outputTypesDir, scopeDir, sharedDir, rootTypesDir) {
271
307
  const poDirs = this.getPageObjectsDirsForScope(scopeDir, sharedDir);
272
308
  const actionsDirs = this.getEntityDirsForScope('actions', scopeDir, sharedDir);
273
309
  const stepsDirs = this.getEntityDirsForScope('steps', scopeDir, sharedDir);
310
+ const isComponentTesting = isComponentTestingProject(cwd);
274
311
  const platforms = ['web', 'android', 'ios', 'mobile', 'common'];
275
312
  const results = {
276
313
  web: '',
@@ -279,14 +316,39 @@ export class TypeGenerator {
279
316
  mobile: '',
280
317
  common: '',
281
318
  };
319
+ // `env.d.ts` always lives at the workspace root types dir (env vars come from a single
320
+ // `spectra.config.ts`, unlike per-scope fixtures/page-objects), so a shared/feature scope's
321
+ // own declarations reference it via a relative path back up to the root.
322
+ let envRefPath = './env.d.ts';
323
+ if (rootTypesDir && rootTypesDir !== outputTypesDir) {
324
+ envRefPath = path.relative(outputTypesDir, path.join(rootTypesDir, 'env.d.ts')).replace(/\\/g, '/');
325
+ if (!envRefPath.startsWith('.'))
326
+ envRefPath = `./${envRefPath}`;
327
+ }
282
328
  for (const platform of platforms) {
283
329
  const hierarchy = PLATFORM_HIERARCHY[platform];
284
330
  let content = `// Auto-generated ambient declarations for TestSpectra [${platform.toUpperCase()}]\n`;
285
331
  content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
286
- content += `/// <reference types="@wdio/globals/types" />\n`;
287
- content += `/// <reference types="@wdio/mocha-framework" />\n`;
332
+ // TestSpectra's Rust-native orchestrator worker (core/orchestrator/src/worker/bdd_runner.ts)
333
+ // registers its own `it`/`test`/lifecycle-hook globals directly — there is no WDIO or Mocha
334
+ // runtime involved at all (not just for component testing; this is true across every
335
+ // platform). `describe` is intentionally NOT declared here even though the runtime has a
336
+ // transparent no-op implementation of it — spec-schema-linter's no-describe-block rule
337
+ // forbids authoring it, so leaving it undeclared makes that a compile error too, not just a
338
+ // lint error.
339
+ content += `declare function it(title: string, fn: () => Promise<void> | void): void;\n`;
340
+ if (isComponentTesting) {
341
+ // Vitest's own `expect` global (test.globals: true in tools/react/vitest.config.ts) — the
342
+ // one thing component specs legitimately need beyond the Spectra.* DSL, for asserting
343
+ // plain closure/callback state (e.g. "was this onClick handler invoked") that has no DOM
344
+ // element to target. Reuses @testspectra/react's own re-exported `ExpectStatic` type
345
+ // rather than hand-duplicating Vitest's expect API surface. Not declared for E2E projects
346
+ // — the Rust orchestrator's BDD runtime has no `expect` global at all.
347
+ content += `declare const expect: import('@testspectra/react').ExpectStatic;\n`;
348
+ }
288
349
  content += `/// <reference types="@testspectra/matchers" />\n`;
289
- content += `/// <reference path="./fixtures.d.ts" />\n\n`;
350
+ content += `/// <reference path="./fixtures.d.ts" />\n`;
351
+ content += `/// <reference path="${envRefPath}" />\n\n`;
290
352
  // 1. Page Objects Global Declarations
291
353
  const declaredPOMs = new Set();
292
354
  for (const poDir of poDirs) {
@@ -423,7 +485,10 @@ export class TypeGenerator {
423
485
  }
424
486
  }
425
487
  content += `}\n`;
426
- content += `type TestSpectraActions = import('@testspectra/matchers').SpectraStatic & TestSpectraCustomActions;\n`;
488
+ // Reuses @testspectra/react's own exported `mount` type rather than hand-duplicating its
489
+ // signature here — stays in sync automatically if that signature ever changes.
490
+ const mountAugment = isComponentTesting ? ` & { mount: typeof import('@testspectra/react').mount }` : '';
491
+ content += `type TestSpectraActions = import('@testspectra/matchers').SpectraStatic & TestSpectraCustomActions${mountAugment};\n`;
427
492
  content += `declare const Spectra: TestSpectraActions;\n\n`;
428
493
  // 3. Built-in Steps Declaration
429
494
  content += `interface TestSpectraSteps {\n`;
@@ -543,11 +608,42 @@ export class TypeGenerator {
543
608
  content += `declare const Fixture: TestSpectraFixtures;\n`;
544
609
  return content;
545
610
  }
611
+ /**
612
+ * Generates the typed `Spectra.env` ambient declaration from `spectra.config.ts`'s
613
+ * `executionConfig.environmentVariables` — mirrors `generateFixturesDeclarationForScope`, but
614
+ * env vars come from a single project-level config rather than a scanned directory.
615
+ *
616
+ * Emits a bare global `interface SpectraEnv { readonly KEY: string; ... }`, which TypeScript
617
+ * interface-merges with the empty `SpectraEnv` shipped by `@testspectra/matchers` (the same
618
+ * mechanism `@types/node` uses for projects to augment `NodeJS.ProcessEnv`) — so `Spectra.env.KEY`
619
+ * resolves to `string`, never `string | undefined` like raw `process.env.KEY`.
620
+ */
621
+ static generateEnvDeclaration(environmentVariables) {
622
+ let content = `// Auto-generated ambient Spectra.env declarations for TestSpectra\n`;
623
+ content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
624
+ content += `interface SpectraEnv {\n`;
625
+ for (const key of Object.keys(environmentVariables)) {
626
+ content += ` readonly ${JSON.stringify(key)}: string;\n`;
627
+ }
628
+ content += `}\n`;
629
+ return content;
630
+ }
546
631
  static writeDeclarationFiles(cwd) {
547
632
  const rootTypesDir = path.join(cwd, '.testspectra', 'types');
548
633
  if (!fs.existsSync(rootTypesDir)) {
549
634
  fs.mkdirSync(rootTypesDir, { recursive: true });
550
635
  }
636
+ // env.d.ts always lives at the root, regardless of Nx scopes — a single spectra.config.ts's
637
+ // executionConfig.environmentVariables applies workspace-wide (see generateEnvDeclaration).
638
+ // Reading spectra.config.ts itself is unavoidably async (ESM dynamic import), which would ripple
639
+ // an async signature through every caller of this long-standing sync method — instead, this only
640
+ // guarantees env.d.ts *exists* (so the reference below never points at a missing file) and
641
+ // leaves it accurate as of the last real sync. `syncEnvDeclaration` (called at the actual
642
+ // execution entry points: run/watch/sync-types) is what keeps its content up to date.
643
+ const envDtsPath = path.join(rootTypesDir, 'env.d.ts');
644
+ if (!fs.existsSync(envDtsPath)) {
645
+ fs.writeFileSync(envDtsPath, this.generateEnvDeclaration({}), 'utf-8');
646
+ }
551
647
  const scopes = this.getEntityScopes(cwd);
552
648
  const sharedScope = scopes.find((s) => s.isShared);
553
649
  const featureScopes = scopes.filter((s) => !s.isShared);
@@ -561,7 +657,7 @@ export class TypeGenerator {
561
657
  if (!hasScopes || hasRootEntities) {
562
658
  const rootFixtures = this.generateFixturesDeclarationForScope(cwd, rootTypesDir, cwd);
563
659
  fs.writeFileSync(path.join(rootTypesDir, 'fixtures.d.ts'), rootFixtures, 'utf-8');
564
- const rootDeclarations = this.generateDeclarationsForScope(cwd, rootTypesDir, cwd);
660
+ const rootDeclarations = this.generateDeclarationsForScope(cwd, rootTypesDir, cwd, undefined, rootTypesDir);
565
661
  for (const [platform, content] of Object.entries(rootDeclarations)) {
566
662
  fs.writeFileSync(path.join(rootTypesDir, `${platform}.d.ts`), content, 'utf-8');
567
663
  }
@@ -587,7 +683,7 @@ export class TypeGenerator {
587
683
  fs.mkdirSync(sharedTypesDir, { recursive: true });
588
684
  const sharedFixtures = this.generateFixturesDeclarationForScope(cwd, sharedTypesDir, sharedScope.dir);
589
685
  fs.writeFileSync(path.join(sharedTypesDir, 'fixtures.d.ts'), sharedFixtures, 'utf-8');
590
- const sharedDeclarations = this.generateDeclarationsForScope(cwd, sharedTypesDir, sharedScope.dir);
686
+ const sharedDeclarations = this.generateDeclarationsForScope(cwd, sharedTypesDir, sharedScope.dir, undefined, rootTypesDir);
591
687
  for (const [platform, content] of Object.entries(sharedDeclarations)) {
592
688
  fs.writeFileSync(path.join(sharedTypesDir, `${platform}.d.ts`), content, 'utf-8');
593
689
  }
@@ -609,7 +705,7 @@ export class TypeGenerator {
609
705
  fs.mkdirSync(featureTypesDir, { recursive: true });
610
706
  const featureFixtures = this.generateFixturesDeclarationForScope(cwd, featureTypesDir, feature.dir, sharedScope?.dir);
611
707
  fs.writeFileSync(path.join(featureTypesDir, 'fixtures.d.ts'), featureFixtures, 'utf-8');
612
- const featureDeclarations = this.generateDeclarationsForScope(cwd, featureTypesDir, feature.dir, sharedScope?.dir);
708
+ const featureDeclarations = this.generateDeclarationsForScope(cwd, featureTypesDir, feature.dir, sharedScope?.dir, rootTypesDir);
613
709
  for (const [platform, content] of Object.entries(featureDeclarations)) {
614
710
  fs.writeFileSync(path.join(featureTypesDir, `${platform}.d.ts`), content, 'utf-8');
615
711
  }
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@ import { Command } from 'commander';
2
2
  export * from './config/schema.js';
3
3
  export * from './config/loader.js';
4
4
  export * from './generator/index.js';
5
- export * from './lifecycle/index.js';
6
5
  export * from './refactor/index.js';
7
6
  export * from './mobile/index.js';
7
+ export * from './linter/index.js';
8
8
  export * from './types/generator.js';
9
9
  export * from '@testspectra/matchers';
10
10
  export declare function createCliProgram(): Command;