@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
@@ -0,0 +1,69 @@
1
+ # TestSpectra React Component Testing Template
2
+
3
+ A high-performance, real-browser React component testing template powered by **TestSpectra**, **WebdriverIO Browser Runner**, and **Testing Library**.
4
+
5
+ ## 🚀 Features
6
+
7
+ - **Direct Component Mounting**: Mount TSX components directly in real Chromium/Blink engines using `@testing-library/react`.
8
+ - **Zero Mock Browser**: Real layout calculations, CSS rendering, focus states, and browser events without JSDOM/HappyDOM limitations.
9
+ - **Fast Vite HMR Core**: Instant test startup powered by Vite bundler inside the browser runner.
10
+ - **TestSpectra Matchers**: Comprehensive semantic assertions (`.shouldBeVisible()`, `.shouldExist()`, `expect(el).toHaveText()`).
11
+
12
+ ## 📁 Directory Structure
13
+
14
+ ```
15
+ .
16
+ ├── spectra.config.ts # TestSpectra centralized configuration
17
+ ├── wdio.conf.ts # WebdriverIO Browser Runner + Vite config
18
+ ├── tsconfig.json # TypeScript configuration for JSX & runner types
19
+ └── src/
20
+ ├── components/ # React components and tests
21
+ │ ├── Button/
22
+ │ │ ├── Button.tsx
23
+ │ │ └── Button.test.tsx
24
+ │ ├── Badge/
25
+ │ │ ├── Badge.tsx
26
+ │ │ └── Badge.test.tsx
27
+ │ └── Input/
28
+ │ ├── Input.tsx
29
+ │ └── Input.test.tsx
30
+ └── test-utils.tsx # Common testing helpers & providers
31
+ ```
32
+
33
+ ## 🛠️ Usage & Commands
34
+
35
+ ```bash
36
+ # Run all component tests
37
+ pnpm test
38
+
39
+ # Run component tests in headless mode
40
+ pnpm run test:headless
41
+
42
+ # Run tests in high-concurrency chunked worker mode
43
+ pnpm run test:chunked
44
+
45
+ # Type check
46
+ pnpm run type-check
47
+ ```
48
+
49
+ ## 📝 Writing Component Tests
50
+
51
+ ```tsx
52
+ import React from 'react';
53
+ import { render } from '@testing-library/react';
54
+ import { expect, $ } from '@wdio/globals';
55
+ import { Button } from './Button';
56
+
57
+ describe('Button Component', () => {
58
+ it('renders label and handles user clicks', async () => {
59
+ let clicked = false;
60
+ render(<Button label="Submit" onClick={() => (clicked = true)} />);
61
+
62
+ const button = await $('button=Submit');
63
+ await expect(button).toBeDisplayed();
64
+
65
+ await button.click();
66
+ expect(clicked).toBe(true);
67
+ });
68
+ });
69
+ ```
@@ -0,0 +1,12 @@
1
+ {
2
+ "user": {
3
+ "name": "Alex Mercer",
4
+ "role": "Developer",
5
+ "status": "active"
6
+ },
7
+ "labels": {
8
+ "save": "Save Changes",
9
+ "cancel": "Cancel",
10
+ "delete": "Delete Item"
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ export default async function () {
2
+ // Global after hook for web component testing suite
3
+ }
@@ -0,0 +1,3 @@
1
+ export default async function () {
2
+ // Global before hook for web component testing suite
3
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@testspectra/template-react-component",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "spectra test",
8
+ "test:headed": "spectra test --headed",
9
+ "spectra": "spectra",
10
+ "doctor": "spectra doctor",
11
+ "watch": "spectra watch",
12
+ "type-check": "tsc -b",
13
+ "sync-types": "spectra sync-types",
14
+ "postinstall": "spectra sync-types"
15
+ },
16
+ "devDependencies": {
17
+ "@testspectra/cli": "link:../..",
18
+ "@testspectra/matchers": "link:../../../tools/matchers",
19
+ "@testspectra/react": "link:../../../tools/react",
20
+ "@types/node": "^20.14.0",
21
+ "@types/react": "^18.3.3",
22
+ "@types/react-dom": "^18.3.0",
23
+ "react": "^18.3.1",
24
+ "react-dom": "^18.3.1",
25
+ "typescript": "^5.4.5"
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ class BadgeComponent {
2
+ /** Root badge element */
3
+ get root() {
4
+ return Spectra.get("[data-testid='testspectra-badge']");
5
+ }
6
+
7
+ /** Dot indicator element */
8
+ get dot() {
9
+ return Spectra.get("[data-testid='badge-dot']");
10
+ }
11
+
12
+ /** Badge label text element */
13
+ get label() {
14
+ return Spectra.get("[data-testid='badge-label']");
15
+ }
16
+
17
+ /** Lookup badge by severity attribute */
18
+ bySeverity(severity: 'info' | 'success' | 'warning' | 'danger') {
19
+ return Spectra.get(`[data-testid='testspectra-badge'][data-severity='${severity}']`);
20
+ }
21
+ }
22
+
23
+ export default new BadgeComponent();
@@ -0,0 +1,23 @@
1
+ class ButtonComponent {
2
+ /** Root button element */
3
+ get root() {
4
+ return Spectra.get("[data-testid='testspectra-button']");
5
+ }
6
+
7
+ /** Label element inside button */
8
+ get label() {
9
+ return Spectra.get("[data-testid='button-label']");
10
+ }
11
+
12
+ /** Optional icon element */
13
+ get icon() {
14
+ return Spectra.get("[data-testid='button-icon']");
15
+ }
16
+
17
+ /** Lookup button element by visible text */
18
+ byText(text: string) {
19
+ return Spectra.get(`button*=${text}`);
20
+ }
21
+ }
22
+
23
+ export default new ButtonComponent();
@@ -0,0 +1,13 @@
1
+ class InputComponent {
2
+ /** Root input element */
3
+ get root() {
4
+ return Spectra.get("[data-testid='testspectra-input']");
5
+ }
6
+
7
+ /** Lookup input element by placeholder substring */
8
+ byPlaceholder(placeholder: string) {
9
+ return Spectra.get(`input[placeholder*='${placeholder}']`);
10
+ }
11
+ }
12
+
13
+ export default new InputComponent();
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: TC-0002
3
+ title: Verify default badge with info severity
4
+ priority: Critical
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0002: Badge Default Rendering
12
+
13
+ Verify that `<Badge />` renders with the default `info` severity when no `severity` prop is provided.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Badge label="System Active" />`.
18
+ 2. Verify the badge is visible, shows the correct label text, and has `data-severity="info"`.
@@ -0,0 +1,11 @@
1
+ import { Badge } from '../../../src/components/Badge/Badge.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify default badge with info severity', async () => {
5
+ await Spectra.mount(withProviders(<Badge label="System Active" />));
6
+
7
+ // Assert via BadgeComponent component object
8
+ await BadgeComponent.root.shouldBeVisible();
9
+ await BadgeComponent.label.shouldHaveText('System Active');
10
+ await BadgeComponent.root.shouldHaveAttribute('data-severity', 'info');
11
+ });
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: TC-0006
3
+ title: Verify badge with success severity and dot indicator
4
+ priority: High
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0006: Badge Success Severity with Dot Indicator
12
+
13
+ Verify that `<Badge />` renders the `success` severity styling and shows a dot indicator when the `dot` prop is set.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Badge label="Completed" severity="success" dot />`.
18
+ 2. Verify the badge is visible with `data-severity="success"` and the dot indicator is visible.
@@ -0,0 +1,10 @@
1
+ import { Badge } from '../../../src/components/Badge/Badge.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify badge with success severity and dot indicator', async () => {
5
+ await Spectra.mount(withProviders(<Badge label="Completed" severity="success" dot />));
6
+
7
+ await BadgeComponent.root.shouldBeVisible();
8
+ await BadgeComponent.root.shouldHaveAttribute('data-severity', 'success');
9
+ await BadgeComponent.dot.shouldBeVisible();
10
+ });
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: TC-0007
3
+ title: Verify dynamic lookup using bySeverity helper
4
+ priority: Medium
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0007: Badge Lookup by Severity Helper
12
+
13
+ Verify that multiple `<Badge />` instances rendered together can be located individually via `BadgeComponent.bySeverity(...)`.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount two badges: `<Badge label="Warning Alert" severity="warning" />` and `<Badge label="Critical Error" severity="danger" />`.
18
+ 2. Verify each badge can be located and is visible via `BadgeComponent.bySeverity('warning')` and `BadgeComponent.bySeverity('danger')`.
@@ -0,0 +1,16 @@
1
+ import { Badge } from '../../../src/components/Badge/Badge.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify dynamic lookup using bySeverity helper', async () => {
5
+ await Spectra.mount(
6
+ withProviders(
7
+ <div>
8
+ <Badge label="Warning Alert" severity="warning" />
9
+ <Badge label="Critical Error" severity="danger" />
10
+ </div>,
11
+ ),
12
+ );
13
+
14
+ await BadgeComponent.bySeverity('warning').shouldBeVisible();
15
+ await BadgeComponent.bySeverity('danger').shouldBeVisible();
16
+ });
@@ -0,0 +1,12 @@
1
+ ---
2
+ id: badge-component-suite
3
+ name: Badge Component Suite
4
+ executionOrder: 2
5
+ tags:
6
+ - component
7
+ - badge
8
+ - status
9
+ ---
10
+
11
+ # Badge Component Testing Suite
12
+ Verification of badge labels, severity color variants, and dot indicators.
@@ -0,0 +1,19 @@
1
+ ---
2
+ id: TC-0001
3
+ title: Verify button render, text, and click with Spectra DSL
4
+ priority: Critical
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0001: Button Rendering and Click Interaction
12
+
13
+ Verify that the `<Button />` component renders with the requested label and variant, and fires its `onClick` callback when clicked.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Button label="Save Changes" variant="primary" />`.
18
+ 2. Verify the button is visible with the correct label and `data-variant` attribute.
19
+ 3. Click the button and verify the `onClick` callback fires.
@@ -0,0 +1,28 @@
1
+ import { Button } from '../../../src/components/Button/Button.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify button render, text, and click with Spectra DSL', async () => {
5
+ let isClicked = false;
6
+
7
+ // Spectra.mount() renders into the shared root — no explicit cleanup between mounts needed.
8
+ await Spectra.mount(
9
+ withProviders(
10
+ <Button
11
+ label="Save Changes"
12
+ variant="primary"
13
+ onClick={() => {
14
+ isClicked = true;
15
+ }}
16
+ />,
17
+ ),
18
+ );
19
+
20
+ // TestSpectra Ambient Component Object (ButtonComponent)
21
+ await ButtonComponent.root.shouldBeVisible();
22
+ await ButtonComponent.root.shouldHaveText('Save Changes');
23
+ await ButtonComponent.root.shouldHaveAttribute('data-variant', 'primary');
24
+ await ButtonComponent.root.click();
25
+
26
+ // Verify callback state
27
+ expect(isClicked).toBe(true);
28
+ });
@@ -0,0 +1,19 @@
1
+ ---
2
+ id: TC-0004
3
+ title: Verify disabled button cannot be clicked
4
+ priority: High
5
+ caseType: Negative
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0004: Disabled Button Interaction
12
+
13
+ Verify that a disabled `<Button />` renders in a disabled state and its `onClick` callback does not fire when clicked.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Button label="Disabled Action" disabled />`.
18
+ 2. Verify the button is visible and disabled.
19
+ 3. Attempt to click the button and verify the `onClick` callback does not fire.
@@ -0,0 +1,22 @@
1
+ import { Button } from '../../../src/components/Button/Button.js';
2
+
3
+ it('Verify disabled button cannot be clicked', async () => {
4
+ let isClicked = false;
5
+
6
+ await Spectra.mount(
7
+ <Button
8
+ label="Disabled Action"
9
+ disabled
10
+ onClick={() => {
11
+ isClicked = true;
12
+ }}
13
+ />,
14
+ );
15
+
16
+ await ButtonComponent.root.shouldBeVisible();
17
+ await ButtonComponent.root.shouldBeDisabled();
18
+
19
+ // Attempt click - should not fire callback
20
+ await ButtonComponent.root.click().catch(() => {});
21
+ expect(isClicked).toBe(false);
22
+ });
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: TC-0005
3
+ title: Verify button renders optional icon
4
+ priority: Medium
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0005: Button Optional Icon Rendering
12
+
13
+ Verify that `<Button />` renders an optional leading icon when the `icon` prop is provided.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Button label="Download Report" icon={...} />` with an icon element.
18
+ 2. Verify the icon is visible and renders the expected content.
@@ -0,0 +1,8 @@
1
+ import { Button } from '../../../src/components/Button/Button.js';
2
+
3
+ it('Verify button renders optional icon', async () => {
4
+ await Spectra.mount(<Button label="Download Report" icon={<span data-testid="button-icon">📥</span>} />);
5
+
6
+ await ButtonComponent.icon.shouldBeVisible();
7
+ await ButtonComponent.icon.shouldHaveText('📥');
8
+ });
@@ -0,0 +1,12 @@
1
+ ---
2
+ id: button-component-suite
3
+ name: Button Component Suite
4
+ executionOrder: 1
5
+ tags:
6
+ - component
7
+ - button
8
+ - atoms
9
+ ---
10
+
11
+ # Button Component Testing Suite
12
+ Verification of isolated button props, event listeners, accessibility, and visual variants.
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: TC-0003
3
+ title: Verify input placeholder and typing interaction
4
+ priority: Critical
5
+ caseType: Positive
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0003: Input Typing and Placeholder
12
+
13
+ Verify that `<Input />` renders its placeholder, accepts typed text, and fires `onChange` with the typed value.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Input placeholder="Enter email address" />` with an `onChange` handler.
18
+ 2. Verify the input is visible and the placeholder is discoverable.
19
+ 3. Type `test@example.com` into the input and verify the value updates.
20
+ 4. Verify the `onChange` handler received the typed value.
@@ -0,0 +1,28 @@
1
+ import { Input } from '../../../src/components/Input/Input.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify input placeholder and typing interaction', async () => {
5
+ let currentVal = '';
6
+
7
+ await Spectra.mount(
8
+ withProviders(
9
+ <Input
10
+ placeholder="Enter email address"
11
+ onChange={(e) => {
12
+ currentVal = e.target.value;
13
+ }}
14
+ />,
15
+ ),
16
+ );
17
+
18
+ // 1. Assert element is visible via InputComponent
19
+ await InputComponent.root.shouldBeVisible();
20
+ await InputComponent.byPlaceholder('email address').shouldBeVisible();
21
+
22
+ // 2. Type text using Spectra DSL
23
+ await InputComponent.root.type('test@example.com');
24
+ await InputComponent.root.shouldHaveValue('test@example.com');
25
+
26
+ // 3. Verify onChange state
27
+ expect(currentVal).toBe('test@example.com');
28
+ });
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: TC-0008
3
+ title: Verify disabled input cannot be typed into
4
+ priority: High
5
+ caseType: Negative
6
+ status: automated
7
+ coverage:
8
+ web: automated
9
+ ---
10
+
11
+ # TC-0008: Disabled Input State
12
+
13
+ Verify that a disabled `<Input />` renders in a disabled state.
14
+
15
+ ## Test Steps
16
+
17
+ 1. Mount `<Input disabled placeholder="Disabled field" />`.
18
+ 2. Verify the input is visible and disabled.
@@ -0,0 +1,9 @@
1
+ import { Input } from '../../../src/components/Input/Input.js';
2
+ import { withProviders } from '../../../src/test-utils.js';
3
+
4
+ it('Verify disabled input cannot be typed into', async () => {
5
+ await Spectra.mount(withProviders(<Input disabled placeholder="Disabled field" />));
6
+
7
+ await InputComponent.root.shouldBeVisible();
8
+ await InputComponent.root.shouldBeDisabled();
9
+ });
@@ -0,0 +1,12 @@
1
+ ---
2
+ id: input-component-suite
3
+ name: Input Component Suite
4
+ executionOrder: 3
5
+ tags:
6
+ - component
7
+ - input
8
+ - forms
9
+ ---
10
+
11
+ # Input Component Testing Suite
12
+ Verification of text inputs, placeholder labels, keyboard typing, and onChange events.
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "@testspectra/cli";
2
+
3
+ export default defineConfig({
4
+ webConfig: {
5
+ baseUrl: "http://localhost:5173",
6
+ maxConcurrentSessions: "4",
7
+ headless: true,
8
+ implicitWait: "5000",
9
+ pageLoadTimeout: "30000",
10
+ scriptTimeout: "30000",
11
+ parallelizationMode: "testcase",
12
+ },
13
+ browsers: [
14
+ {
15
+ id: "chrome-desktop",
16
+ type: "chrome",
17
+ mobileEmulation: false,
18
+ },
19
+ ],
20
+ executionConfig: {
21
+ networkMonitoringEnabled: true,
22
+ fastResponseTime: "200",
23
+ normalResponseTime: "1000",
24
+ monitoredDomains: [],
25
+ environmentVariables: [],
26
+ },
27
+ });
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+
3
+ export interface BadgeProps {
4
+ label: string;
5
+ severity?: 'info' | 'success' | 'warning' | 'danger';
6
+ dot?: boolean;
7
+ }
8
+
9
+ export const Badge: React.FC<BadgeProps> = ({
10
+ label,
11
+ severity = 'info',
12
+ dot = false,
13
+ }) => {
14
+ const getColors = () => {
15
+ switch (severity) {
16
+ case 'success':
17
+ return { bg: '#dcfce7', text: '#15803d' };
18
+ case 'warning':
19
+ return { bg: '#fef9c3', text: '#a16207' };
20
+ case 'danger':
21
+ return { bg: '#fee2e2', text: '#b91c1c' };
22
+ case 'info':
23
+ default:
24
+ return { bg: '#e0f2fe', text: '#0369a1' };
25
+ }
26
+ };
27
+
28
+ const { bg, text } = getColors();
29
+
30
+ return (
31
+ <span
32
+ data-testid="testspectra-badge"
33
+ data-severity={severity}
34
+ style={{
35
+ display: 'inline-flex',
36
+ alignItems: 'center',
37
+ gap: '6px',
38
+ padding: '2px 8px',
39
+ borderRadius: '9999px',
40
+ backgroundColor: bg,
41
+ color: text,
42
+ fontSize: '12px',
43
+ fontWeight: 600,
44
+ }}
45
+ >
46
+ {dot && (
47
+ <span
48
+ data-testid="badge-dot"
49
+ style={{
50
+ width: '6px',
51
+ height: '6px',
52
+ borderRadius: '50%',
53
+ backgroundColor: text,
54
+ }}
55
+ />
56
+ )}
57
+ <span data-testid="badge-label">{label}</span>
58
+ </span>
59
+ );
60
+ };