@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.
- package/bin/.testspectra-runner.hash +1 -1
- package/bin/spectra.js +0 -0
- package/bin/testspectra-runner.exe +0 -0
- package/demo-app/assets/index-BhlW-eXG.css +1 -0
- package/demo-app/assets/index-DOtRypCa.js +154 -0
- package/demo-app/index.html +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/__tests__/run-e2e.test.js +270 -29
- package/dist/commands/__tests__/run.test.js +92 -16
- package/dist/commands/demo.js +17 -7
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +154 -149
- package/dist/commands/lint.d.ts +5 -0
- package/dist/commands/lint.js +55 -0
- package/dist/commands/refactor.d.ts +2 -1
- package/dist/commands/refactor.js +129 -35
- package/dist/commands/run.d.ts +2 -3
- package/dist/commands/run.js +93 -28
- package/dist/commands/skills.d.ts +9 -0
- package/dist/commands/skills.js +28 -0
- package/dist/commands/sync-types.js +1 -0
- package/dist/commands/test.d.ts +17 -0
- package/dist/commands/test.js +104 -0
- package/dist/commands/watch.js +9 -3
- package/dist/config/loader.js +1 -1
- package/dist/config/schema.d.ts +5 -114
- package/dist/config/schema.js +1 -17
- package/dist/generator/__tests__/tsconfig-isolation.test.js +6 -2
- package/dist/generator/__tests__/type-generator.test.js +1 -1
- package/dist/generator/tsconfig-generator.js +44 -16
- package/dist/generator/type-generator.d.ts +24 -1
- package/dist/generator/type-generator.js +104 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +76 -40
- package/dist/linter/__tests__/anonymous-export.test.js +184 -0
- package/dist/linter/__tests__/component-test-imports.test.d.ts +1 -0
- package/dist/linter/__tests__/component-test-imports.test.js +115 -0
- package/dist/linter/__tests__/shared-lib-boundary.test.d.ts +1 -0
- package/dist/linter/__tests__/shared-lib-boundary.test.js +270 -0
- package/dist/linter/__tests__/spec-schema.test.d.ts +1 -0
- package/dist/linter/__tests__/spec-schema.test.js +369 -0
- package/dist/linter/__tests__/step-parity.test.d.ts +1 -0
- package/dist/linter/__tests__/step-parity.test.js +372 -0
- package/dist/linter/discovery.d.ts +30 -0
- package/dist/linter/discovery.js +249 -0
- package/dist/linter/extractor.d.ts +67 -0
- package/dist/linter/extractor.js +245 -0
- package/dist/linter/index.d.ts +7 -0
- package/dist/linter/index.js +7 -0
- package/dist/linter/parser.d.ts +24 -0
- package/dist/linter/parser.js +219 -0
- package/dist/linter/schemas/spec.schema.d.ts +83 -0
- package/dist/linter/schemas/spec.schema.js +150 -0
- package/dist/linter/schemas/suite.schema.d.ts +59 -0
- package/dist/linter/schemas/suite.schema.js +83 -0
- package/dist/linter/spec-linter.d.ts +23 -0
- package/dist/linter/spec-linter.js +630 -0
- package/dist/linter/types.d.ts +28 -0
- package/dist/linter/types.js +1 -0
- package/dist/refactor/__tests__/refactor-engine.test.js +698 -0
- package/dist/refactor/refactor-engine.d.ts +25 -0
- package/dist/refactor/refactor-engine.js +374 -42
- package/dist/reporter/semantic-formatter.js +22 -2
- package/dist/reporter/types.d.ts +1 -1
- package/dist/runner/bridge.d.ts +1 -1
- package/dist/runner/bridge.js +34 -21
- package/dist/runner/reporter.d.ts +2 -0
- package/dist/runner/reporter.js +11 -3
- package/dist/utils/__tests__/api-server.test.d.ts +1 -0
- package/dist/utils/__tests__/api-server.test.js +62 -0
- package/dist/utils/__tests__/demo-server.test.d.ts +1 -0
- package/dist/utils/__tests__/demo-server.test.js +35 -0
- package/dist/utils/api-server.d.ts +19 -0
- package/dist/utils/api-server.js +179 -0
- package/dist/utils/demo-server.d.ts +5 -0
- package/dist/utils/demo-server.js +8 -1
- package/package.json +29 -25
- package/templates/default/CLAUDE.md +16 -11
- package/templates/default/global-hooks/after/mobile.hook.ts +3 -3
- package/templates/default/global-hooks/after/web.hook.ts +3 -3
- package/templates/default/global-hooks/before/mobile.hook.ts +3 -3
- package/templates/default/global-hooks/before/web.hook.ts +3 -3
- package/templates/default/package.json +3 -3
- package/templates/default/page-objects/AuthPage/mobile.ts +5 -0
- package/templates/default/page-objects/AuthPage/web.ts +10 -0
- package/templates/default/page-objects/MatchersPage/mobile.ts +70 -46
- package/templates/default/page-objects/MatchersPage/web.ts +43 -37
- package/templates/default/page-objects/NavigationPage/mobile.ts +14 -14
- package/templates/default/page-objects/NavigationPage/web.ts +8 -8
- package/templates/default/page-objects/PlaygroundPage/mobile.ts +42 -36
- package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -11
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -12
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -27
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -16
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -12
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -24
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -0
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -0
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -0
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -0
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -0
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -0
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -0
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -0
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -0
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -0
- package/templates/default/specs/ApiInterception/suite.md +10 -9
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -0
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -0
- package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -0
- package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -0
- package/templates/default/specs/ApiSeeding/suite.md +9 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -0
- package/templates/default/specs/Authentication/suite.md +9 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +9 -9
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +17 -12
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -0
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -0
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -12
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +11 -11
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +17 -12
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -0
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -0
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -0
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -0
- package/templates/default/specs/DeepLink/suite.md +9 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +11 -11
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +17 -12
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +29 -29
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +19 -12
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
- package/templates/default/specs/ElementMatchers/suite.md +10 -9
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
- package/templates/default/specs/EnvironmentConfig/suite.md +9 -0
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -0
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -0
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -0
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -0
- package/templates/default/specs/PermissionRationale/suite.md +9 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +18 -12
- package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +18 -12
- package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
- package/templates/default/spectra.config.ts +52 -68
- package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -4
- package/templates/default/support/actions/fillCredentials/web.action.ts +4 -4
- package/templates/default/support/actions/seedSession/mobile.action.ts +9 -0
- package/templates/default/support/actions/seedSession/web.action.ts +13 -0
- package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +5 -5
- package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -5
- package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +6 -6
- package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -6
- package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db +0 -0
- package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm +0 -0
- package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +1577 -168
- package/templates/nx/.nx/workspace-data/d/server-process.json +3 -0
- package/templates/nx/.nx/workspace-data/file-map.json +464 -0
- package/templates/nx/.nx/workspace-data/lockfile.hash +1 -0
- package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
- package/templates/nx/.nx/workspace-data/parsed-lock-file.json +13485 -0
- package/templates/nx/.nx/workspace-data/project-graph.json +14389 -0
- package/templates/nx/CLAUDE.md +10 -10
- package/templates/nx/package.json +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -3
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -27
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -3
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -24
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +17 -17
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -3
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -3
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
- package/templates/nx/pnpm-lock.yaml +6663 -6663
- package/templates/nx/pnpm-workspace.yaml +9 -9
- package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +33 -33
- package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +91 -37
- package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -5
- package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -8
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -26
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +31 -31
- package/templates/nx/shared/testing/project.json +19 -19
- package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -4
- package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -4
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -3
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -5
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -4
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -6
- package/templates/nx/spectra.config.ts +52 -68
- package/templates/react-component/.cursorrules +17 -0
- package/templates/react-component/.vscode/extensions.json +5 -0
- package/templates/react-component/CLAUDE.md +14 -0
- package/templates/react-component/README.md +69 -0
- package/templates/react-component/fixtures/component-mock.json +12 -0
- package/templates/react-component/global-hooks/after/web.hook.ts +3 -0
- package/templates/react-component/global-hooks/before/web.hook.ts +3 -0
- package/templates/react-component/package.json +27 -0
- package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -0
- package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -0
- package/templates/react-component/page-objects/InputComponent/web.ts +13 -0
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -0
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -0
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -0
- package/templates/react-component/specs/BadgeComponent/suite.md +12 -0
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -0
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -0
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -0
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -0
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -0
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -0
- package/templates/react-component/specs/ButtonComponent/suite.md +12 -0
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -0
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -0
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -0
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -0
- package/templates/react-component/specs/InputComponent/suite.md +12 -0
- package/templates/react-component/spectra.config.ts +27 -0
- package/templates/react-component/src/components/Badge/Badge.tsx +60 -0
- package/templates/react-component/src/components/Button/Button.tsx +57 -0
- package/templates/react-component/src/components/Input/Input.tsx +41 -0
- package/templates/react-component/src/test-utils.tsx +23 -0
- package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -0
- package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -0
- package/templates/react-component/tsconfig.json +8 -0
- package/bin/testspectra-runner +0 -0
- package/demo-app/assets/index-DPfv5_EF.js +0 -116
- package/demo-app/assets/index-gIwu2EMV.css +0 -1
- package/dist/commands/__tests__/cloud-run.test.js +0 -93
- package/dist/commands/cloud-run.d.ts +0 -13
- package/dist/commands/cloud-run.js +0 -120
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -17
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -17
- /package/dist/{commands/__tests__/cloud-run.test.d.ts → linter/__tests__/anonymous-export.test.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { addCommand } from './commands/add.js';
|
|
3
|
-
import { cloudRunCommand } from './commands/cloud-run.js';
|
|
4
3
|
import { demoCommand } from './commands/demo.js';
|
|
5
4
|
import { devicesCommand } from './commands/devices.js';
|
|
6
5
|
import { doctorCommand } from './commands/doctor.js';
|
|
7
6
|
import { initCommand } from './commands/init.js';
|
|
8
7
|
import { installCommand } from './commands/install.js';
|
|
9
8
|
import { runCommand } from './commands/run.js';
|
|
9
|
+
import { testCommand } from './commands/test.js';
|
|
10
10
|
import { updateCommand } from './commands/update.js';
|
|
11
11
|
import { watchCommand } from './commands/watch.js';
|
|
12
12
|
import { syncTypesCommand } from './commands/sync-types.js';
|
|
13
13
|
import { refactorCommand } from './commands/refactor.js';
|
|
14
|
+
import { lintCommand } from './commands/lint.js';
|
|
14
15
|
import { licenseCommand } from './commands/license.js';
|
|
16
|
+
import { skillsInitCommand, skillsListCommand, skillsAddCommand, skillsUpdateCommand, skillsRemoveCommand, } from './commands/skills.js';
|
|
15
17
|
export * from './config/schema.js';
|
|
16
18
|
export * from './config/loader.js';
|
|
17
19
|
export * from './generator/index.js';
|
|
18
|
-
export * from './lifecycle/index.js';
|
|
19
20
|
export * from './refactor/index.js';
|
|
20
21
|
export * from './mobile/index.js';
|
|
22
|
+
export * from './linter/index.js';
|
|
21
23
|
export * from './types/generator.js';
|
|
22
24
|
export * from '@testspectra/matchers';
|
|
23
25
|
export function createCliProgram() {
|
|
@@ -26,7 +28,7 @@ export function createCliProgram() {
|
|
|
26
28
|
program
|
|
27
29
|
.command('init')
|
|
28
30
|
.description('Initialize testspectra.config.json and project directories')
|
|
29
|
-
.option('-t, --template <template>', 'Project template: default, nx')
|
|
31
|
+
.option('-t, --template <template>', 'Project template: default, nx, react-component')
|
|
30
32
|
.option('-f, --force', 'Overwrite existing configuration if present')
|
|
31
33
|
.action(initCommand);
|
|
32
34
|
program
|
|
@@ -39,13 +41,13 @@ export function createCliProgram() {
|
|
|
39
41
|
.command('install [dependency]')
|
|
40
42
|
.description('Install missing testing dependencies, browser drivers, and workspace packages')
|
|
41
43
|
.option('-f, --force', 'Force re-installation of dependencies')
|
|
42
|
-
.addHelpText('after', `
|
|
43
|
-
Examples:
|
|
44
|
-
$ spectra install Install all missing required dependencies automatically
|
|
45
|
-
$ spectra install chrome Install Google Chrome for Testing & matching Chromedriver
|
|
46
|
-
$ spectra install adb Install Google Android SDK Platform Tools (ADB)
|
|
47
|
-
$ spectra install bun Install Bun runtime
|
|
48
|
-
$ 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
|
|
49
51
|
$ spectra install wdio Install WebDriverIO core packages and matchers`)
|
|
50
52
|
.action(installCommand);
|
|
51
53
|
program
|
|
@@ -71,7 +73,16 @@ Examples:
|
|
|
71
73
|
.option('--to <path>', 'Destination file or directory to refactor to')
|
|
72
74
|
.option('--dry-run', 'Preview changes without modifying files')
|
|
73
75
|
.option('--json', 'Output results as JSON')
|
|
74
|
-
.action(
|
|
76
|
+
.action(async (...args) => {
|
|
77
|
+
await refactorCommand(...args);
|
|
78
|
+
});
|
|
79
|
+
program
|
|
80
|
+
.command('lint [path]')
|
|
81
|
+
.description('Validate spec and suite schemas, frontmatter, and step integrity')
|
|
82
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
83
|
+
.option('--strict', 'Treat warnings as errors and exit with code 1')
|
|
84
|
+
.option('--json', 'Output diagnostic results as JSON')
|
|
85
|
+
.action(lintCommand);
|
|
75
86
|
program
|
|
76
87
|
.command('doctor')
|
|
77
88
|
.description('Verify local environment prerequisites (ADB, Spectra Android Driver, Chrome, Bun)')
|
|
@@ -94,14 +105,50 @@ Examples:
|
|
|
94
105
|
.command('license [action] [key]')
|
|
95
106
|
.description('Manage TestSpectra commercial license (activate, status, remove)')
|
|
96
107
|
.option('--json', 'Output license details as JSON')
|
|
97
|
-
.addHelpText('after', `
|
|
98
|
-
Examples:
|
|
99
|
-
$ spectra license activate <KEY> Activate license key and auto-configure private driver registry
|
|
100
|
-
$ 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
|
|
101
112
|
$ spectra license remove Remove locally activated license credentials`)
|
|
102
113
|
.action(licenseCommand);
|
|
114
|
+
const skills = program
|
|
115
|
+
.command('skills')
|
|
116
|
+
.description('Manage AI agent skills that standardize TestSpectra script authoring (@testspectra/skills)');
|
|
117
|
+
skills
|
|
118
|
+
.command('init')
|
|
119
|
+
.description('Prompt which AI agent(s) you use and save the choice for future add/update/remove/list')
|
|
120
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
121
|
+
.action(skillsInitCommand);
|
|
122
|
+
skills
|
|
123
|
+
.command('list')
|
|
124
|
+
.alias('ls')
|
|
125
|
+
.description('List all available and installed TestSpectra AI agent skills')
|
|
126
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
127
|
+
.option('-a, --agent <ids>', 'One-off override: comma-separated agent(s) to target, bypassing the saved "init" selection (claude, opencode, kilo, agents, gemini)')
|
|
128
|
+
.action(skillsListCommand);
|
|
129
|
+
skills
|
|
130
|
+
.command('add [skillIds...]')
|
|
131
|
+
.alias('install')
|
|
132
|
+
.description('Install specified skills into the detected AI agent directories (interactive if none provided)')
|
|
133
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
134
|
+
.option('-a, --agent <ids>', 'One-off override: comma-separated agent(s) to target, bypassing the saved "init" selection (claude, opencode, kilo, agents, gemini)')
|
|
135
|
+
.action(skillsAddCommand);
|
|
136
|
+
skills
|
|
137
|
+
.command('update [skillIds...]')
|
|
138
|
+
.alias('up')
|
|
139
|
+
.description('Update installed skills to the current bundled version (defaults to all installed)')
|
|
140
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
141
|
+
.option('-a, --agent <ids>', 'One-off override: comma-separated agent(s) to target, bypassing the saved "init" selection (claude, opencode, kilo, agents, gemini)')
|
|
142
|
+
.action(skillsUpdateCommand);
|
|
143
|
+
skills
|
|
144
|
+
.command('remove <skillIds...>')
|
|
145
|
+
.alias('rm')
|
|
146
|
+
.description('Remove specified skills from the detected AI agent directories')
|
|
147
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
148
|
+
.option('-a, --agent <ids>', 'One-off override: comma-separated agent(s) to target, bypassing the saved "init" selection (claude, opencode, kilo, agents, gemini)')
|
|
149
|
+
.action(skillsRemoveCommand);
|
|
103
150
|
program
|
|
104
|
-
.command('run [
|
|
151
|
+
.command('run [paths...]')
|
|
105
152
|
.description('Execute test suite or individual test case')
|
|
106
153
|
.option('-s, --spec <path>', 'Spec file path to execute')
|
|
107
154
|
.option('-t, --target <target>', 'Target platform: web, android, ios, common', 'web')
|
|
@@ -111,7 +158,6 @@ Examples:
|
|
|
111
158
|
.option('--devices <list>', 'Comma-separated list of device UDIDs to run on')
|
|
112
159
|
.option('--license-key <key>', 'Explicit commercial license key override')
|
|
113
160
|
.option('--demo', 'Automatically launch built-in demo web app server during test execution')
|
|
114
|
-
.option('--base-url <url>', "Override spectra.config.ts's webConfig.baseUrl for this run (e.g. a dev server started by CI just for this job)")
|
|
115
161
|
.option('--headless', 'Run in headless mode')
|
|
116
162
|
.option('--no-headless', 'Run in headed mode')
|
|
117
163
|
.option('--headed', 'Run browser in visible GUI mode (alias for --no-headless)')
|
|
@@ -122,29 +168,19 @@ Examples:
|
|
|
122
168
|
.option('-w, --workdir <dir>', 'Custom app data / temp workdir')
|
|
123
169
|
.option('-o, --output <path>', 'Custom test report JSON output path')
|
|
124
170
|
.action(runCommand);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
.
|
|
129
|
-
.
|
|
130
|
-
.option('--
|
|
131
|
-
.option('--
|
|
132
|
-
.option('--
|
|
133
|
-
.option('--
|
|
134
|
-
.option('--
|
|
135
|
-
.option('--
|
|
136
|
-
.option('--
|
|
137
|
-
.
|
|
138
|
-
.option('--base-url <url>', 'Override spectra.config.ts\'s webConfig.baseUrl for this run — for a dev server the CI runner itself started in the background and exposed at a reachable URL, forwarded to the cloud server as `spectra run --base-url`')
|
|
139
|
-
.addHelpText('after', `
|
|
140
|
-
Requires SPECTRA_URL and SPECTRA_TOKEN (a Gitea Personal Access Token) as
|
|
141
|
-
environment variables — see docs/v2/cloud/git-spec-standards-and-workflow.md.
|
|
142
|
-
|
|
143
|
-
Examples:
|
|
144
|
-
$ spectra cloud run --project org/repo --branch main --platform web --wait
|
|
145
|
-
$ spectra cloud run --project org/repo --no-wait
|
|
146
|
-
$ spectra cloud run --project org/repo --base-url http://192.168.1.50:5173 --wait`)
|
|
147
|
-
.action(cloudRunCommand);
|
|
171
|
+
program
|
|
172
|
+
.command('test')
|
|
173
|
+
.description('Run React component tests via @testspectra/react (bundled Vitest browser mode)')
|
|
174
|
+
.option('-w, --cwd <dir>', 'Custom workspace directory')
|
|
175
|
+
.option('-s, --spec <path>', 'Spec file path (or substring filter) to execute')
|
|
176
|
+
.option('--headless', 'Run in headless mode (default)')
|
|
177
|
+
.option('--no-headless', 'Run in headed mode')
|
|
178
|
+
.option('--headed', 'Run the browser in visible GUI mode (alias for --no-headless)')
|
|
179
|
+
.option('--step-delay <ms>', 'Delay after each action/assertion in milliseconds (slow motion, for observing DOM transitions in --headed mode)')
|
|
180
|
+
.option('--delay <ms>', 'Alias for --step-delay')
|
|
181
|
+
.option('-c, --concurrency <number>', 'Run spec files sequentially (1) or in parallel (>1, default). Browser mode has no numeric N-worker sizing like `spectra run`.')
|
|
182
|
+
.option('-o, --output <path>', 'Write a Vitest JSON test report to this path')
|
|
183
|
+
.action(testCommand);
|
|
148
184
|
return program;
|
|
149
185
|
}
|
|
150
186
|
export async function runCli(args = process.argv) {
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { extractModuleExports } from '../extractor.js';
|
|
6
|
+
import { SpecLinter } from '../spec-linter.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
describe('Anonymous Default Export (testspectra/require-anonymous-default-export)', () => {
|
|
10
|
+
const tempDir = path.join(__dirname, '__temp_anonymous_export__');
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
if (fs.existsSync(tempDir)) {
|
|
16
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
describe('extractModuleExports()', () => {
|
|
20
|
+
it('detects valid anonymous async default export in .step.ts', () => {
|
|
21
|
+
const code = `export default async function () {
|
|
22
|
+
await LoginPage.open();
|
|
23
|
+
}`;
|
|
24
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
25
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(true);
|
|
26
|
+
expect(result.hasNamedExports).toBe(false);
|
|
27
|
+
expect(result.testBlocks).toHaveLength(0);
|
|
28
|
+
});
|
|
29
|
+
it('detects valid anonymous async arrow function default export in .step.ts', () => {
|
|
30
|
+
const code = `export default async () => {
|
|
31
|
+
await LoginPage.open();
|
|
32
|
+
};`;
|
|
33
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
34
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(true);
|
|
35
|
+
expect(result.hasNamedExports).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it('detects named function with export default (forbidden)', () => {
|
|
38
|
+
const code = `export default async function login() {
|
|
39
|
+
await LoginPage.open();
|
|
40
|
+
}`;
|
|
41
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
42
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
43
|
+
expect(result.hasNamedExports).toBe(true);
|
|
44
|
+
expect(result.namedExportLines).toHaveLength(1);
|
|
45
|
+
expect(result.namedExportLines[0].name).toBe('login');
|
|
46
|
+
expect(result.namedExportLines[0].line).toBe(1);
|
|
47
|
+
});
|
|
48
|
+
it('detects named export function (forbidden)', () => {
|
|
49
|
+
const code = `export async function verifyUser() {
|
|
50
|
+
await UserPage.verify();
|
|
51
|
+
}`;
|
|
52
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
53
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
54
|
+
expect(result.hasNamedExports).toBe(true);
|
|
55
|
+
expect(result.namedExportLines[0].name).toBe('verifyUser');
|
|
56
|
+
});
|
|
57
|
+
it('detects named export class (forbidden)', () => {
|
|
58
|
+
const code = `export class Helper {}`;
|
|
59
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
60
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
61
|
+
expect(result.hasNamedExports).toBe(true);
|
|
62
|
+
expect(result.namedExportLines[0].name).toBe('Helper');
|
|
63
|
+
});
|
|
64
|
+
it('detects export { ... } clauses (forbidden)', () => {
|
|
65
|
+
const code = `const a = 1; export { a };`;
|
|
66
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
67
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
68
|
+
expect(result.hasNamedExports).toBe(true);
|
|
69
|
+
expect(result.namedExportLines[0].name).toBe('a');
|
|
70
|
+
});
|
|
71
|
+
it('detects named arrow function with export default (forbidden)', () => {
|
|
72
|
+
const code = `const fn = async () => {}; export default async function named() {}`;
|
|
73
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
74
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
75
|
+
expect(result.hasNamedExports).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
it('detects valid anonymous async default export in .action.ts', () => {
|
|
78
|
+
const code = `export default async function (email: string) {
|
|
79
|
+
await this.get("#email").type(email);
|
|
80
|
+
}`;
|
|
81
|
+
const result = extractModuleExports(code, 'web.action.ts');
|
|
82
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(true);
|
|
83
|
+
expect(result.hasNamedExports).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
it('detects valid anonymous async default export in .hook.ts', () => {
|
|
86
|
+
const code = `export default async function () {}`;
|
|
87
|
+
const result = extractModuleExports(code, 'web.hook.ts');
|
|
88
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(true);
|
|
89
|
+
expect(result.hasNamedExports).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it('detects missing default export', () => {
|
|
92
|
+
const code = `function verifyLogin() {
|
|
93
|
+
await LoginPage.open();
|
|
94
|
+
}`;
|
|
95
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
96
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
it('detects non-async default export as invalid', () => {
|
|
99
|
+
const code = `export default function verifyLogin() {
|
|
100
|
+
console.log("hello");
|
|
101
|
+
}`;
|
|
102
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
103
|
+
expect(result.hasAnonymousAsyncDefaultExport).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
it('detects forbidden it() and describe() blocks in shared module', () => {
|
|
106
|
+
const code = `export default async function () {}
|
|
107
|
+
it("test block", async () => {});
|
|
108
|
+
describe("suite block", () => {});
|
|
109
|
+
`;
|
|
110
|
+
const result = extractModuleExports(code, 'web.step.ts');
|
|
111
|
+
expect(result.testBlocks).toHaveLength(2);
|
|
112
|
+
expect(result.testBlocks[0].name).toBe('it');
|
|
113
|
+
expect(result.testBlocks[1].name).toBe('describe');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('testspectra/require-anonymous-default-export workspace validation', () => {
|
|
117
|
+
it('emits testspectra/require-anonymous-default-export when shared step lacks anonymous async default export', () => {
|
|
118
|
+
const stepsDir = path.join(tempDir, 'shared', 'testing', 'support', 'steps', 'login');
|
|
119
|
+
fs.mkdirSync(stepsDir, { recursive: true });
|
|
120
|
+
fs.writeFileSync(path.join(stepsDir, 'web.step.ts'), `function verifyLogin() {
|
|
121
|
+
console.log("no export");
|
|
122
|
+
}
|
|
123
|
+
`);
|
|
124
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
125
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
126
|
+
expect(errors.length).toBeGreaterThanOrEqual(1);
|
|
127
|
+
expect(errors[0].message).toContain('anonymous async function as default export');
|
|
128
|
+
});
|
|
129
|
+
it('passes validation when shared step has valid anonymous async default export', () => {
|
|
130
|
+
const stepsDir = path.join(tempDir, 'shared', 'testing', 'support', 'steps', 'login');
|
|
131
|
+
fs.mkdirSync(stepsDir, { recursive: true });
|
|
132
|
+
fs.writeFileSync(path.join(stepsDir, 'web.step.ts'), `export default async function () {
|
|
133
|
+
await LoginPage.open();
|
|
134
|
+
}
|
|
135
|
+
`);
|
|
136
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
137
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
138
|
+
expect(errors).toHaveLength(0);
|
|
139
|
+
});
|
|
140
|
+
it('emits testspectra/require-anonymous-default-export when shared action has named exports', () => {
|
|
141
|
+
const actionsDir = path.join(tempDir, 'shared', 'testing', 'support', 'actions', 'fillForm');
|
|
142
|
+
fs.mkdirSync(actionsDir, { recursive: true });
|
|
143
|
+
fs.writeFileSync(path.join(actionsDir, 'web.action.ts'), `export default async function () {}
|
|
144
|
+
export function helper() {}
|
|
145
|
+
`);
|
|
146
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
147
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
148
|
+
expect(errors.length).toBeGreaterThanOrEqual(1);
|
|
149
|
+
expect(errors.some((d) => d.message.includes('Named export'))).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
it('emits testspectra/require-anonymous-default-export when shared hook has test blocks', () => {
|
|
152
|
+
const hooksDir = path.join(tempDir, 'shared', 'testing', 'global-hooks', 'before');
|
|
153
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
154
|
+
fs.writeFileSync(path.join(hooksDir, 'web.hook.ts'), `export default async function () {}
|
|
155
|
+
it("should run", async () => {});
|
|
156
|
+
`);
|
|
157
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
158
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
159
|
+
expect(errors.length).toBeGreaterThanOrEqual(1);
|
|
160
|
+
expect(errors.some((d) => d.message.includes('it() block is forbidden'))).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('does NOT emit testspectra/require-anonymous-default-export for valid shared step files', () => {
|
|
163
|
+
const stepsDir = path.join(tempDir, 'shared', 'testing', 'support', 'steps', 'verify');
|
|
164
|
+
fs.mkdirSync(stepsDir, { recursive: true });
|
|
165
|
+
fs.writeFileSync(path.join(stepsDir, 'web.step.ts'), `export default async function () {
|
|
166
|
+
await Page.open();
|
|
167
|
+
await Page.element.should("be.visible");
|
|
168
|
+
}
|
|
169
|
+
`);
|
|
170
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
171
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
172
|
+
expect(errors).toHaveLength(0);
|
|
173
|
+
});
|
|
174
|
+
it('does NOT emit testspectra/require-anonymous-default-export for files outside shared/ directory', () => {
|
|
175
|
+
const featureDir = path.join(tempDir, 'packages', 'playground', 'e2e');
|
|
176
|
+
fs.mkdirSync(featureDir, { recursive: true });
|
|
177
|
+
fs.writeFileSync(path.join(featureDir, 'helper.ts'), `export function helper() {}
|
|
178
|
+
`);
|
|
179
|
+
const result = SpecLinter.validateWorkspace(tempDir);
|
|
180
|
+
const errors = result.diagnostics.filter((d) => d.code === 'testspectra/require-anonymous-default-export');
|
|
181
|
+
expect(errors).toHaveLength(0);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { SpecLinter } from '../spec-linter.js';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
/**
|
|
9
|
+
* Covers the component-testing `.tsx` import exception (see
|
|
10
|
+
* docs/v2/features/component-testing/react/mount-convention.md §"Static Imports Are Allowed
|
|
11
|
+
* Here"): imports are allowed in `.test.tsx` files EXCEPT ones resolving into ambient-global-
|
|
12
|
+
* backed directories (page-objects/, support/steps/, fixtures/). Structural rules
|
|
13
|
+
* (single-it-required, no-describe-block) stay fully enforced for `.tsx` exactly as for `.ts`.
|
|
14
|
+
*/
|
|
15
|
+
describe('Component Test (.tsx) Import Exception', () => {
|
|
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.
|
|
31
|
+
`;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
34
|
+
fs.writeFileSync(path.join(tempDir, 'spec.md'), specFrontmatter);
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
if (fs.existsSync(tempDir)) {
|
|
38
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
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
|
+
});
|
|
48
|
+
`);
|
|
49
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
50
|
+
const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
|
|
51
|
+
expect(importErrors).toHaveLength(0);
|
|
52
|
+
});
|
|
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
|
+
});
|
|
59
|
+
`);
|
|
60
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
61
|
+
const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
|
|
62
|
+
expect(importErrors).toHaveLength(1);
|
|
63
|
+
expect(importErrors[0].message).toContain('page-objects');
|
|
64
|
+
});
|
|
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
|
+
});
|
|
71
|
+
`);
|
|
72
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
73
|
+
const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
|
|
74
|
+
expect(importErrors).toHaveLength(1);
|
|
75
|
+
});
|
|
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
|
+
});
|
|
82
|
+
`);
|
|
83
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
84
|
+
const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
|
|
85
|
+
expect(importErrors).toHaveLength(1);
|
|
86
|
+
});
|
|
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
|
+
});
|
|
99
|
+
`);
|
|
100
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
101
|
+
expect(diagnostics.some((d) => d.code === 'testspectra/no-describe-block')).toBe(true);
|
|
102
|
+
expect(diagnostics.some((d) => d.code === 'testspectra/single-it-required')).toBe(true);
|
|
103
|
+
});
|
|
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
|
+
});
|
|
110
|
+
`);
|
|
111
|
+
const { diagnostics } = SpecLinter.validateSpecFile(path.join(tempDir, 'spec.md'));
|
|
112
|
+
const importErrors = diagnostics.filter((d) => d.code === 'testspectra/no-manual-import');
|
|
113
|
+
expect(importErrors).toHaveLength(1);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|