@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
@@ -13,8 +13,11 @@ export const PLATFORM_HIERARCHY = {
13
13
  * Discovers all feature directories and shared library directories.
14
14
  * Standard discovery hierarchy:
15
15
  * 1. Nx / Monorepo Project Descriptors (`project.json`):
16
- * - projectType: "library" OR tags: ["type:shared", "type:library", "scope:shared"] => Shared Library Scope (isShared: true)
17
- * - projectType: "application" OR tags: ["type:e2e", "type:app"] => Feature Scope (isShared: false)
16
+ * - tags: ["testspectra:shared"] => Shared Library Scope (isShared: true)
17
+ * - tags: ["testspectra:e2e"] or ["testspectra:scope:<name>"] => Feature Scope (isShared: false)
18
+ * - Generic Nx libraries/apps without testspectra tags are not TestSpectra scopes and recursion
19
+ * continues into their subdirectories so nested e2e projects (e.g. `packages/features/auth/e2e`)
20
+ * are discovered.
18
21
  * 2. Explicit User Configuration (`tsconfig.spectra.shared.json`):
19
22
  * - Custom include path for shared testing utilities
20
23
  * 3. Dynamic Workspace Filesystem Discovery (Fallback for non-Nx / custom layouts):
@@ -48,16 +51,9 @@ export function discoverEntityScopes(cwd) {
48
51
  if (fs.existsSync(projectJsonPath)) {
49
52
  try {
50
53
  const pObj = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
51
- const projectType = pObj.projectType;
52
54
  const tags = Array.isArray(pObj.tags) ? pObj.tags : [];
53
- const isSharedLib = projectType === 'library' ||
54
- tags.includes('type:shared') ||
55
- tags.includes('type:library') ||
56
- tags.includes('scope:shared');
57
- const isE2eApp = projectType === 'application' ||
58
- tags.includes('type:e2e') ||
59
- tags.includes('type:app') ||
60
- tags.some((t) => t.startsWith('scope:') && t !== 'scope:shared');
55
+ const isSharedLib = tags.includes('testspectra:shared');
56
+ const isE2eApp = tags.includes('testspectra:e2e') || tags.some((t) => t.startsWith('testspectra:scope:'));
61
57
  if (isSharedLib) {
62
58
  if (!scopes.some((s) => s.dir === dir)) {
63
59
  scopes.push({ name: 'shared', dir, isShared: true });
@@ -65,9 +61,9 @@ export function discoverEntityScopes(cwd) {
65
61
  return; // Do not recurse into subdirectories of an identified project
66
62
  }
67
63
  else if (isE2eApp) {
68
- // Extract scope name from tags (e.g. "scope:matchers" => "matchers") or project name
69
- const scopeTag = tags.find((t) => t.startsWith('scope:') && t !== 'scope:shared');
70
- let featureName = scopeTag ? scopeTag.replace('scope:', '') : '';
64
+ // Extract scope name from tags (e.g. "testspectra:scope:matchers" => "matchers") or project name
65
+ const scopeTag = tags.find((t) => t.startsWith('testspectra:scope:'));
66
+ let featureName = scopeTag ? scopeTag.replace('testspectra:scope:', '') : '';
71
67
  if (!featureName && pObj.name) {
72
68
  featureName = pObj.name.replace(/-e2e$/, '').replace(/^e2e-/, '');
73
69
  }
@@ -329,6 +325,9 @@ export class TypeGenerator {
329
325
  const hierarchy = PLATFORM_HIERARCHY[platform];
330
326
  let content = `// Auto-generated ambient declarations for TestSpectra [${platform.toUpperCase()}]\n`;
331
327
  content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
328
+ content += `/// <reference types="@testspectra/matchers" />\n`;
329
+ content += `/// <reference path="./fixtures.d.ts" />\n`;
330
+ content += `/// <reference path="${envRefPath}" />\n\n`;
332
331
  // TestSpectra's Rust-native orchestrator worker (core/orchestrator/src/worker/bdd_runner.ts)
333
332
  // registers its own `it`/`test`/lifecycle-hook globals directly — there is no WDIO or Mocha
334
333
  // runtime involved at all (not just for component testing; this is true across every
@@ -346,9 +345,7 @@ export class TypeGenerator {
346
345
  // — the Rust orchestrator's BDD runtime has no `expect` global at all.
347
346
  content += `declare const expect: import('@testspectra/react').ExpectStatic;\n`;
348
347
  }
349
- content += `/// <reference types="@testspectra/matchers" />\n`;
350
- content += `/// <reference path="./fixtures.d.ts" />\n`;
351
- content += `/// <reference path="${envRefPath}" />\n\n`;
348
+ content += `\n`;
352
349
  // 1. Page Objects Global Declarations
353
350
  const declaredPOMs = new Set();
354
351
  for (const poDir of poDirs) {
package/dist/index.js CHANGED
@@ -41,13 +41,13 @@ export function createCliProgram() {
41
41
  .command('install [dependency]')
42
42
  .description('Install missing testing dependencies, browser drivers, and workspace packages')
43
43
  .option('-f, --force', 'Force re-installation of dependencies')
44
- .addHelpText('after', `
45
- Examples:
46
- $ spectra install Install all missing required dependencies automatically
47
- $ spectra install chrome Install Google Chrome for Testing & matching Chromedriver
48
- $ spectra install adb Install Google Android SDK Platform Tools (ADB)
49
- $ spectra install bun Install Bun runtime
50
- $ spectra install android-driver Install Spectra Android Native Driver & WDIO Service
44
+ .addHelpText('after', `
45
+ Examples:
46
+ $ spectra install Install all missing required dependencies automatically
47
+ $ spectra install chrome Install Google Chrome for Testing & matching Chromedriver
48
+ $ spectra install adb Install Google Android SDK Platform Tools (ADB)
49
+ $ spectra install bun Install Bun runtime
50
+ $ spectra install android-driver Install Spectra Android Native Driver & WDIO Service
51
51
  $ spectra install wdio Install WebDriverIO core packages and matchers`)
52
52
  .action(installCommand);
53
53
  program
@@ -105,10 +105,10 @@ Examples:
105
105
  .command('license [action] [key]')
106
106
  .description('Manage TestSpectra commercial license (activate, status, remove)')
107
107
  .option('--json', 'Output license details as JSON')
108
- .addHelpText('after', `
109
- Examples:
110
- $ spectra license activate <KEY> Activate license key and auto-configure private driver registry
111
- $ spectra license status Display active license organization, plan, and expiration
108
+ .addHelpText('after', `
109
+ Examples:
110
+ $ spectra license activate <KEY> Activate license key and auto-configure private driver registry
111
+ $ spectra license status Display active license organization, plan, and expiration
112
112
  $ spectra license remove Remove locally activated license credentials`)
113
113
  .action(licenseCommand);
114
114
  const skills = program
@@ -14,20 +14,20 @@ const __dirname = path.dirname(__filename);
14
14
  */
15
15
  describe('Component Test (.tsx) Import Exception', () => {
16
16
  const tempDir = path.join(__dirname, '__temp_tsx_imports__');
17
- const specFrontmatter = `---
18
- id: TC-0200
19
- title: Sample component test
20
- priority: High
21
- caseType: Positive
22
- status: automated
23
- coverage:
24
- web: automated
25
- ---
26
-
27
- # Sample component test
28
-
29
- ## Test Steps
30
- 1. Mount the component and assert it is visible.
17
+ const specFrontmatter = `---
18
+ id: TC-0200
19
+ title: Sample component test
20
+ priority: High
21
+ caseType: Positive
22
+ status: automated
23
+ coverage:
24
+ web: automated
25
+ ---
26
+
27
+ # Sample component test
28
+
29
+ ## Test Steps
30
+ 1. Mount the component and assert it is visible.
31
31
  `;
32
32
  beforeEach(() => {
33
33
  fs.mkdirSync(tempDir, { recursive: true });
@@ -39,23 +39,23 @@ coverage:
39
39
  }
40
40
  });
41
41
  it('does not flag no-manual-import for ordinary component/library imports in .test.tsx', () => {
42
- fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import { Button } from '../../src/components/Button/Button.js';
43
- import React from 'react';
44
-
45
- it('Sample component test', async () => {
46
- await Spectra.mount(<Button label="Save" />);
47
- });
42
+ fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import { Button } from '../../src/components/Button/Button.js';
43
+ import React from 'react';
44
+
45
+ it('Sample component test', async () => {
46
+ await Spectra.mount(<Button label="Save" />);
47
+ });
48
48
  `);
49
49
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
50
50
  const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
51
51
  expect(importErrors).toHaveLength(0);
52
52
  });
53
53
  it('still flags no-manual-import when a .test.tsx import resolves into page-objects/', () => {
54
- fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import ButtonComponent from '../../page-objects/ButtonComponent/web.js';
55
-
56
- it('Sample component test', async () => {
57
- await ButtonComponent.root.shouldBeVisible();
58
- });
54
+ fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import ButtonComponent from '../../page-objects/ButtonComponent/web.js';
55
+
56
+ it('Sample component test', async () => {
57
+ await ButtonComponent.root.shouldBeVisible();
58
+ });
59
59
  `);
60
60
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
61
61
  const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
@@ -63,50 +63,50 @@ it('Sample component test', async () => {
63
63
  expect(importErrors[0].message).toContain('page-objects');
64
64
  });
65
65
  it('still flags no-manual-import when a .test.tsx import resolves into support/steps/', () => {
66
- fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import verifyButtonState from '../../support/steps/verifyButtonState/web.step.js';
67
-
68
- it('Sample component test', async () => {
69
- await verifyButtonState('Save', true);
70
- });
66
+ fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import verifyButtonState from '../../support/steps/verifyButtonState/web.step.js';
67
+
68
+ it('Sample component test', async () => {
69
+ await verifyButtonState('Save', true);
70
+ });
71
71
  `);
72
72
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
73
73
  const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
74
74
  expect(importErrors).toHaveLength(1);
75
75
  });
76
76
  it('still flags no-manual-import when a .test.tsx import resolves into fixtures/', () => {
77
- fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import users from '../../fixtures/users.json';
78
-
79
- it('Sample component test', async () => {
80
- expect(users).toBeDefined();
81
- });
77
+ fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import users from '../../fixtures/users.json';
78
+
79
+ it('Sample component test', async () => {
80
+ expect(users).toBeDefined();
81
+ });
82
82
  `);
83
83
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
84
84
  const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
85
85
  expect(importErrors).toHaveLength(1);
86
86
  });
87
87
  it('still enforces single-it-required and no-describe-block in .test.tsx files', () => {
88
- fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import { Button } from '../../src/components/Button/Button.js';
89
-
90
- describe('Button', () => {
91
- it('Sample component test', async () => {
92
- await Spectra.mount(<Button label="Save" />);
93
- });
94
-
95
- it('Another test', async () => {
96
- await Spectra.mount(<Button label="Cancel" />);
97
- });
98
- });
88
+ fs.writeFileSync(path.join(tempDir, 'web.test.tsx'), `import { Button } from '../../src/components/Button/Button.js';
89
+
90
+ describe('Button', () => {
91
+ it('Sample component test', async () => {
92
+ await Spectra.mount(<Button label="Save" />);
93
+ });
94
+
95
+ it('Another test', async () => {
96
+ await Spectra.mount(<Button label="Cancel" />);
97
+ });
98
+ });
99
99
  `);
100
100
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
101
101
  expect(diagnostics.some((d) => d.code === 'testspectra/no-describe-block')).toBe(true);
102
102
  expect(diagnostics.some((d) => d.code === 'testspectra/single-it-required')).toBe(true);
103
103
  });
104
104
  it('still forbids all imports (unchanged behavior) in plain .test.ts E2E specs', () => {
105
- fs.writeFileSync(path.join(tempDir, 'web.test.ts'), `import { LoginPage } from '../../page-objects/LoginPage/web.js';
106
-
107
- it('Sample component test', async () => {
108
- await LoginPage.submitButton.click();
109
- });
105
+ fs.writeFileSync(path.join(tempDir, 'web.test.ts'), `import { LoginPage } from '../../page-objects/LoginPage/web.js';
106
+
107
+ it('Sample component test', async () => {
108
+ await LoginPage.submitButton.click();
109
+ });
110
110
  `);
111
111
  const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
112
112
  const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
@@ -17,16 +17,16 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
17
17
  }
18
18
  });
19
19
  describe('findSharedLibraryRoots()', () => {
20
- it('discovers parent folders of project.json files containing both scope:shared and type:shared', () => {
20
+ it('discovers parent folders of project.json files containing testspectra:shared', () => {
21
21
  const sharedDir1 = path.join(tempDir, 'shared', 'testing');
22
22
  const sharedDir2 = path.join(tempDir, 'libs', 'common-helpers');
23
23
  const nonSharedDir = path.join(tempDir, 'packages', 'playground', 'e2e');
24
24
  fs.mkdirSync(sharedDir1, { recursive: true });
25
25
  fs.mkdirSync(sharedDir2, { recursive: true });
26
26
  fs.mkdirSync(nonSharedDir, { recursive: true });
27
- fs.writeFileSync(path.join(sharedDir1, 'project.json'), JSON.stringify({ name: 'shared-testing', tags: ['scope:shared', 'type:shared'] }));
28
- fs.writeFileSync(path.join(sharedDir2, 'project.json'), JSON.stringify({ name: 'common-helpers', tags: ['scope:shared', 'type:shared'] }));
29
- fs.writeFileSync(path.join(nonSharedDir, 'project.json'), JSON.stringify({ name: 'playground-e2e', tags: ['scope:playground', 'type:e2e'] }));
27
+ fs.writeFileSync(path.join(sharedDir1, 'project.json'), JSON.stringify({ name: 'shared-testing', tags: ['testspectra:shared'] }));
28
+ fs.writeFileSync(path.join(sharedDir2, 'project.json'), JSON.stringify({ name: 'common-helpers', tags: ['testspectra:shared'] }));
29
+ fs.writeFileSync(path.join(nonSharedDir, 'project.json'), JSON.stringify({ name: 'playground-e2e', tags: ['testspectra:e2e', 'testspectra:scope:playground'] }));
30
30
  const roots = findSharedLibraryRoots(tempDir);
31
31
  expect(roots).toHaveLength(2);
32
32
  expect(roots).toContain(sharedDir1);
@@ -35,12 +35,12 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
35
35
  });
36
36
  });
37
37
  describe('isInsideSharedLibrary()', () => {
38
- it('returns true for files inside a project tagged with scope:shared or type:shared in project.json', () => {
38
+ it('returns true for files inside a project tagged with testspectra:shared in project.json', () => {
39
39
  const sharedDir = path.join(tempDir, 'shared', 'testing');
40
40
  fs.mkdirSync(sharedDir, { recursive: true });
41
41
  fs.writeFileSync(path.join(sharedDir, 'project.json'), JSON.stringify({
42
42
  name: 'shared-testing',
43
- tags: ['scope:shared', 'type:shared'],
43
+ tags: ['testspectra:shared'],
44
44
  }));
45
45
  const stepFile = path.join(sharedDir, 'support', 'steps', 'web.step.ts');
46
46
  const specFile = path.join(sharedDir, 'specs', 'TC-001', 'spec.md');
@@ -55,7 +55,7 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
55
55
  fs.mkdirSync(customLibDir, { recursive: true });
56
56
  fs.writeFileSync(path.join(customLibDir, 'project.json'), JSON.stringify({
57
57
  name: 'custom-common-lib',
58
- tags: ['scope:shared', 'type:shared'],
58
+ tags: ['testspectra:shared'],
59
59
  }));
60
60
  const actionFile = path.join(customLibDir, 'support', 'actions', 'click.action.ts');
61
61
  expect(isInsideSharedLibrary(actionFile)).toBe(true);
@@ -65,7 +65,7 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
65
65
  fs.mkdirSync(sharedAppDir, { recursive: true });
66
66
  fs.writeFileSync(path.join(sharedAppDir, 'project.json'), JSON.stringify({
67
67
  name: 'shared-app',
68
- tags: ['scope:shared-app', 'type:e2e'],
68
+ tags: ['testspectra:e2e', 'testspectra:scope:shared-app'],
69
69
  }));
70
70
  const specFile = path.join(sharedAppDir, 'specs', 'TC-001', 'spec.md');
71
71
  expect(isInsideSharedLibrary(specFile)).toBe(false);
@@ -75,7 +75,7 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
75
75
  fs.mkdirSync(featureDir, { recursive: true });
76
76
  fs.writeFileSync(path.join(featureDir, 'project.json'), JSON.stringify({
77
77
  name: 'playground-e2e',
78
- tags: ['scope:playground', 'type:e2e'],
78
+ tags: ['testspectra:e2e', 'testspectra:scope:playground'],
79
79
  }));
80
80
  const specFile = path.join(featureDir, 'specs', 'TC-001', 'spec.md');
81
81
  expect(isInsideSharedLibrary(specFile)).toBe(false);
@@ -91,7 +91,7 @@ describe('Shared Library Boundary (testspectra/no-specs-in-shared-lib)', () => {
91
91
  fs.mkdirSync(sharedLibDir, { recursive: true });
92
92
  fs.writeFileSync(path.join(sharedLibDir, 'project.json'), JSON.stringify({
93
93
  name: 'shared-testing',
94
- tags: ['scope:shared', 'type:shared'],
94
+ tags: ['testspectra:shared'],
95
95
  }));
96
96
  const sharedSpecDir = path.join(sharedLibDir, 'specs', 'TC-error');
97
97
  fs.mkdirSync(sharedSpecDir, { recursive: true });
@@ -119,7 +119,7 @@ status: draft
119
119
  fs.mkdirSync(sharedLibDir, { recursive: true });
120
120
  fs.writeFileSync(path.join(sharedLibDir, 'project.json'), JSON.stringify({
121
121
  name: 'shared-testing',
122
- tags: ['scope:shared', 'type:shared'],
122
+ tags: ['testspectra:shared'],
123
123
  }));
124
124
  const sharedSuiteDir = path.join(sharedLibDir, 'support');
125
125
  fs.mkdirSync(sharedSuiteDir, { recursive: true });
@@ -141,7 +141,7 @@ status: active
141
141
  fs.mkdirSync(customLibDir, { recursive: true });
142
142
  fs.writeFileSync(path.join(customLibDir, 'project.json'), JSON.stringify({
143
143
  name: 'my-testing-utils',
144
- tags: ['scope:shared', 'type:shared'],
144
+ tags: ['testspectra:shared'],
145
145
  }));
146
146
  const specDir = path.join(customLibDir, 'specs', 'TC-custom');
147
147
  fs.mkdirSync(specDir, { recursive: true });
@@ -169,7 +169,7 @@ status: draft
169
169
  fs.mkdirSync(featureDir, { recursive: true });
170
170
  fs.writeFileSync(path.join(featureDir, 'project.json'), JSON.stringify({
171
171
  name: 'playground-e2e',
172
- tags: ['scope:playground', 'type:e2e'],
172
+ tags: ['testspectra:e2e', 'testspectra:scope:playground'],
173
173
  }));
174
174
  const featureSpecDir = path.join(featureDir, 'specs', 'TC-001');
175
175
  fs.mkdirSync(featureSpecDir, { recursive: true });
@@ -195,7 +195,7 @@ status: draft
195
195
  fs.mkdirSync(sharedE2eDir, { recursive: true });
196
196
  fs.writeFileSync(path.join(sharedE2eDir, 'project.json'), JSON.stringify({
197
197
  name: 'shared-feature-e2e',
198
- tags: ['scope:shared-feature', 'type:e2e'],
198
+ tags: ['testspectra:e2e', 'testspectra:scope:shared-feature'],
199
199
  }));
200
200
  const specDir = path.join(sharedE2eDir, 'specs', 'TC-001');
201
201
  fs.mkdirSync(specDir, { recursive: true });
@@ -241,7 +241,7 @@ status: draft
241
241
  fs.mkdirSync(sharedLibDir, { recursive: true });
242
242
  fs.writeFileSync(path.join(sharedLibDir, 'project.json'), JSON.stringify({
243
243
  name: 'shared-testing',
244
- tags: ['scope:shared', 'type:shared'],
244
+ tags: ['testspectra:shared'],
245
245
  }));
246
246
  const sharedDir1 = path.join(sharedLibDir, 'specs', 'TC-001');
247
247
  const sharedDir2 = path.join(sharedLibDir, 'specs', 'TC-002');