@testspectra/cli 1.1.8-rc.2 → 1.1.8-rc.21
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/README.md +47 -0
- package/bin/.testspectra-runner.hash +1 -1
- package/bin/spectra.js +0 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/__tests__/cloud-run.test.d.ts +1 -0
- package/dist/commands/__tests__/cloud-run.test.js +93 -0
- package/dist/commands/__tests__/run-e2e.test.js +49 -49
- package/dist/commands/add.js +2 -1
- package/dist/commands/cloud-run.d.ts +13 -0
- package/dist/commands/cloud-run.js +120 -0
- package/dist/commands/devices.js +1 -0
- package/dist/commands/docs.d.ts +4 -0
- package/dist/commands/docs.js +17 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/init.js +7 -160
- package/dist/commands/install.js +2 -0
- package/dist/commands/license.js +1 -0
- package/dist/commands/test.js +5 -3
- package/dist/generator/__tests__/tsconfig-isolation.test.js +2 -1
- package/dist/generator/__tests__/type-generator.test.js +38 -0
- package/dist/generator/architecture-doc-generator.d.ts +14 -0
- package/dist/generator/architecture-doc-generator.js +82 -0
- package/dist/generator/index.d.ts +1 -0
- package/dist/generator/index.js +1 -0
- package/dist/generator/tsconfig-generator.js +1 -1
- package/dist/generator/type-generator.d.ts +5 -2
- package/dist/generator/type-generator.js +14 -17
- package/dist/index.js +18 -11
- package/dist/linter/__tests__/component-test-imports.test.js +51 -51
- package/dist/linter/__tests__/shared-lib-boundary.test.js +15 -15
- package/dist/linter/__tests__/spec-schema.test.js +167 -167
- package/dist/linter/discovery.d.ts +3 -3
- package/dist/linter/discovery.js +5 -5
- package/dist/linter/schemas/spec.schema.d.ts +2 -2
- package/dist/linter/schemas/suite.schema.d.ts +4 -4
- package/dist/runner/bridge.js +53 -4
- package/package.json +34 -29
- package/templates/default/CLAUDE.md +16 -16
- 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 +2 -2
- package/templates/default/page-objects/AuthPage/mobile.ts +5 -5
- package/templates/default/page-objects/AuthPage/web.ts +10 -10
- package/templates/default/page-objects/MatchersPage/mobile.ts +70 -70
- package/templates/default/page-objects/MatchersPage/web.ts +43 -43
- 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 -42
- package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -53
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -19
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -31
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -3
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -3
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -27
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -19
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -26
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -30
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -22
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -27
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -20
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -24
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -26
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -37
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -21
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -29
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -16
- package/templates/default/specs/ApiInterception/suite.md +10 -10
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -30
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -23
- package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -16
- package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -28
- package/templates/default/specs/ApiSeeding/suite.md +9 -9
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -7
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -19
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -9
- package/templates/default/specs/Authentication/suite.md +9 -9
- 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 -17
- 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 -17
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -28
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -26
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -26
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -19
- 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 -17
- 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 -14
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -21
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -28
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -29
- package/templates/default/specs/DeepLink/suite.md +9 -9
- 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 -17
- 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 -19
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
- package/templates/default/specs/ElementMatchers/suite.md +10 -10
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
- package/templates/default/specs/EnvironmentConfig/suite.md +9 -9
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -26
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -22
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -13
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -20
- package/templates/default/specs/PermissionRationale/suite.md +9 -9
- 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 -18
- 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 -18
- package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
- package/templates/default/spectra.config.ts +52 -52
- 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 -9
- package/templates/default/support/actions/seedSession/web.action.ts +13 -13
- 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/docs/ARCHITECTURE.default.md +26 -0
- package/templates/docs/ARCHITECTURE.nx.md +119 -0
- package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db-shm} +0 -0
- package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal} +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +1090 -1577
- package/templates/nx/.nx/workspace-data/d/server-process.json +1 -1
- package/templates/nx/.nx/workspace-data/file-map.json +56 -56
- package/templates/nx/.nx/workspace-data/lockfile.hash +1 -1
- package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
- package/templates/nx/.nx/workspace-data/parsed-lock-file.json +185 -6681
- package/templates/nx/.nx/workspace-data/project-graph.json +190 -7434
- package/templates/nx/CLAUDE.md +10 -10
- package/templates/nx/package.json +3 -3
- package/templates/nx/packages/matchers/e2e/project.json +2 -2
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -27
- 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 -24
- 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 -5
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -9
- package/templates/nx/packages/playground/e2e/project.json +2 -2
- 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 -91
- 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 +18 -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 -52
- package/templates/react-component/.cursorrules +17 -17
- package/templates/react-component/.vscode/extensions.json +5 -5
- package/templates/react-component/CLAUDE.md +14 -14
- package/templates/react-component/README.md +69 -69
- package/templates/react-component/fixtures/component-mock.json +12 -12
- package/templates/react-component/global-hooks/after/web.hook.ts +3 -3
- package/templates/react-component/global-hooks/before/web.hook.ts +3 -3
- package/templates/react-component/package.json +27 -27
- package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -23
- package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -23
- package/templates/react-component/page-objects/InputComponent/web.ts +13 -13
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -11
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -10
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -16
- package/templates/react-component/specs/BadgeComponent/suite.md +12 -12
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -19
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -28
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -19
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -22
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -18
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -8
- package/templates/react-component/specs/ButtonComponent/suite.md +12 -12
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -20
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -28
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -18
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -9
- package/templates/react-component/specs/InputComponent/suite.md +12 -12
- package/templates/react-component/spectra.config.ts +27 -27
- package/templates/react-component/src/components/Badge/Badge.tsx +60 -60
- package/templates/react-component/src/components/Button/Button.tsx +57 -57
- package/templates/react-component/src/components/Input/Input.tsx +41 -41
- package/templates/react-component/src/test-utils.tsx +23 -23
- package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -9
- package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -12
- package/bin/testspectra-runner.exe +0 -0
- /package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
|
@@ -19,23 +19,23 @@ describe('Spec Schema Linter (Stage 1 & Stage 2)', () => {
|
|
|
19
19
|
describe('Spec File Validation (spec.md)', () => {
|
|
20
20
|
it('passes validation for a well-formed spec.md with frontmatter, H1 title, and Test Steps', () => {
|
|
21
21
|
const specPath = path.join(tempDir, 'spec.md');
|
|
22
|
-
fs.writeFileSync(specPath, `---
|
|
23
|
-
id: TC-0001-valid
|
|
24
|
-
title: Verify user authentication
|
|
25
|
-
priority: High
|
|
26
|
-
caseType: Positive
|
|
27
|
-
status: ready-for-automation
|
|
28
|
-
coverage:
|
|
29
|
-
web: automated
|
|
30
|
-
mobile: automated
|
|
31
|
-
tags:
|
|
32
|
-
- auth
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
# Verify user authentication
|
|
36
|
-
|
|
37
|
-
## Test Steps
|
|
38
|
-
1. Open login page and submit credentials
|
|
22
|
+
fs.writeFileSync(specPath, `---
|
|
23
|
+
id: TC-0001-valid
|
|
24
|
+
title: Verify user authentication
|
|
25
|
+
priority: High
|
|
26
|
+
caseType: Positive
|
|
27
|
+
status: ready-for-automation
|
|
28
|
+
coverage:
|
|
29
|
+
web: automated
|
|
30
|
+
mobile: automated
|
|
31
|
+
tags:
|
|
32
|
+
- auth
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Verify user authentication
|
|
36
|
+
|
|
37
|
+
## Test Steps
|
|
38
|
+
1. Open login page and submit credentials
|
|
39
39
|
`);
|
|
40
40
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
41
41
|
expect(result.diagnostics).toHaveLength(0);
|
|
@@ -43,16 +43,16 @@ tags:
|
|
|
43
43
|
});
|
|
44
44
|
it('reports testspectra/missing-frontmatter when required frontmatter attributes are missing', () => {
|
|
45
45
|
const specPath = path.join(tempDir, 'spec.md');
|
|
46
|
-
fs.writeFileSync(specPath, `---
|
|
47
|
-
id: TC-0002-missing-attrs
|
|
48
|
-
title: Missing priority and caseType
|
|
49
|
-
status: draft
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
# Missing priority and caseType
|
|
53
|
-
|
|
54
|
-
## Test Steps
|
|
55
|
-
1. Step one
|
|
46
|
+
fs.writeFileSync(specPath, `---
|
|
47
|
+
id: TC-0002-missing-attrs
|
|
48
|
+
title: Missing priority and caseType
|
|
49
|
+
status: draft
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Missing priority and caseType
|
|
53
|
+
|
|
54
|
+
## Test Steps
|
|
55
|
+
1. Step one
|
|
56
56
|
`);
|
|
57
57
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
58
58
|
const codes = result.diagnostics.map((d) => d.code);
|
|
@@ -69,17 +69,17 @@ status: draft
|
|
|
69
69
|
it('differentiates missing status (testspectra/missing-frontmatter) from invalid status value (testspectra/invalid-frontmatter-enum)', () => {
|
|
70
70
|
// 1. Missing status -> testspectra/missing-frontmatter
|
|
71
71
|
const missingSpecPath = path.join(tempDir, 'missing-status.spec.md');
|
|
72
|
-
fs.writeFileSync(missingSpecPath, `---
|
|
73
|
-
id: TC-0002-missing-status
|
|
74
|
-
title: Missing Status Spec
|
|
75
|
-
priority: High
|
|
76
|
-
caseType: Positive
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
# Missing Status Spec
|
|
80
|
-
|
|
81
|
-
## Test Steps
|
|
82
|
-
1. Step one
|
|
72
|
+
fs.writeFileSync(missingSpecPath, `---
|
|
73
|
+
id: TC-0002-missing-status
|
|
74
|
+
title: Missing Status Spec
|
|
75
|
+
priority: High
|
|
76
|
+
caseType: Positive
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
# Missing Status Spec
|
|
80
|
+
|
|
81
|
+
## Test Steps
|
|
82
|
+
1. Step one
|
|
83
83
|
`);
|
|
84
84
|
const missingResult = SpecLinter.validateSpecFile(missingSpecPath);
|
|
85
85
|
const missingStatusDiag = missingResult.diagnostics.find((d) => d.code === 'testspectra/missing-frontmatter' &&
|
|
@@ -89,18 +89,18 @@ caseType: Positive
|
|
|
89
89
|
expect(missingResult.diagnostics.some((d) => d.code === 'testspectra/invalid-frontmatter-enum')).toBe(false);
|
|
90
90
|
// 2. Invalid status -> testspectra/invalid-frontmatter-enum
|
|
91
91
|
const invalidSpecPath = path.join(tempDir, 'invalid-status.spec.md');
|
|
92
|
-
fs.writeFileSync(invalidSpecPath, `---
|
|
93
|
-
id: TC-0002-invalid-status
|
|
94
|
-
title: Invalid Status Spec
|
|
95
|
-
priority: High
|
|
96
|
-
caseType: Positive
|
|
97
|
-
status: not-a-valid-status
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
# Invalid Status Spec
|
|
101
|
-
|
|
102
|
-
## Test Steps
|
|
103
|
-
1. Step one
|
|
92
|
+
fs.writeFileSync(invalidSpecPath, `---
|
|
93
|
+
id: TC-0002-invalid-status
|
|
94
|
+
title: Invalid Status Spec
|
|
95
|
+
priority: High
|
|
96
|
+
caseType: Positive
|
|
97
|
+
status: not-a-valid-status
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# Invalid Status Spec
|
|
101
|
+
|
|
102
|
+
## Test Steps
|
|
103
|
+
1. Step one
|
|
104
104
|
`);
|
|
105
105
|
const invalidResult = SpecLinter.validateSpecFile(invalidSpecPath);
|
|
106
106
|
const invalidStatusDiag = invalidResult.diagnostics.find((d) => d.code === 'testspectra/invalid-frontmatter-enum' &&
|
|
@@ -111,18 +111,18 @@ status: not-a-valid-status
|
|
|
111
111
|
});
|
|
112
112
|
it('reports testspectra/invalid-frontmatter-enum when enum attributes have invalid values', () => {
|
|
113
113
|
const specPath = path.join(tempDir, 'spec.md');
|
|
114
|
-
fs.writeFileSync(specPath, `---
|
|
115
|
-
id: TC-0003-invalid-enum
|
|
116
|
-
title: Invalid priority value
|
|
117
|
-
priority: SuperHigh
|
|
118
|
-
caseType: MaybePositive
|
|
119
|
-
status: unknown-status
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
# Invalid priority value
|
|
123
|
-
|
|
124
|
-
## Test Steps
|
|
125
|
-
1. Step one
|
|
114
|
+
fs.writeFileSync(specPath, `---
|
|
115
|
+
id: TC-0003-invalid-enum
|
|
116
|
+
title: Invalid priority value
|
|
117
|
+
priority: SuperHigh
|
|
118
|
+
caseType: MaybePositive
|
|
119
|
+
status: unknown-status
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
# Invalid priority value
|
|
123
|
+
|
|
124
|
+
## Test Steps
|
|
125
|
+
1. Step one
|
|
126
126
|
`);
|
|
127
127
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
128
128
|
const enumErrors = result.diagnostics.filter((d) => d.code === 'testspectra/invalid-frontmatter-enum');
|
|
@@ -133,16 +133,16 @@ status: unknown-status
|
|
|
133
133
|
});
|
|
134
134
|
it('reports testspectra/missing-frontmatter when required H1 title section is missing in spec.md body', () => {
|
|
135
135
|
const specPath = path.join(tempDir, 'spec.md');
|
|
136
|
-
fs.writeFileSync(specPath, `---
|
|
137
|
-
id: TC-0004-no-h1
|
|
138
|
-
title: No Heading Section
|
|
139
|
-
priority: High
|
|
140
|
-
caseType: Positive
|
|
141
|
-
status: draft
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## Test Steps
|
|
145
|
-
1. Step without H1
|
|
136
|
+
fs.writeFileSync(specPath, `---
|
|
137
|
+
id: TC-0004-no-h1
|
|
138
|
+
title: No Heading Section
|
|
139
|
+
priority: High
|
|
140
|
+
caseType: Positive
|
|
141
|
+
status: draft
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Test Steps
|
|
145
|
+
1. Step without H1
|
|
146
146
|
`);
|
|
147
147
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
148
148
|
const missingHeading = result.diagnostics.find((d) => d.code === 'testspectra/missing-frontmatter' && d.message.includes('expected top-level test title'));
|
|
@@ -151,17 +151,17 @@ status: draft
|
|
|
151
151
|
});
|
|
152
152
|
it('reports testspectra/missing-frontmatter when required ## Test Steps section is missing in spec.md body', () => {
|
|
153
153
|
const specPath = path.join(tempDir, 'spec.md');
|
|
154
|
-
fs.writeFileSync(specPath, `---
|
|
155
|
-
id: TC-0005-no-steps
|
|
156
|
-
title: No Steps Heading
|
|
157
|
-
priority: High
|
|
158
|
-
caseType: Positive
|
|
159
|
-
status: draft
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
# No Steps Heading
|
|
163
|
-
|
|
164
|
-
Some narrative without Test Steps section.
|
|
154
|
+
fs.writeFileSync(specPath, `---
|
|
155
|
+
id: TC-0005-no-steps
|
|
156
|
+
title: No Steps Heading
|
|
157
|
+
priority: High
|
|
158
|
+
caseType: Positive
|
|
159
|
+
status: draft
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
# No Steps Heading
|
|
163
|
+
|
|
164
|
+
Some narrative without Test Steps section.
|
|
165
165
|
`);
|
|
166
166
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
167
167
|
const missingSteps = result.diagnostics.find((d) => d.code === 'testspectra/missing-frontmatter' && d.message.includes('expected "## Test Steps"'));
|
|
@@ -170,20 +170,20 @@ Some narrative without Test Steps section.
|
|
|
170
170
|
});
|
|
171
171
|
it('warns when multiple H1 headings are declared in spec.md body', () => {
|
|
172
172
|
const specPath = path.join(tempDir, 'spec.md');
|
|
173
|
-
fs.writeFileSync(specPath, `---
|
|
174
|
-
id: TC-0006-multiple-h1
|
|
175
|
-
title: Multiple Headings
|
|
176
|
-
priority: High
|
|
177
|
-
caseType: Positive
|
|
178
|
-
status: draft
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
# First Title
|
|
182
|
-
|
|
183
|
-
# Second Title
|
|
184
|
-
|
|
185
|
-
## Test Steps
|
|
186
|
-
1. Step
|
|
173
|
+
fs.writeFileSync(specPath, `---
|
|
174
|
+
id: TC-0006-multiple-h1
|
|
175
|
+
title: Multiple Headings
|
|
176
|
+
priority: High
|
|
177
|
+
caseType: Positive
|
|
178
|
+
status: draft
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
# First Title
|
|
182
|
+
|
|
183
|
+
# Second Title
|
|
184
|
+
|
|
185
|
+
## Test Steps
|
|
186
|
+
1. Step
|
|
187
187
|
`);
|
|
188
188
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
189
189
|
const multipleH1 = result.diagnostics.find((d) => d.severity === 'warning' && d.message.includes('Multiple top-level H1 headings'));
|
|
@@ -191,10 +191,10 @@ status: draft
|
|
|
191
191
|
});
|
|
192
192
|
it('reports testspectra/parse-error on malformed YAML syntax', () => {
|
|
193
193
|
const specPath = path.join(tempDir, 'spec.md');
|
|
194
|
-
fs.writeFileSync(specPath, `---
|
|
195
|
-
id: [broken yaml
|
|
196
|
-
title: Test
|
|
197
|
-
---
|
|
194
|
+
fs.writeFileSync(specPath, `---
|
|
195
|
+
id: [broken yaml
|
|
196
|
+
title: Test
|
|
197
|
+
---
|
|
198
198
|
`);
|
|
199
199
|
const result = SpecLinter.validateSpecFile(specPath);
|
|
200
200
|
expect(result.diagnostics.some((d) => d.code === 'testspectra/parse-error')).toBe(true);
|
|
@@ -203,16 +203,16 @@ title: Test
|
|
|
203
203
|
describe('Suite File Validation (suite.md)', () => {
|
|
204
204
|
it('passes validation for a well-formed suite.md with H1 suite heading', () => {
|
|
205
205
|
const suitePath = path.join(tempDir, 'suite.md');
|
|
206
|
-
fs.writeFileSync(suitePath, `---
|
|
207
|
-
id: suite-auth
|
|
208
|
-
name: Authentication
|
|
209
|
-
description: User authentication flows
|
|
210
|
-
executionOrder: 1.0
|
|
211
|
-
parallel: true
|
|
212
|
-
status: active
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
# Suite: Authentication
|
|
206
|
+
fs.writeFileSync(suitePath, `---
|
|
207
|
+
id: suite-auth
|
|
208
|
+
name: Authentication
|
|
209
|
+
description: User authentication flows
|
|
210
|
+
executionOrder: 1.0
|
|
211
|
+
parallel: true
|
|
212
|
+
status: active
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
# Suite: Authentication
|
|
216
216
|
`);
|
|
217
217
|
const result = SpecLinter.validateSuiteFile(suitePath);
|
|
218
218
|
expect(result.diagnostics).toHaveLength(0);
|
|
@@ -220,24 +220,24 @@ status: active
|
|
|
220
220
|
});
|
|
221
221
|
it('reports testspectra/missing-frontmatter when suite id or title/name is missing', () => {
|
|
222
222
|
const suitePath = path.join(tempDir, 'suite.md');
|
|
223
|
-
fs.writeFileSync(suitePath, `---
|
|
224
|
-
description: No id and no name
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
# Suite: Incomplete
|
|
223
|
+
fs.writeFileSync(suitePath, `---
|
|
224
|
+
description: No id and no name
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# Suite: Incomplete
|
|
228
228
|
`);
|
|
229
229
|
const result = SpecLinter.validateSuiteFile(suitePath);
|
|
230
230
|
expect(result.diagnostics.some((d) => d.code === 'testspectra/missing-frontmatter')).toBe(true);
|
|
231
231
|
});
|
|
232
232
|
it('reports testspectra/invalid-frontmatter-enum when suite status has invalid enum value', () => {
|
|
233
233
|
const suitePath = path.join(tempDir, 'suite.md');
|
|
234
|
-
fs.writeFileSync(suitePath, `---
|
|
235
|
-
id: suite-invalid-status
|
|
236
|
-
name: Invalid Status Suite
|
|
237
|
-
status: unknown-status
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
# Suite: Invalid Status Suite
|
|
234
|
+
fs.writeFileSync(suitePath, `---
|
|
235
|
+
id: suite-invalid-status
|
|
236
|
+
name: Invalid Status Suite
|
|
237
|
+
status: unknown-status
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
# Suite: Invalid Status Suite
|
|
241
241
|
`);
|
|
242
242
|
const result = SpecLinter.validateSuiteFile(suitePath);
|
|
243
243
|
const enumErr = result.diagnostics.find((d) => d.code === 'testspectra/invalid-frontmatter-enum');
|
|
@@ -246,13 +246,13 @@ status: unknown-status
|
|
|
246
246
|
});
|
|
247
247
|
it('supports in-memory content override for suite.md without writing to disk', () => {
|
|
248
248
|
const suitePath = path.join(tempDir, 'non-existent-suite.md');
|
|
249
|
-
const inMemoryContent = `---
|
|
250
|
-
id: suite-in-memory
|
|
251
|
-
title: In Memory Suite
|
|
252
|
-
status: active
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
# Suite: In Memory Suite
|
|
249
|
+
const inMemoryContent = `---
|
|
250
|
+
id: suite-in-memory
|
|
251
|
+
title: In Memory Suite
|
|
252
|
+
status: active
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
# Suite: In Memory Suite
|
|
256
256
|
`;
|
|
257
257
|
const result = SpecLinter.validateSuiteFile(suitePath, inMemoryContent);
|
|
258
258
|
expect(result.diagnostics).toHaveLength(0);
|
|
@@ -260,12 +260,12 @@ status: active
|
|
|
260
260
|
});
|
|
261
261
|
it('reports testspectra/missing-frontmatter when suite H1 title heading is missing', () => {
|
|
262
262
|
const suitePath = path.join(tempDir, 'suite.md');
|
|
263
|
-
fs.writeFileSync(suitePath, `---
|
|
264
|
-
id: suite-no-heading
|
|
265
|
-
name: No Heading
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
Body without H1 suite heading.
|
|
263
|
+
fs.writeFileSync(suitePath, `---
|
|
264
|
+
id: suite-no-heading
|
|
265
|
+
name: No Heading
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
Body without H1 suite heading.
|
|
269
269
|
`);
|
|
270
270
|
const result = SpecLinter.validateSuiteFile(suitePath);
|
|
271
271
|
const headingErr = result.diagnostics.find((d) => d.code === 'testspectra/missing-frontmatter' && d.message.includes('expected top-level suite title'));
|
|
@@ -278,18 +278,18 @@ Body without H1 suite heading.
|
|
|
278
278
|
const tc2Dir = path.join(tempDir, 'TC-2');
|
|
279
279
|
fs.mkdirSync(tc1Dir, { recursive: true });
|
|
280
280
|
fs.mkdirSync(tc2Dir, { recursive: true });
|
|
281
|
-
const specContent = `---
|
|
282
|
-
id: TC-DUPLICATE-001
|
|
283
|
-
title: Duplicate test case
|
|
284
|
-
priority: High
|
|
285
|
-
caseType: Positive
|
|
286
|
-
status: automated
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
# Duplicate test case
|
|
290
|
-
|
|
291
|
-
## Test Steps
|
|
292
|
-
1. Duplicate step
|
|
281
|
+
const specContent = `---
|
|
282
|
+
id: TC-DUPLICATE-001
|
|
283
|
+
title: Duplicate test case
|
|
284
|
+
priority: High
|
|
285
|
+
caseType: Positive
|
|
286
|
+
status: automated
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
# Duplicate test case
|
|
290
|
+
|
|
291
|
+
## Test Steps
|
|
292
|
+
1. Duplicate step
|
|
293
293
|
`;
|
|
294
294
|
fs.writeFileSync(path.join(tc1Dir, 'spec.md'), specContent);
|
|
295
295
|
fs.writeFileSync(path.join(tc2Dir, 'spec.md'), specContent);
|
|
@@ -303,26 +303,26 @@ status: automated
|
|
|
303
303
|
const validSuiteDir = path.join(tempDir, 'ValidSuite');
|
|
304
304
|
const validTcDir = path.join(validSuiteDir, 'TC-001');
|
|
305
305
|
fs.mkdirSync(validTcDir, { recursive: true });
|
|
306
|
-
fs.writeFileSync(path.join(validSuiteDir, 'suite.md'), `---
|
|
307
|
-
id: suite-valid
|
|
308
|
-
name: Valid Suite
|
|
309
|
-
status: active
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
# Suite: Valid Suite
|
|
306
|
+
fs.writeFileSync(path.join(validSuiteDir, 'suite.md'), `---
|
|
307
|
+
id: suite-valid
|
|
308
|
+
name: Valid Suite
|
|
309
|
+
status: active
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
# Suite: Valid Suite
|
|
313
313
|
`);
|
|
314
|
-
fs.writeFileSync(path.join(validTcDir, 'spec.md'), `---
|
|
315
|
-
id: TC-VALID-001
|
|
316
|
-
title: Valid Test Case
|
|
317
|
-
priority: High
|
|
318
|
-
caseType: Positive
|
|
319
|
-
status: automated
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
# Valid Test Case
|
|
323
|
-
|
|
324
|
-
## Test Steps
|
|
325
|
-
1. Open page
|
|
314
|
+
fs.writeFileSync(path.join(validTcDir, 'spec.md'), `---
|
|
315
|
+
id: TC-VALID-001
|
|
316
|
+
title: Valid Test Case
|
|
317
|
+
priority: High
|
|
318
|
+
caseType: Positive
|
|
319
|
+
status: automated
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
# Valid Test Case
|
|
323
|
+
|
|
324
|
+
## Test Steps
|
|
325
|
+
1. Open page
|
|
326
326
|
`);
|
|
327
327
|
fs.writeFileSync(path.join(validTcDir, 'web.test.ts'), `it('Valid Test Case', async () => {});\n`);
|
|
328
328
|
const result = SpecLinter.validateWorkspace(tempDir);
|
|
@@ -4,14 +4,14 @@ export interface WorkspaceAssets {
|
|
|
4
4
|
fixtures: Set<string>;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Scans a workspace directory for all `project.json` files that have
|
|
8
|
-
* "
|
|
7
|
+
* Scans a workspace directory for all `project.json` files that have the
|
|
8
|
+
* "testspectra:shared" tag, and returns their parent folder paths.
|
|
9
9
|
*/
|
|
10
10
|
export declare function findSharedLibraryRoots(workspaceDir: string): string[];
|
|
11
11
|
/**
|
|
12
12
|
* Resolves the root directory of a shared testing library if the file path is inside one.
|
|
13
13
|
* Determines shared library status based on `project.json` containing
|
|
14
|
-
*
|
|
14
|
+
* the "testspectra:shared" tag in ancestor directories,
|
|
15
15
|
* taking the parent folder of that `project.json` as the shared library root.
|
|
16
16
|
*/
|
|
17
17
|
export declare function getSharedLibraryRoot(filePath: string, workspaceDir?: string): string | null;
|
package/dist/linter/discovery.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
/**
|
|
4
|
-
* Scans a workspace directory for all `project.json` files that have
|
|
5
|
-
* "
|
|
4
|
+
* Scans a workspace directory for all `project.json` files that have the
|
|
5
|
+
* "testspectra:shared" tag, and returns their parent folder paths.
|
|
6
6
|
*/
|
|
7
7
|
export function findSharedLibraryRoots(workspaceDir) {
|
|
8
8
|
const sharedRoots = [];
|
|
@@ -23,7 +23,7 @@ export function findSharedLibraryRoots(workspaceDir) {
|
|
|
23
23
|
const raw = fs.readFileSync(path.join(dir, entry.name), 'utf-8');
|
|
24
24
|
const parsed = JSON.parse(raw);
|
|
25
25
|
const tags = Array.isArray(parsed.tags) ? parsed.tags : [];
|
|
26
|
-
if (tags.includes('
|
|
26
|
+
if (tags.includes('testspectra:shared')) {
|
|
27
27
|
sharedRoots.push(dir);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -39,7 +39,7 @@ export function findSharedLibraryRoots(workspaceDir) {
|
|
|
39
39
|
/**
|
|
40
40
|
* Resolves the root directory of a shared testing library if the file path is inside one.
|
|
41
41
|
* Determines shared library status based on `project.json` containing
|
|
42
|
-
*
|
|
42
|
+
* the "testspectra:shared" tag in ancestor directories,
|
|
43
43
|
* taking the parent folder of that `project.json` as the shared library root.
|
|
44
44
|
*/
|
|
45
45
|
export function getSharedLibraryRoot(filePath, workspaceDir) {
|
|
@@ -67,7 +67,7 @@ export function getSharedLibraryRoot(filePath, workspaceDir) {
|
|
|
67
67
|
const raw = fs.readFileSync(projectJsonPath, 'utf-8');
|
|
68
68
|
const projectConfig = JSON.parse(raw);
|
|
69
69
|
const tags = Array.isArray(projectConfig.tags) ? projectConfig.tags : [];
|
|
70
|
-
if (tags.includes('
|
|
70
|
+
if (tags.includes('testspectra:shared')) {
|
|
71
71
|
return current;
|
|
72
72
|
}
|
|
73
73
|
return null;
|
|
@@ -36,8 +36,8 @@ export declare const SpecFrontmatterSchema: z.ZodObject<{
|
|
|
36
36
|
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
37
37
|
}, "strip", z.ZodTypeAny, {
|
|
38
38
|
id: string;
|
|
39
|
-
status: "draft" | "ready-for-automation" | "automated" | "deprecated";
|
|
40
39
|
title: string;
|
|
40
|
+
status: "draft" | "ready-for-automation" | "automated" | "deprecated";
|
|
41
41
|
priority: "Critical" | "High" | "Medium" | "Low";
|
|
42
42
|
caseType: "Positive" | "Negative" | "Edge";
|
|
43
43
|
coverage?: {
|
|
@@ -47,8 +47,8 @@ export declare const SpecFrontmatterSchema: z.ZodObject<{
|
|
|
47
47
|
tags?: string[] | undefined;
|
|
48
48
|
}, {
|
|
49
49
|
id: string;
|
|
50
|
-
status: "draft" | "ready-for-automation" | "automated" | "deprecated";
|
|
51
50
|
title: string;
|
|
51
|
+
status: "draft" | "ready-for-automation" | "automated" | "deprecated";
|
|
52
52
|
priority: "Critical" | "High" | "Medium" | "Low";
|
|
53
53
|
caseType: "Positive" | "Negative" | "Edge";
|
|
54
54
|
coverage?: {
|
|
@@ -15,8 +15,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
|
15
15
|
}, "strip", z.ZodTypeAny, {
|
|
16
16
|
id: string;
|
|
17
17
|
name?: string | undefined;
|
|
18
|
-
status?: "active" | "draft" | "deprecated" | undefined;
|
|
19
18
|
title?: string | undefined;
|
|
19
|
+
status?: "draft" | "deprecated" | "active" | undefined;
|
|
20
20
|
tags?: string[] | undefined;
|
|
21
21
|
description?: string | undefined;
|
|
22
22
|
executionOrder?: number | undefined;
|
|
@@ -24,8 +24,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
|
24
24
|
}, {
|
|
25
25
|
id: string;
|
|
26
26
|
name?: string | undefined;
|
|
27
|
-
status?: "active" | "draft" | "deprecated" | undefined;
|
|
28
27
|
title?: string | undefined;
|
|
28
|
+
status?: "draft" | "deprecated" | "active" | undefined;
|
|
29
29
|
tags?: string[] | undefined;
|
|
30
30
|
description?: string | undefined;
|
|
31
31
|
executionOrder?: number | undefined;
|
|
@@ -33,8 +33,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
|
33
33
|
}>, {
|
|
34
34
|
id: string;
|
|
35
35
|
name?: string | undefined;
|
|
36
|
-
status?: "active" | "draft" | "deprecated" | undefined;
|
|
37
36
|
title?: string | undefined;
|
|
37
|
+
status?: "draft" | "deprecated" | "active" | undefined;
|
|
38
38
|
tags?: string[] | undefined;
|
|
39
39
|
description?: string | undefined;
|
|
40
40
|
executionOrder?: number | undefined;
|
|
@@ -42,8 +42,8 @@ export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
|
42
42
|
}, {
|
|
43
43
|
id: string;
|
|
44
44
|
name?: string | undefined;
|
|
45
|
-
status?: "active" | "draft" | "deprecated" | undefined;
|
|
46
45
|
title?: string | undefined;
|
|
46
|
+
status?: "draft" | "deprecated" | "active" | undefined;
|
|
47
47
|
tags?: string[] | undefined;
|
|
48
48
|
description?: string | undefined;
|
|
49
49
|
executionOrder?: number | undefined;
|
package/dist/runner/bridge.js
CHANGED
|
@@ -1,15 +1,60 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { createRequire } from 'module';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
7
|
const __dirname = path.dirname(__filename);
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
// Maps process.platform/process.arch to the optionalDependency package that carries the
|
|
10
|
+
// prebuilt native binary for that platform (see cli/npm/*). Keep in sync with the
|
|
11
|
+
// optionalDependencies list in cli/package.json and the packages built by
|
|
12
|
+
// scripts/release-cli-local.sh.
|
|
13
|
+
function platformPackageName() {
|
|
14
|
+
const { platform, arch } = process;
|
|
15
|
+
if (platform === 'darwin' && arch === 'arm64')
|
|
16
|
+
return '@testspectra/cli-darwin-arm64';
|
|
17
|
+
if (platform === 'linux' && arch === 'x64')
|
|
18
|
+
return '@testspectra/cli-linux-x64';
|
|
19
|
+
if (platform === 'win32' && arch === 'arm64')
|
|
20
|
+
return '@testspectra/cli-win32-arm64';
|
|
21
|
+
if (platform === 'win32' && arch === 'x64')
|
|
22
|
+
return '@testspectra/cli-win32-x64';
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
7
25
|
export class RustCoreBridge {
|
|
8
26
|
static resolveBinaryPath() {
|
|
9
|
-
// 1. Look for precompiled release/debug binary in workspace target
|
|
10
|
-
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
11
27
|
const isWindows = process.platform === 'win32';
|
|
12
28
|
const binaryName = isWindows ? 'testspectra-runner.exe' : 'testspectra-runner';
|
|
29
|
+
// 1. Production install: resolve the prebuilt binary from the platform-specific
|
|
30
|
+
// optionalDependency package (e.g. @testspectra/cli-darwin-arm64). This is how the
|
|
31
|
+
// published npm package ships native binaries for every supported platform without
|
|
32
|
+
// bundling all of them into @testspectra/cli itself.
|
|
33
|
+
const pkgName = platformPackageName();
|
|
34
|
+
if (pkgName) {
|
|
35
|
+
try {
|
|
36
|
+
const pkgJsonPath = require.resolve(`${pkgName}/package.json`);
|
|
37
|
+
const pkgBinPath = path.join(path.dirname(pkgJsonPath), 'bin', binaryName);
|
|
38
|
+
if (fs.existsSync(pkgBinPath)) {
|
|
39
|
+
if (process.platform !== 'win32') {
|
|
40
|
+
try {
|
|
41
|
+
const stat = fs.statSync(pkgBinPath);
|
|
42
|
+
if ((stat.mode & 0o111) === 0) {
|
|
43
|
+
fs.chmodSync(pkgBinPath, 0o755);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
}
|
|
48
|
+
return pkgBinPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Optional dependency not installed — fall through to the workspace dev paths below.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// 2. Monorepo dev fallback: look for a precompiled release/debug binary in the workspace
|
|
56
|
+
// target, as produced by scripts/ensure-runner-binary.js during local development.
|
|
57
|
+
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
13
58
|
const candidatePaths = [
|
|
14
59
|
path.join(workspaceRoot, 'core/target/release', binaryName),
|
|
15
60
|
path.join(workspaceRoot, 'target/release', binaryName),
|
|
@@ -29,8 +74,11 @@ export class RustCoreBridge {
|
|
|
29
74
|
return `${p}.exe`;
|
|
30
75
|
}
|
|
31
76
|
}
|
|
32
|
-
throw new Error(
|
|
33
|
-
|
|
77
|
+
throw new Error(pkgName
|
|
78
|
+
? `[TestSpectra] Native core runner binary not found for ${process.platform}/${process.arch}.\n` +
|
|
79
|
+
`Expected the optional dependency '${pkgName}' to provide it. Try reinstalling ` +
|
|
80
|
+
"('npm install' / 'pnpm install') without --no-optional, or build locally with 'pnpm run build:core'."
|
|
81
|
+
: `[TestSpectra] ${process.platform}/${process.arch} is not currently supported by @testspectra/cli.`);
|
|
34
82
|
}
|
|
35
83
|
static async run(options, reporter) {
|
|
36
84
|
const binPath = this.resolveBinaryPath();
|
|
@@ -66,6 +114,7 @@ export class RustCoreBridge {
|
|
|
66
114
|
return new Promise((resolve, reject) => {
|
|
67
115
|
const child = spawn(binPath, [payload], {
|
|
68
116
|
env: runnerEnv,
|
|
117
|
+
windowsHide: true,
|
|
69
118
|
});
|
|
70
119
|
let runStatus = 'failed';
|
|
71
120
|
let runDuration = '0s';
|