@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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const PRIORITY_VALUES = ['Critical', 'High', 'Medium', 'Low'];
|
|
3
|
+
export const CASE_TYPE_VALUES = ['Positive', 'Negative', 'Edge'];
|
|
4
|
+
export const STATUS_VALUES = ['draft', 'ready-for-automation', 'automated', 'deprecated'];
|
|
5
|
+
export const COVERAGE_STATUS_VALUES = ['automated', 'manual', 'unsupported'];
|
|
6
|
+
export const PriorityEnum = z.enum(PRIORITY_VALUES, {
|
|
7
|
+
errorMap: (issue) => {
|
|
8
|
+
if (issue.code === 'invalid_type' && issue.received === 'undefined') {
|
|
9
|
+
return { message: "Missing required frontmatter attribute 'priority'" };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
message: `Invalid priority value. Expected one of: ${PRIORITY_VALUES.join(', ')}`,
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
export const CaseTypeEnum = z.enum(CASE_TYPE_VALUES, {
|
|
17
|
+
errorMap: (issue) => {
|
|
18
|
+
if (issue.code === 'invalid_type' && issue.received === 'undefined') {
|
|
19
|
+
return { message: "Missing required frontmatter attribute 'caseType'" };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
message: `Invalid caseType value. Expected one of: ${CASE_TYPE_VALUES.join(', ')}`,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
export const StatusEnum = z.enum(STATUS_VALUES, {
|
|
27
|
+
errorMap: (issue) => {
|
|
28
|
+
if (issue.code === 'invalid_type' && issue.received === 'undefined') {
|
|
29
|
+
return { message: "Missing required frontmatter attribute 'status'" };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
message: `Invalid status value. Expected one of: ${STATUS_VALUES.join(', ')}`,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
export const CoverageSchema = z.object({
|
|
37
|
+
web: z.enum(COVERAGE_STATUS_VALUES).optional(),
|
|
38
|
+
mobile: z.enum(COVERAGE_STATUS_VALUES).optional(),
|
|
39
|
+
});
|
|
40
|
+
export const SpecFrontmatterSchema = z.object({
|
|
41
|
+
id: z
|
|
42
|
+
.string({
|
|
43
|
+
required_error: "Missing required frontmatter attribute 'id'",
|
|
44
|
+
invalid_type_error: "Frontmatter attribute 'id' must be a string",
|
|
45
|
+
})
|
|
46
|
+
.min(1, "Attribute 'id' cannot be empty"),
|
|
47
|
+
title: z
|
|
48
|
+
.string({
|
|
49
|
+
required_error: "Missing required frontmatter attribute 'title'",
|
|
50
|
+
invalid_type_error: "Frontmatter attribute 'title' must be a string",
|
|
51
|
+
})
|
|
52
|
+
.min(1, "Attribute 'title' cannot be empty"),
|
|
53
|
+
priority: PriorityEnum,
|
|
54
|
+
caseType: CaseTypeEnum,
|
|
55
|
+
status: StatusEnum,
|
|
56
|
+
coverage: CoverageSchema.optional(),
|
|
57
|
+
tags: z.array(z.string()).optional(),
|
|
58
|
+
});
|
|
59
|
+
export const REQUIRED_SPEC_SECTIONS = [
|
|
60
|
+
{
|
|
61
|
+
name: 'Title',
|
|
62
|
+
level: 1,
|
|
63
|
+
required: true,
|
|
64
|
+
headerPattern: /^#\s+(.+)$/,
|
|
65
|
+
description: 'Top-level H1 test case heading (# <Title>)',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'Test Steps',
|
|
69
|
+
level: 2,
|
|
70
|
+
required: true,
|
|
71
|
+
headerPattern: /^##\s+Test Steps/i,
|
|
72
|
+
description: 'Ordered test case steps and @step:<name> directives (## Test Steps)',
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
export const STANDARD_SPEC_SECTIONS = [
|
|
76
|
+
...REQUIRED_SPEC_SECTIONS,
|
|
77
|
+
{
|
|
78
|
+
name: 'Description',
|
|
79
|
+
level: 2,
|
|
80
|
+
required: false,
|
|
81
|
+
headerPattern: /^##\s+Description/i,
|
|
82
|
+
description: 'Detailed scenario narrative and business context (## Description)',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'Prerequisites',
|
|
86
|
+
level: 2,
|
|
87
|
+
required: false,
|
|
88
|
+
headerPattern: /^##\s+Prerequisites/i,
|
|
89
|
+
description: 'Precondition steps or initial test data requirements (## Prerequisites)',
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
/**
|
|
93
|
+
* Extracts markdown headers from document body.
|
|
94
|
+
*/
|
|
95
|
+
export function extractMarkdownHeaders(body, startLine) {
|
|
96
|
+
const headers = [];
|
|
97
|
+
const lines = body.split('\n');
|
|
98
|
+
for (let i = 0; i < lines.length; i++) {
|
|
99
|
+
const line = lines[i];
|
|
100
|
+
const match = line.match(/^(#{1,6})\s+(.+)$/);
|
|
101
|
+
if (match) {
|
|
102
|
+
headers.push({
|
|
103
|
+
level: match[1].length,
|
|
104
|
+
text: match[2].trim(),
|
|
105
|
+
line: startLine + i,
|
|
106
|
+
raw: line,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return headers;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validates markdown body sections against spec rules.
|
|
114
|
+
*/
|
|
115
|
+
export function validateSpecSections(body, filePath, bodyStartLine) {
|
|
116
|
+
const diagnostics = [];
|
|
117
|
+
const headers = extractMarkdownHeaders(body, bodyStartLine);
|
|
118
|
+
// 1. Verify required H1 Title header exists
|
|
119
|
+
const h1Headers = headers.filter((h) => h.level === 1);
|
|
120
|
+
if (h1Headers.length === 0) {
|
|
121
|
+
diagnostics.push({
|
|
122
|
+
code: 'testspectra/missing-frontmatter',
|
|
123
|
+
severity: 'error',
|
|
124
|
+
message: 'Missing required markdown heading: expected top-level test title "# <Title>"',
|
|
125
|
+
filePath,
|
|
126
|
+
line: bodyStartLine,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else if (h1Headers.length > 1) {
|
|
130
|
+
diagnostics.push({
|
|
131
|
+
code: 'testspectra/missing-frontmatter',
|
|
132
|
+
severity: 'warning',
|
|
133
|
+
message: `Multiple top-level H1 headings found in spec.md (${h1Headers.length}). Expected exactly one test title heading.`,
|
|
134
|
+
filePath,
|
|
135
|
+
line: h1Headers[1].line,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// 2. Verify required "## Test Steps" header exists
|
|
139
|
+
const testStepsHeader = headers.find((h) => h.level === 2 && /^Test Steps$/i.test(h.text));
|
|
140
|
+
if (!testStepsHeader) {
|
|
141
|
+
diagnostics.push({
|
|
142
|
+
code: 'testspectra/missing-frontmatter',
|
|
143
|
+
severity: 'error',
|
|
144
|
+
message: 'Missing required markdown section: expected "## Test Steps"',
|
|
145
|
+
filePath,
|
|
146
|
+
line: bodyStartLine,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return diagnostics;
|
|
150
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { LintDiagnostic } from '../types.js';
|
|
3
|
+
import { type SectionDefinition } from './spec.schema.js';
|
|
4
|
+
export declare const SUITE_STATUS_VALUES: readonly ["active", "draft", "deprecated"];
|
|
5
|
+
export declare const SuiteStatusEnum: z.ZodEnum<["active", "draft", "deprecated"]>;
|
|
6
|
+
export declare const SuiteFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
name: z.ZodOptional<z.ZodString>;
|
|
9
|
+
title: z.ZodOptional<z.ZodString>;
|
|
10
|
+
description: z.ZodOptional<z.ZodString>;
|
|
11
|
+
executionOrder: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
parallel: z.ZodOptional<z.ZodBoolean>;
|
|
13
|
+
status: z.ZodOptional<z.ZodEnum<["active", "draft", "deprecated"]>>;
|
|
14
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
id: string;
|
|
17
|
+
name?: string | undefined;
|
|
18
|
+
status?: "active" | "draft" | "deprecated" | undefined;
|
|
19
|
+
title?: string | undefined;
|
|
20
|
+
tags?: string[] | undefined;
|
|
21
|
+
description?: string | undefined;
|
|
22
|
+
executionOrder?: number | undefined;
|
|
23
|
+
parallel?: boolean | undefined;
|
|
24
|
+
}, {
|
|
25
|
+
id: string;
|
|
26
|
+
name?: string | undefined;
|
|
27
|
+
status?: "active" | "draft" | "deprecated" | undefined;
|
|
28
|
+
title?: string | undefined;
|
|
29
|
+
tags?: string[] | undefined;
|
|
30
|
+
description?: string | undefined;
|
|
31
|
+
executionOrder?: number | undefined;
|
|
32
|
+
parallel?: boolean | undefined;
|
|
33
|
+
}>, {
|
|
34
|
+
id: string;
|
|
35
|
+
name?: string | undefined;
|
|
36
|
+
status?: "active" | "draft" | "deprecated" | undefined;
|
|
37
|
+
title?: string | undefined;
|
|
38
|
+
tags?: string[] | undefined;
|
|
39
|
+
description?: string | undefined;
|
|
40
|
+
executionOrder?: number | undefined;
|
|
41
|
+
parallel?: boolean | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
id: string;
|
|
44
|
+
name?: string | undefined;
|
|
45
|
+
status?: "active" | "draft" | "deprecated" | undefined;
|
|
46
|
+
title?: string | undefined;
|
|
47
|
+
tags?: string[] | undefined;
|
|
48
|
+
description?: string | undefined;
|
|
49
|
+
executionOrder?: number | undefined;
|
|
50
|
+
parallel?: boolean | undefined;
|
|
51
|
+
}>;
|
|
52
|
+
export type SuiteStatus = (typeof SUITE_STATUS_VALUES)[number];
|
|
53
|
+
export type SuiteFrontmatter = z.infer<typeof SuiteFrontmatterSchema>;
|
|
54
|
+
export declare const REQUIRED_SUITE_SECTIONS: SectionDefinition[];
|
|
55
|
+
export declare const STANDARD_SUITE_SECTIONS: SectionDefinition[];
|
|
56
|
+
/**
|
|
57
|
+
* Validates markdown body sections against suite rules.
|
|
58
|
+
*/
|
|
59
|
+
export declare function validateSuiteSections(body: string, filePath: string, bodyStartLine: number): LintDiagnostic[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { extractMarkdownHeaders } from './spec.schema.js';
|
|
3
|
+
export const SUITE_STATUS_VALUES = ['active', 'draft', 'deprecated'];
|
|
4
|
+
export const SuiteStatusEnum = z.enum(SUITE_STATUS_VALUES, {
|
|
5
|
+
errorMap: (issue) => {
|
|
6
|
+
if (issue.code === 'invalid_type' && issue.received === 'undefined') {
|
|
7
|
+
return { message: "Missing required frontmatter attribute 'status'" };
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
message: `Invalid status value. Expected one of: ${SUITE_STATUS_VALUES.join(', ')}`,
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export const SuiteFrontmatterSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
id: z
|
|
17
|
+
.string({
|
|
18
|
+
required_error: "Missing required frontmatter attribute 'id'",
|
|
19
|
+
invalid_type_error: "Frontmatter attribute 'id' must be a string",
|
|
20
|
+
})
|
|
21
|
+
.min(1, "Attribute 'id' cannot be empty"),
|
|
22
|
+
name: z.string().optional(),
|
|
23
|
+
title: z.string().optional(),
|
|
24
|
+
description: z.string().optional(),
|
|
25
|
+
executionOrder: z.number().optional(),
|
|
26
|
+
parallel: z.boolean().optional(),
|
|
27
|
+
status: SuiteStatusEnum.optional(),
|
|
28
|
+
tags: z.array(z.string()).optional(),
|
|
29
|
+
})
|
|
30
|
+
.refine((data) => Boolean(data.name || data.title), {
|
|
31
|
+
message: "Missing required frontmatter attribute 'name' or 'title'",
|
|
32
|
+
path: ['title'],
|
|
33
|
+
});
|
|
34
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Suite Markdown Section Rules
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
export const REQUIRED_SUITE_SECTIONS = [
|
|
38
|
+
{
|
|
39
|
+
name: 'Suite Title',
|
|
40
|
+
level: 1,
|
|
41
|
+
required: true,
|
|
42
|
+
headerPattern: /^#\s+(Suite:\s*)?.+$/i,
|
|
43
|
+
description: 'Top-level H1 suite heading (# Suite: <Name> or # <Name>)',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
export const STANDARD_SUITE_SECTIONS = [
|
|
47
|
+
...REQUIRED_SUITE_SECTIONS,
|
|
48
|
+
{
|
|
49
|
+
name: 'Description',
|
|
50
|
+
level: 2,
|
|
51
|
+
required: false,
|
|
52
|
+
headerPattern: /^##\s+Description/i,
|
|
53
|
+
description: 'Detailed description of suite scope and test coverage (## Description)',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Validates markdown body sections against suite rules.
|
|
58
|
+
*/
|
|
59
|
+
export function validateSuiteSections(body, filePath, bodyStartLine) {
|
|
60
|
+
const diagnostics = [];
|
|
61
|
+
const headers = extractMarkdownHeaders(body, bodyStartLine);
|
|
62
|
+
// 1. Verify required H1 Suite Title header exists
|
|
63
|
+
const h1Headers = headers.filter((h) => h.level === 1);
|
|
64
|
+
if (h1Headers.length === 0) {
|
|
65
|
+
diagnostics.push({
|
|
66
|
+
code: 'testspectra/missing-frontmatter',
|
|
67
|
+
severity: 'error',
|
|
68
|
+
message: 'Missing required markdown heading: expected top-level suite title "# Suite: <Name>"',
|
|
69
|
+
filePath,
|
|
70
|
+
line: bodyStartLine,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (h1Headers.length > 1) {
|
|
74
|
+
diagnostics.push({
|
|
75
|
+
code: 'testspectra/missing-frontmatter',
|
|
76
|
+
severity: 'warning',
|
|
77
|
+
message: `Multiple top-level H1 headings found in suite.md (${h1Headers.length}). Expected exactly one suite title heading.`,
|
|
78
|
+
filePath,
|
|
79
|
+
line: h1Headers[1].line,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return diagnostics;
|
|
83
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LintDiagnostic, SpecLintResult } from './types.js';
|
|
2
|
+
export declare class SpecLinter {
|
|
3
|
+
/**
|
|
4
|
+
* Validates a single spec.md file.
|
|
5
|
+
*/
|
|
6
|
+
static validateSpecFile(filePath: string, contentOverride?: string, companionCodeOverrides?: Map<string, string> | Record<string, string>): {
|
|
7
|
+
diagnostics: LintDiagnostic[];
|
|
8
|
+
id?: string;
|
|
9
|
+
idLine?: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Validates a single suite.md file.
|
|
13
|
+
*/
|
|
14
|
+
static validateSuiteFile(filePath: string, contentOverride?: string): {
|
|
15
|
+
diagnostics: LintDiagnostic[];
|
|
16
|
+
id?: string;
|
|
17
|
+
idLine?: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Validates all spec.md and suite.md files in a workspace or specific target path.
|
|
21
|
+
*/
|
|
22
|
+
static validateWorkspace(workspaceDir: string, targetPath?: string): SpecLintResult;
|
|
23
|
+
}
|