@testspectra/cli 1.1.8-rc.2 → 1.1.8-rc.20

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 (205) hide show
  1. package/README.md +47 -0
  2. package/bin/spectra.js +0 -0
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/commands/__tests__/run-e2e.test.js +49 -49
  5. package/dist/commands/add.js +2 -1
  6. package/dist/commands/devices.js +1 -0
  7. package/dist/commands/doctor.js +1 -0
  8. package/dist/commands/init.js +150 -149
  9. package/dist/commands/install.js +2 -0
  10. package/dist/commands/license.js +1 -0
  11. package/dist/commands/test.js +5 -3
  12. package/dist/generator/__tests__/tsconfig-isolation.test.js +2 -1
  13. package/dist/generator/__tests__/type-generator.test.js +38 -0
  14. package/dist/generator/tsconfig-generator.js +1 -1
  15. package/dist/generator/type-generator.d.ts +5 -2
  16. package/dist/generator/type-generator.js +14 -17
  17. package/dist/index.js +11 -11
  18. package/dist/linter/__tests__/component-test-imports.test.js +51 -51
  19. package/dist/linter/__tests__/shared-lib-boundary.test.js +15 -15
  20. package/dist/linter/__tests__/spec-schema.test.js +167 -167
  21. package/dist/linter/discovery.d.ts +3 -3
  22. package/dist/linter/discovery.js +5 -5
  23. package/dist/linter/schemas/spec.schema.d.ts +2 -2
  24. package/dist/linter/schemas/suite.schema.d.ts +4 -4
  25. package/dist/runner/bridge.js +53 -4
  26. package/package.json +34 -29
  27. package/templates/default/CLAUDE.md +16 -16
  28. package/templates/default/global-hooks/after/mobile.hook.ts +3 -3
  29. package/templates/default/global-hooks/after/web.hook.ts +3 -3
  30. package/templates/default/global-hooks/before/mobile.hook.ts +3 -3
  31. package/templates/default/global-hooks/before/web.hook.ts +3 -3
  32. package/templates/default/package.json +2 -2
  33. package/templates/default/page-objects/AuthPage/mobile.ts +5 -5
  34. package/templates/default/page-objects/AuthPage/web.ts +10 -10
  35. package/templates/default/page-objects/MatchersPage/mobile.ts +70 -70
  36. package/templates/default/page-objects/MatchersPage/web.ts +43 -43
  37. package/templates/default/page-objects/NavigationPage/mobile.ts +14 -14
  38. package/templates/default/page-objects/NavigationPage/web.ts +8 -8
  39. package/templates/default/page-objects/PlaygroundPage/mobile.ts +42 -42
  40. package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
  41. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -53
  42. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -19
  43. package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -31
  44. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -3
  45. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -3
  46. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -27
  47. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -19
  48. package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -26
  49. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -30
  50. package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -22
  51. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -27
  52. package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -20
  53. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -24
  54. package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -26
  55. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -37
  56. package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -21
  57. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -29
  58. package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -16
  59. package/templates/default/specs/ApiInterception/suite.md +10 -10
  60. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -30
  61. package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -23
  62. package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -16
  63. package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -28
  64. package/templates/default/specs/ApiSeeding/suite.md +9 -9
  65. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -7
  66. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -19
  67. package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -9
  68. package/templates/default/specs/Authentication/suite.md +9 -9
  69. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +9 -9
  70. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +17 -17
  71. package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  72. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -17
  73. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -28
  74. package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -26
  75. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -26
  76. package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -19
  77. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +11 -11
  78. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +17 -17
  79. package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  80. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -14
  81. package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -21
  82. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -28
  83. package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -29
  84. package/templates/default/specs/DeepLink/suite.md +9 -9
  85. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +11 -11
  86. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +17 -17
  87. package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  88. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +29 -29
  89. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +19 -19
  90. package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  91. package/templates/default/specs/ElementMatchers/suite.md +10 -10
  92. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
  93. package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
  94. package/templates/default/specs/EnvironmentConfig/suite.md +9 -9
  95. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -26
  96. package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -22
  97. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -13
  98. package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -20
  99. package/templates/default/specs/PermissionRationale/suite.md +9 -9
  100. package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +6 -6
  101. package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +18 -18
  102. package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  103. package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +6 -6
  104. package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +18 -18
  105. package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  106. package/templates/default/spectra.config.ts +52 -52
  107. package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -4
  108. package/templates/default/support/actions/fillCredentials/web.action.ts +4 -4
  109. package/templates/default/support/actions/seedSession/mobile.action.ts +9 -9
  110. package/templates/default/support/actions/seedSession/web.action.ts +13 -13
  111. package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +5 -5
  112. package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  113. package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +6 -6
  114. package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  115. package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db-shm} +0 -0
  116. package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal +0 -0
  117. package/templates/nx/.nx/workspace-data/d/daemon.log +6269 -1577
  118. package/templates/nx/.nx/workspace-data/file-map.json +129 -129
  119. package/templates/nx/.nx/workspace-data/lockfile.hash +1 -1
  120. package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
  121. package/templates/nx/.nx/workspace-data/parsed-lock-file.json +535 -535
  122. package/templates/nx/.nx/workspace-data/project-graph.json +589 -590
  123. package/templates/nx/CLAUDE.md +10 -10
  124. package/templates/nx/package.json +2 -2
  125. package/templates/nx/packages/matchers/e2e/project.json +2 -2
  126. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -27
  127. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -27
  128. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -24
  129. package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -24
  130. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -3
  131. package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
  132. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -3
  133. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +17 -17
  134. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -3
  135. package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
  136. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -3
  137. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
  138. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -3
  139. package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
  140. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
  141. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
  142. package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -9
  143. package/templates/nx/packages/playground/e2e/project.json +2 -2
  144. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -3
  145. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
  146. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -3
  147. package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
  148. package/templates/nx/pnpm-lock.yaml +6663 -6663
  149. package/templates/nx/pnpm-workspace.yaml +9 -9
  150. package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +33 -33
  151. package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +91 -91
  152. package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -5
  153. package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -8
  154. package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -26
  155. package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +31 -31
  156. package/templates/nx/shared/testing/project.json +18 -19
  157. package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -4
  158. package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -4
  159. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -3
  160. package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -5
  161. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -4
  162. package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -6
  163. package/templates/nx/spectra.config.ts +52 -52
  164. package/templates/react-component/.cursorrules +17 -17
  165. package/templates/react-component/.vscode/extensions.json +5 -5
  166. package/templates/react-component/CLAUDE.md +14 -14
  167. package/templates/react-component/README.md +69 -69
  168. package/templates/react-component/fixtures/component-mock.json +12 -12
  169. package/templates/react-component/global-hooks/after/web.hook.ts +3 -3
  170. package/templates/react-component/global-hooks/before/web.hook.ts +3 -3
  171. package/templates/react-component/package.json +27 -27
  172. package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -23
  173. package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -23
  174. package/templates/react-component/page-objects/InputComponent/web.ts +13 -13
  175. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -18
  176. package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -11
  177. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -18
  178. package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -10
  179. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -18
  180. package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -16
  181. package/templates/react-component/specs/BadgeComponent/suite.md +12 -12
  182. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -19
  183. package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -28
  184. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -19
  185. package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -22
  186. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -18
  187. package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -8
  188. package/templates/react-component/specs/ButtonComponent/suite.md +12 -12
  189. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -20
  190. package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -28
  191. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -18
  192. package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -9
  193. package/templates/react-component/specs/InputComponent/suite.md +12 -12
  194. package/templates/react-component/spectra.config.ts +27 -27
  195. package/templates/react-component/src/components/Badge/Badge.tsx +60 -60
  196. package/templates/react-component/src/components/Button/Button.tsx +57 -57
  197. package/templates/react-component/src/components/Input/Input.tsx +41 -41
  198. package/templates/react-component/src/test-utils.tsx +23 -23
  199. package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -9
  200. package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -12
  201. package/bin/.testspectra-runner.hash +0 -1
  202. package/bin/testspectra-runner.exe +0 -0
  203. package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
  204. package/templates/nx/.nx/workspace-data/d/server-process.json +0 -3
  205. /package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
@@ -146,10 +146,10 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
146
146
  // Inject a failing step / invalid selector in a new test case
147
147
  const failingDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0099-failing');
148
148
  fs.mkdirSync(failingDir, { recursive: true });
149
- fs.writeFileSync(path.join(failingDir, 'web.test.ts'), `it("should fail when element does not exist", async () => {
150
- await Step.verifyPlaygroundState();
151
- await $("#non-existent-button-999").shouldBeVisible();
152
- });
149
+ fs.writeFileSync(path.join(failingDir, 'web.test.ts'), `it("should fail when element does not exist", async () => {
150
+ await Step.verifyPlaygroundState();
151
+ await $("#non-existent-button-999").shouldBeVisible();
152
+ });
153
153
  `, 'utf-8');
154
154
  // Mock exit to prevent process termination during unit test assertion
155
155
  const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
@@ -166,13 +166,13 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
166
166
  // 1. Test a valid spec using pure Spectra static commands
167
167
  const spectraStaticDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0100-spectra-static');
168
168
  fs.mkdirSync(spectraStaticDir, { recursive: true });
169
- fs.writeFileSync(path.join(spectraStaticDir, 'web.test.ts'), `it("should execute via unified Spectra static object", async () => {
170
- await Spectra.navigate("/#playground");
171
- await Spectra.type("#demo-email-input-pg", "automated@spectra.dev");
172
- await Spectra.get("#demo-email-input-pg").shouldHaveValue("automated@spectra.dev");
173
- await Spectra.wait(50);
174
- await Spectra.getAll(".demo-product-item").shouldHaveLengthGreaterThan(0);
175
- });
169
+ fs.writeFileSync(path.join(spectraStaticDir, 'web.test.ts'), `it("should execute via unified Spectra static object", async () => {
170
+ await Spectra.navigate("/#playground");
171
+ await Spectra.type("#demo-email-input-pg", "automated@spectra.dev");
172
+ await Spectra.get("#demo-email-input-pg").shouldHaveValue("automated@spectra.dev");
173
+ await Spectra.wait(50);
174
+ await Spectra.getAll(".demo-product-item").shouldHaveLengthGreaterThan(0);
175
+ });
176
176
  `, 'utf-8');
177
177
  await runCommand(['specs/Playground/TC-0100-spectra-static/web.test.ts'], { target: 'web', demo: true });
178
178
  const resultStatic = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
@@ -181,11 +181,11 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
181
181
  // 2. Test that assertion mismatch on existing element strictly FAILS (no false positive)
182
182
  const mismatchDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0101-mismatch');
183
183
  fs.mkdirSync(mismatchDir, { recursive: true });
184
- fs.writeFileSync(path.join(mismatchDir, 'web.test.ts'), `it("should strictly fail when assertion text does not match", async () => {
185
- await Spectra.navigate("/#playground");
186
- // Element exists, but text is wrong -> MUST throw and fail
187
- await Spectra.get("#demo-email-input-pg").shouldHaveValue("completely_wrong_value_xyz");
188
- });
184
+ fs.writeFileSync(path.join(mismatchDir, 'web.test.ts'), `it("should strictly fail when assertion text does not match", async () => {
185
+ await Spectra.navigate("/#playground");
186
+ // Element exists, but text is wrong -> MUST throw and fail
187
+ await Spectra.get("#demo-email-input-pg").shouldHaveValue("completely_wrong_value_xyz");
188
+ });
189
189
  `, 'utf-8');
190
190
  const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
191
191
  await runCommand(['specs/Playground/TC-0101-mismatch/web.test.ts'], { target: 'web', demo: true });
@@ -375,39 +375,39 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
375
375
  it('E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation in real Chromium', async () => {
376
376
  const fifoAbortDir = path.join(tmpDir, 'specs', 'ApiInterception', 'TC-0010-fifo-abort');
377
377
  fs.mkdirSync(fifoAbortDir, { recursive: true });
378
- fs.writeFileSync(path.join(fifoAbortDir, 'web.test.ts'), `it("should handle FIFO respondOnce queue and abort simulation", async () => {
379
- await MatchersPage.open("api-fetch");
380
-
381
- // Setup intercept with default fallback and FIFO queue
382
- const mock = await Spectra.intercept("api/posts", "GET", [
383
- { id: 999, title: "Default Fallback Post" }
384
- ]);
385
-
386
- await mock.respondOnce([
387
- { id: 101, title: "FIFO Queued Post #1" }
388
- ]);
389
- await mock.respondOnce([
390
- { id: 102, title: "FIFO Queued Post #2" }
391
- ]);
392
-
393
- // 1. First fetch triggers FIFO item #1
394
- await MatchersPage.fetchApiButton.click();
395
- await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #1");
396
-
397
- // 2. Second fetch triggers FIFO item #2
398
- await MatchersPage.fetchApiButton.click();
399
- await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #2");
400
-
401
- // 3. Third fetch falls back to default response
402
- await MatchersPage.fetchApiButton.click();
403
- await MatchersPage.fetchedItems.first().shouldContainText("Default Fallback Post");
404
-
405
- // 4. Test abort simulation
406
- await mock.abort("Failed");
407
- await MatchersPage.fetchApiButton.click();
408
- // On network failure, demo app shows fallback 200 OK from catch block
409
- await MatchersPage.fetchStatusBadge.shouldHaveText("200 OK");
410
- });
378
+ fs.writeFileSync(path.join(fifoAbortDir, 'web.test.ts'), `it("should handle FIFO respondOnce queue and abort simulation", async () => {
379
+ await MatchersPage.open("api-fetch");
380
+
381
+ // Setup intercept with default fallback and FIFO queue
382
+ const mock = await Spectra.intercept("api/posts", "GET", [
383
+ { id: 999, title: "Default Fallback Post" }
384
+ ]);
385
+
386
+ await mock.respondOnce([
387
+ { id: 101, title: "FIFO Queued Post #1" }
388
+ ]);
389
+ await mock.respondOnce([
390
+ { id: 102, title: "FIFO Queued Post #2" }
391
+ ]);
392
+
393
+ // 1. First fetch triggers FIFO item #1
394
+ await MatchersPage.fetchApiButton.click();
395
+ await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #1");
396
+
397
+ // 2. Second fetch triggers FIFO item #2
398
+ await MatchersPage.fetchApiButton.click();
399
+ await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #2");
400
+
401
+ // 3. Third fetch falls back to default response
402
+ await MatchersPage.fetchApiButton.click();
403
+ await MatchersPage.fetchedItems.first().shouldContainText("Default Fallback Post");
404
+
405
+ // 4. Test abort simulation
406
+ await mock.abort("Failed");
407
+ await MatchersPage.fetchApiButton.click();
408
+ // On network failure, demo app shows fallback 200 OK from catch block
409
+ await MatchersPage.fetchStatusBadge.shouldHaveText("200 OK");
410
+ });
411
411
  `, 'utf-8');
412
412
  await runCommand(['specs/ApiInterception/TC-0010-fifo-abort/web.test.ts'], { target: 'web' });
413
413
  const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
@@ -289,6 +289,7 @@ export async function addCommand(moduleNameArg, options = {}) {
289
289
  copyRecursive(sampleE2eSrc, targetE2eDir, {
290
290
  'playground-e2e': e2eProjectName,
291
291
  'packages/playground/e2e': path.relative(rootDir, targetE2eDir).replace(/\\/g, '/'),
292
+ 'testspectra:scope:playground': `testspectra:scope:${featureName}`,
292
293
  'scope:playground': `scope:${featureName}`,
293
294
  });
294
295
  // Adjust project.json
@@ -300,7 +301,7 @@ export async function addCommand(moduleNameArg, options = {}) {
300
301
  pObj.sourceRoot = path.relative(rootDir, targetE2eDir).replace(/\\/g, '/');
301
302
  pObj.targets.e2e.options.cwd = path.relative(rootDir, targetE2eDir).replace(/\\/g, '/');
302
303
  pObj.targets['type-check'].options.cwd = '.';
303
- pObj.tags = [`scope:${featureName}`, 'type:e2e'];
304
+ pObj.tags = ['testspectra:e2e', `testspectra:scope:${featureName}`];
304
305
  fs.writeFileSync(projJsonPath, JSON.stringify(pObj, null, 2), 'utf-8');
305
306
  }
306
307
  catch { }
@@ -32,6 +32,7 @@ export async function devicesCommand(options = {}) {
32
32
  cwd,
33
33
  env: getRunnerEnv(),
34
34
  stdio: 'inherit',
35
+ windowsHide: true,
35
36
  });
36
37
  child.on('close', (code) => {
37
38
  if (code === 0) {
@@ -42,6 +42,7 @@ export async function doctorCommand(options = {}) {
42
42
  cwd,
43
43
  env: getRunnerEnv(),
44
44
  stdio: 'inherit',
45
+ windowsHide: true,
45
46
  });
46
47
  child.on('close', (code) => {
47
48
  if (code === 0 || options.fix) {
@@ -508,6 +508,7 @@ export async function initCommand(options = {}) {
508
508
  copyRecursive(sampleE2eSrc, targetE2eDir, {
509
509
  'playground-e2e': e2eProjectName,
510
510
  'packages/playground/e2e': path.relative(cwd, targetE2eDir).replace(/\\/g, '/'),
511
+ 'testspectra:scope:playground': `testspectra:scope:${featureName}`,
511
512
  'scope:playground': `scope:${featureName}`,
512
513
  });
513
514
  // Adjust project.json
@@ -519,7 +520,7 @@ export async function initCommand(options = {}) {
519
520
  pObj.sourceRoot = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
520
521
  pObj.targets.e2e.options.cwd = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
521
522
  pObj.targets['type-check'].options.cwd = '.';
522
- pObj.tags = [`scope:${featureName}`, 'type:e2e'];
523
+ pObj.tags = ['testspectra:e2e', `testspectra:scope:${featureName}`];
523
524
  fs.writeFileSync(projJsonPath, JSON.stringify(pObj, null, 2), 'utf-8');
524
525
  }
525
526
  catch { }
@@ -557,135 +558,135 @@ export async function initCommand(options = {}) {
557
558
  // 7. Generate Ambient Types in ROOT only
558
559
  TypeGenerator.writeDeclarationFiles(cwd);
559
560
  ensureVsCodeExtensions(cwd);
560
- const archDocContent = `# TestSpectra Enterprise Monorepo Architecture
561
-
562
- This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
563
-
564
- ---
565
-
566
- ## 1. Directory & File Overview
567
-
568
- ### Centralized Root Elements
569
- - \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
570
- - \`./.testspectra/\`: Local test project cache, runtime metadata, auto-generated master TSConfig (\`.testspectra/tsconfig.json\`), platform sub-configs (\`.testspectra/tsconfig/\`), and universal ambient types (\`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
571
- - \`./nx.json\`: Nx target defaults with execution caching and inputs.
572
- - \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
573
- - \`./tsconfig.json\`: Solution-style TypeScript orchestrator referencing \`./.testspectra/tsconfig.json\`.
574
-
575
- ### Centralized Shared Testing Library (\`./${sharedTestingRelPath}\`)
576
- Provides cross-feature reusable test entities with **100% zero-import global resolution**:
577
- - \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
578
- - \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
579
- - \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
580
- - \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
581
-
582
- ### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
561
+ const archDocContent = `# TestSpectra Enterprise Monorepo Architecture
562
+
563
+ This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
564
+
565
+ ---
566
+
567
+ ## 1. Directory & File Overview
568
+
569
+ ### Centralized Root Elements
570
+ - \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
571
+ - \`./.testspectra/\`: Local test project cache, runtime metadata, auto-generated master TSConfig (\`.testspectra/tsconfig.json\`), platform sub-configs (\`.testspectra/tsconfig/\`), and universal ambient types (\`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
572
+ - \`./nx.json\`: Nx target defaults with execution caching and inputs.
573
+ - \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
574
+ - \`./tsconfig.json\`: Solution-style TypeScript orchestrator referencing \`./.testspectra/tsconfig.json\`.
575
+
576
+ ### Centralized Shared Testing Library (\`./${sharedTestingRelPath}\`)
577
+ Provides cross-feature reusable test entities with **100% zero-import global resolution**:
578
+ - \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
579
+ - \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
580
+ - \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
581
+ - \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
582
+
583
+ ### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
583
584
  ${featureE2eDirs
584
585
  .map((d) => {
585
586
  const rel = path.relative(cwd, d);
586
587
  const name = path.basename(path.dirname(d));
587
588
  const e2eName = `${name}-${e2eFolderName}`;
588
- return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
589
- - \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
590
- - \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
589
+ return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
590
+ - \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
591
+ - \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
591
592
  - \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).`;
592
593
  })
593
- .join('\n')}
594
-
595
- ---
596
-
597
- ## 2. Zero-Import Consumption Flow & Mechanics
598
-
599
- TestSpectra completely eliminates boilerplate \`import\` statements across all test specs, steps, and page objects.
600
-
601
- ### How It Works:
602
- 1. **Centralized Ambient Aggregation**:
603
- TestSpectra Language Service Plugin & CLI automatically scan:
604
- - **Local Feature Entities**: \`<feature>/${e2eFolderName}/page-objects/\`, \`<feature>/${e2eFolderName}/steps/\`, \`<feature>/${e2eFolderName}/actions/\`
605
- - **Shared Library Entities**: \`./${sharedTestingRelPath}/page-objects/\`, \`./${sharedTestingRelPath}/steps/\`, \`./${sharedTestingRelPath}/actions/\`, \`./${sharedTestingRelPath}/fixtures/\`
606
- 2. **Ambient Typing Generation**:
607
- These are compiled into \`.testspectra/types/web.d.ts\`, \`android.d.ts\`, \`ios.d.ts\`, \`fixtures.d.ts\`.
608
- 3. **Instant IDE Autocomplete & Zero Clutter**:
609
- Specs consume both local feature objects and shared library utilities globally.
610
-
611
- ### Code Example:
612
- \`\`\`typescript
613
- // packages/features/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts
614
- // 🌟 NOTICE: 100% Zero-Import & Flat Execution! (No import statement, no describe wrapper)
615
-
616
- it("should navigate via shared header and login with local POM", async () => {
617
- // 1. Consumed from Shared Testing Library (${sharedTestingRelPath}/page-objects/NavigationBar)
618
- await NavigationBar.goToLogin();
619
-
620
- // 2. Consumed from Shared Testing Library (${sharedTestingRelPath}/steps/loginAsAdmin)
621
- await Step.loginAsAdmin();
622
-
623
- // 3. Consumed from Local Feature POM (page-objects/LoginPage)
624
- await LoginPage.flashAlert.shouldBeVisible();
625
- await LoginPage.flashAlert.shouldContainText("Welcome");
626
-
627
- // 4. Consumed from Shared Testing Library Custom Action (${sharedTestingRelPath}/actions/dismissBanner)
628
- await Spectra.dismissBanner();
629
- });
630
- \`\`\`
631
-
632
- ---
633
-
634
- ## 3. How to Run Tests
635
-
636
- You can execute tests either from the **Monorepo Root via Nx** or directly inside each **Feature Directory via CLI**:
637
-
638
- ### Option A: From Root Monorepo via Nx (Recommended for CI & Team Workflows)
639
-
640
- \`\`\`bash
641
- # 1. Run E2E for a SPECIFIC feature module (e.g. auth)
642
- pnpm nx run auth-${e2eFolderName}:e2e
643
-
644
- # 2. Run E2E for a specific feature on ANDROID or IOS
645
- pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
646
- pnpm nx run auth-${e2eFolderName}:e2e --configuration=ios
647
-
648
- # 3. Run E2E for a specific feature in HEADLESS browser mode
649
- pnpm nx run auth-${e2eFolderName}:e2e --configuration=headless
650
-
651
- # 4. Run ALL feature E2E suites across the entire monorepo in parallel
652
- pnpm nx run-many -t e2e
653
-
654
- # 5. Run only E2E tests for features modified in current Git branch / PR
655
- pnpm nx affected -t e2e
656
- \`\`\`
657
-
658
- ### Option B: From Inside the Feature Directory (Direct CLI)
659
-
660
- \`\`\`bash
661
- # Navigate to the feature e2e folder
662
- cd ${featureE2eDirs.length > 0 ? path.relative(cwd, featureE2eDirs[0]).replace(/\\/g, '/') : `modules/auth/${e2eFolderName}`}
663
-
664
- # Run all specs in this feature
665
- pnpm spectra run
666
-
667
- # Run a specific test case file
668
- pnpm spectra run specs/Auth/TC-AUTH-01/web.test.ts
669
-
670
- # Target Android device or emulator
671
- pnpm spectra run --target android
672
-
673
- # Run in headed / interactive browser mode
674
- pnpm spectra run --no-headless
675
- \`\`\`
676
-
677
- ---
678
-
679
- ## 4. Type Checking & Validation
680
-
681
- \`\`\`bash
682
- # Type check all E2E projects via Nx
683
- pnpm nx run-many -t type-check
684
-
685
- # Or standard TypeScript project references build from root
686
- pnpm type-check
687
- # or: npx tsc -b
688
- \`\`\`
594
+ .join('\n')}
595
+
596
+ ---
597
+
598
+ ## 2. Zero-Import Consumption Flow & Mechanics
599
+
600
+ TestSpectra completely eliminates boilerplate \`import\` statements across all test specs, steps, and page objects.
601
+
602
+ ### How It Works:
603
+ 1. **Centralized Ambient Aggregation**:
604
+ TestSpectra Language Service Plugin & CLI automatically scan:
605
+ - **Local Feature Entities**: \`<feature>/${e2eFolderName}/page-objects/\`, \`<feature>/${e2eFolderName}/steps/\`, \`<feature>/${e2eFolderName}/actions/\`
606
+ - **Shared Library Entities**: \`./${sharedTestingRelPath}/page-objects/\`, \`./${sharedTestingRelPath}/steps/\`, \`./${sharedTestingRelPath}/actions/\`, \`./${sharedTestingRelPath}/fixtures/\`
607
+ 2. **Ambient Typing Generation**:
608
+ These are compiled into \`.testspectra/types/web.d.ts\`, \`android.d.ts\`, \`ios.d.ts\`, \`fixtures.d.ts\`.
609
+ 3. **Instant IDE Autocomplete & Zero Clutter**:
610
+ Specs consume both local feature objects and shared library utilities globally.
611
+
612
+ ### Code Example:
613
+ \`\`\`typescript
614
+ // packages/features/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts
615
+ // 🌟 NOTICE: 100% Zero-Import & Flat Execution! (No import statement, no describe wrapper)
616
+
617
+ it("should navigate via shared header and login with local POM", async () => {
618
+ // 1. Consumed from Shared Testing Library (${sharedTestingRelPath}/page-objects/NavigationBar)
619
+ await NavigationBar.goToLogin();
620
+
621
+ // 2. Consumed from Shared Testing Library (${sharedTestingRelPath}/steps/loginAsAdmin)
622
+ await Step.loginAsAdmin();
623
+
624
+ // 3. Consumed from Local Feature POM (page-objects/LoginPage)
625
+ await LoginPage.flashAlert.shouldBeVisible();
626
+ await LoginPage.flashAlert.shouldContainText("Welcome");
627
+
628
+ // 4. Consumed from Shared Testing Library Custom Action (${sharedTestingRelPath}/actions/dismissBanner)
629
+ await Spectra.dismissBanner();
630
+ });
631
+ \`\`\`
632
+
633
+ ---
634
+
635
+ ## 3. How to Run Tests
636
+
637
+ You can execute tests either from the **Monorepo Root via Nx** or directly inside each **Feature Directory via CLI**:
638
+
639
+ ### Option A: From Root Monorepo via Nx (Recommended for CI & Team Workflows)
640
+
641
+ \`\`\`bash
642
+ # 1. Run E2E for a SPECIFIC feature module (e.g. auth)
643
+ pnpm nx run auth-${e2eFolderName}:e2e
644
+
645
+ # 2. Run E2E for a specific feature on ANDROID or IOS
646
+ pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
647
+ pnpm nx run auth-${e2eFolderName}:e2e --configuration=ios
648
+
649
+ # 3. Run E2E for a specific feature in HEADLESS browser mode
650
+ pnpm nx run auth-${e2eFolderName}:e2e --configuration=headless
651
+
652
+ # 4. Run ALL feature E2E suites across the entire monorepo in parallel
653
+ pnpm nx run-many -t e2e
654
+
655
+ # 5. Run only E2E tests for features modified in current Git branch / PR
656
+ pnpm nx affected -t e2e
657
+ \`\`\`
658
+
659
+ ### Option B: From Inside the Feature Directory (Direct CLI)
660
+
661
+ \`\`\`bash
662
+ # Navigate to the feature e2e folder
663
+ cd ${featureE2eDirs.length > 0 ? path.relative(cwd, featureE2eDirs[0]).replace(/\\/g, '/') : `modules/auth/${e2eFolderName}`}
664
+
665
+ # Run all specs in this feature
666
+ pnpm spectra run
667
+
668
+ # Run a specific test case file
669
+ pnpm spectra run specs/Auth/TC-AUTH-01/web.test.ts
670
+
671
+ # Target Android device or emulator
672
+ pnpm spectra run --target android
673
+
674
+ # Run in headed / interactive browser mode
675
+ pnpm spectra run --no-headless
676
+ \`\`\`
677
+
678
+ ---
679
+
680
+ ## 4. Type Checking & Validation
681
+
682
+ \`\`\`bash
683
+ # Type check all E2E projects via Nx
684
+ pnpm nx run-many -t type-check
685
+
686
+ # Or standard TypeScript project references build from root
687
+ pnpm type-check
688
+ # or: npx tsc -b
689
+ \`\`\`
689
690
  `;
690
691
  fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), archDocContent, 'utf-8');
691
692
  // 8. Ensure root .gitignore ignores .testspectra/ and *.tsbuildinfo
@@ -741,32 +742,32 @@ pnpm type-check
741
742
  TypeGenerator.writeDeclarationFiles(cwd);
742
743
  ensureVsCodeExtensions(cwd);
743
744
  // Standalone Architecture Documentation
744
- const standaloneArchDoc = `# TestSpectra Project Architecture
745
-
746
- This project is built with the TestSpectra testing framework.
747
-
748
- ## Structure & File Map
749
- - \`./spectra.config.ts\`: Typed runner configuration.
750
- - \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
751
- - \`./page-objects/\`: Page Object models per platform.
752
- - \`./steps/\`: Business flows accessible globally via \`Step.*\`.
753
- - \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
754
- - \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
755
- - \`./global-hooks/\`: Global suite lifecycle hooks.
756
- - \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
757
-
758
- ## Execution
759
- \`\`\`bash
760
- # Run web test suite
761
- pnpm test
762
-
763
- # Run Android / iOS
764
- pnpm spectra run --target android
765
- pnpm spectra run --target ios
766
-
767
- # Type check
768
- pnpm type-check
769
- \`\`\`
745
+ const standaloneArchDoc = `# TestSpectra Project Architecture
746
+
747
+ This project is built with the TestSpectra testing framework.
748
+
749
+ ## Structure & File Map
750
+ - \`./spectra.config.ts\`: Typed runner configuration.
751
+ - \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
752
+ - \`./page-objects/\`: Page Object models per platform.
753
+ - \`./steps/\`: Business flows accessible globally via \`Step.*\`.
754
+ - \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
755
+ - \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
756
+ - \`./global-hooks/\`: Global suite lifecycle hooks.
757
+ - \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
758
+
759
+ ## Execution
760
+ \`\`\`bash
761
+ # Run web test suite
762
+ pnpm test
763
+
764
+ # Run Android / iOS
765
+ pnpm spectra run --target android
766
+ pnpm spectra run --target ios
767
+
768
+ # Type check
769
+ pnpm type-check
770
+ \`\`\`
770
771
  `;
771
772
  fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), standaloneArchDoc, 'utf-8');
772
773
  // Safely update root README.md (do not overwrite existing content)
@@ -34,6 +34,7 @@ export async function installCommand(item, options = {}) {
34
34
  cwd: workspaceRoot,
35
35
  env: runnerEnv,
36
36
  stdio: 'inherit',
37
+ windowsHide: true,
37
38
  });
38
39
  }
39
40
  else {
@@ -41,6 +42,7 @@ export async function installCommand(item, options = {}) {
41
42
  cwd,
42
43
  env: runnerEnv,
43
44
  stdio: 'inherit',
45
+ windowsHide: true,
44
46
  });
45
47
  }
46
48
  child.on('close', (code) => {
@@ -14,6 +14,7 @@ export async function licenseCommand(action = 'status', key, options = {}) {
14
14
  const child = spawn(binPath, args, {
15
15
  stdio: 'inherit',
16
16
  env: process.env,
17
+ windowsHide: true,
17
18
  });
18
19
  child.on('close', (code) => {
19
20
  if (code === 0) {
@@ -15,7 +15,7 @@ import { runSystemChecks } from './doctor.js';
15
15
  async function resolveBrowserExecutable(cwd) {
16
16
  try {
17
17
  const { dependencies } = await runSystemChecks(cwd);
18
- const chrome = dependencies.find((d) => d.category === 'web' && (d.name.includes('Chrome') || d.name.includes('Chromium')));
18
+ const chrome = dependencies.find((d) => d.category === 'web' && d.name.includes('Chrome'));
19
19
  return chrome?.installed && chrome.location ? chrome.location : null;
20
20
  }
21
21
  catch {
@@ -81,9 +81,11 @@ export async function testCommand(options = {}) {
81
81
  concurrency: concurrency !== undefined && !isNaN(concurrency) ? concurrency : undefined,
82
82
  browserExecutablePath,
83
83
  onLine: (line) => {
84
- if (!line.startsWith('[TESTSPECTRA_'))
84
+ // Strip ANSI and whitespace since Vitest might prefix or colorize console logs
85
+ const cleanLine = line.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '').trim();
86
+ if (!cleanLine.startsWith('[TESTSPECTRA_'))
85
87
  return;
86
- reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message: line });
88
+ reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message: cleanLine });
87
89
  if (!reporter.isInteractiveMode()) {
88
90
  console.log(line);
89
91
  }
@@ -41,7 +41,8 @@ describe('Platform TSConfigs & Type Isolation (docs/v2/cli/tsconfig-platform-iso
41
41
  expect(rootConfig.references).toEqual([{ path: './.testspectra/tsconfig.json' }]);
42
42
  // Verify dynamic master solution references all 5 platform solution tsconfigs in .testspectra/tsconfig/
43
43
  const solutionConfig = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'tsconfig.json'), 'utf-8'));
44
- expect(solutionConfig.compilerOptions.noEmit).toBe(true);
44
+ expect(solutionConfig.compilerOptions.composite).toBe(true);
45
+ expect(solutionConfig.compilerOptions.noEmit).toBeUndefined();
45
46
  expect(solutionConfig.references).toEqual([
46
47
  { path: './tsconfig/web.json' },
47
48
  { path: './tsconfig/mobile.json' },
@@ -383,4 +383,42 @@ async function verifyZeroImport() {
383
383
  expect(fs.existsSync(path.join(tmpDir, '.testspectra', 'types', 'web.d.ts'))).toBe(false);
384
384
  expect(fs.existsSync(path.join(tmpDir, '.testspectra', 'types', 'common.d.ts'))).toBe(false);
385
385
  });
386
+ it('nx monorepo: does NOT treat feature library as shared library and discovers nested e2e subproject', () => {
387
+ // 1. Shared library with testspectra:shared
388
+ const sharedDir = path.join(tmpDir, 'shared', 'testing');
389
+ fs.mkdirSync(path.join(sharedDir, 'page-objects', 'SharedNav'), { recursive: true });
390
+ fs.writeFileSync(path.join(sharedDir, 'project.json'), JSON.stringify({ name: 'shared-testing', projectType: 'library', tags: ['testspectra:shared'] }));
391
+ fs.writeFileSync(path.join(sharedDir, 'page-objects', 'SharedNav', 'common.ts'), 'export default class SharedNav {}');
392
+ // 2. Nx Feature library (projectType: "library", standard Nx tags, NO testspectra tags)
393
+ const featureLibDir = path.join(tmpDir, 'packages', 'features', 'auth');
394
+ fs.mkdirSync(featureLibDir, { recursive: true });
395
+ fs.writeFileSync(path.join(featureLibDir, 'project.json'), JSON.stringify({
396
+ name: '@tagsamurai/feature-auth',
397
+ projectType: 'library',
398
+ sourceRoot: 'packages/features/auth',
399
+ targets: {},
400
+ tags: ['type:feature', 'scope:feature-auth'],
401
+ }));
402
+ // 3. Nested E2E subproject inside feature library
403
+ const featureE2eDir = path.join(featureLibDir, 'e2e');
404
+ fs.mkdirSync(path.join(featureE2eDir, 'specs', 'AuthSuite', 'TC-001'), { recursive: true });
405
+ fs.writeFileSync(path.join(featureE2eDir, 'project.json'), JSON.stringify({
406
+ name: 'feature-auth-e2e',
407
+ projectType: 'application',
408
+ tags: ['testspectra:e2e', 'testspectra:scope:feature-auth'],
409
+ }));
410
+ fs.writeFileSync(path.join(featureE2eDir, 'specs', 'AuthSuite', 'TC-001', 'web.test.ts'), 'it("auth test", async () => {});');
411
+ const scopes = TypeGenerator.getEntityScopes(tmpDir);
412
+ expect(scopes).toHaveLength(2);
413
+ const sharedScope = scopes.find((s) => s.isShared);
414
+ expect(sharedScope).toBeDefined();
415
+ expect(sharedScope?.name).toBe('shared');
416
+ expect(sharedScope?.dir).toBe(path.resolve(sharedDir));
417
+ const featureScope = scopes.find((s) => !s.isShared);
418
+ expect(featureScope).toBeDefined();
419
+ expect(featureScope?.name).toBe('feature-auth');
420
+ expect(featureScope?.dir).toBe(path.resolve(featureE2eDir));
421
+ // Verify packages/features/auth is NOT in scopes
422
+ expect(scopes.some((s) => s.dir === path.resolve(featureLibDir))).toBe(false);
423
+ });
386
424
  });
@@ -16,7 +16,7 @@ export class TsConfigGenerator {
16
16
  moduleResolution: 'NodeNext',
17
17
  skipLibCheck: true,
18
18
  strict: true,
19
- noEmit: true,
19
+ composite: true,
20
20
  },
21
21
  files: [],
22
22
  references: references || [
@@ -9,8 +9,11 @@ export interface EntityScope {
9
9
  * Discovers all feature directories and shared library directories.
10
10
  * Standard discovery hierarchy:
11
11
  * 1. Nx / Monorepo Project Descriptors (`project.json`):
12
- * - projectType: "library" OR tags: ["type:shared", "type:library", "scope:shared"] => Shared Library Scope (isShared: true)
13
- * - projectType: "application" OR tags: ["type:e2e", "type:app"] => Feature Scope (isShared: false)
12
+ * - tags: ["testspectra:shared"] => Shared Library Scope (isShared: true)
13
+ * - tags: ["testspectra:e2e"] or ["testspectra:scope:<name>"] => Feature Scope (isShared: false)
14
+ * - Generic Nx libraries/apps without testspectra tags are not TestSpectra scopes and recursion
15
+ * continues into their subdirectories so nested e2e projects (e.g. `packages/features/auth/e2e`)
16
+ * are discovered.
14
17
  * 2. Explicit User Configuration (`tsconfig.spectra.shared.json`):
15
18
  * - Custom include path for shared testing utilities
16
19
  * 3. Dynamic Workspace Filesystem Discovery (Fallback for non-Nx / custom layouts):