@testspectra/cli 1.1.8-rc.3 → 1.1.8-rc.31
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 +50 -0
- package/bin/spectra.js +0 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/__tests__/doctor-filter.test.d.ts +1 -0
- package/dist/commands/__tests__/doctor-filter.test.js +37 -0
- package/dist/commands/__tests__/run-e2e.test.js +49 -49
- package/dist/commands/__tests__/test-error-streaming.test.d.ts +1 -0
- package/dist/commands/__tests__/test-error-streaming.test.js +85 -0
- package/dist/commands/__tests__/test-scaffolding.test.d.ts +1 -0
- package/dist/commands/__tests__/test-scaffolding.test.js +32 -0
- package/dist/commands/__tests__/upgrade.test.d.ts +1 -0
- package/dist/commands/__tests__/upgrade.test.js +95 -0
- package/dist/commands/add.js +5 -4
- 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.d.ts +9 -1
- package/dist/commands/doctor.js +17 -3
- package/dist/commands/init.js +7 -160
- package/dist/commands/install.js +5 -0
- package/dist/commands/license.js +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +22 -0
- package/dist/commands/test.d.ts +13 -0
- package/dist/commands/test.js +115 -21
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/upgrade.js +197 -0
- package/dist/config/schema.d.ts +53 -0
- package/dist/config/schema.js +2 -0
- 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 +80 -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 +56 -26
- 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 +7 -7
- package/dist/linter/schemas/spec.schema.d.ts +8 -8
- package/dist/linter/schemas/suite.schema.d.ts +4 -4
- package/dist/runner/bridge.d.ts +1 -1
- package/dist/runner/bridge.js +101 -11
- package/dist/runner/reporter.d.ts +1 -0
- package/dist/runner/reporter.js +12 -6
- package/dist/utils/__tests__/dev-server-manager.test.d.ts +1 -0
- package/dist/utils/__tests__/dev-server-manager.test.js +72 -0
- package/dist/utils/background-update-check.d.ts +1 -0
- package/dist/utils/background-update-check.js +43 -0
- package/dist/utils/dependency-updates.d.ts +15 -0
- package/dist/utils/dependency-updates.js +107 -0
- package/dist/utils/dev-server-manager.d.ts +40 -0
- package/dist/utils/dev-server-manager.js +257 -0
- package/dist/utils/update-notifier.d.ts +2 -0
- package/dist/utils/update-notifier.js +67 -0
- package/package.json +36 -30
- 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/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +6343 -1612
- package/templates/nx/.nx/workspace-data/d/server-process.json +1 -1
- package/templates/nx/.nx/workspace-data/file-map.json +116 -130
- 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 +504 -504
- package/templates/nx/.nx/workspace-data/project-graph.json +560 -559
- package/templates/nx/.nx/workspace-data/source-maps.json +336 -0
- 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 +65 -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 +25 -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 +13 -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 +24 -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 +17 -27
- package/templates/react-component/src/components/Badge/Badge.tsx +60 -60
- package/templates/react-component/src/components/Button/Button.tsx +59 -57
- package/templates/react-component/src/components/Input/Input.tsx +40 -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.hash +0 -1
- package/bin/testspectra-runner.exe +0 -0
- package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
- /package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
package/dist/commands/init.js
CHANGED
|
@@ -6,6 +6,7 @@ import chalk from 'chalk';
|
|
|
6
6
|
import { ConfigLoader } from '../config/loader.js';
|
|
7
7
|
import { TypeGenerator } from '../types/generator.js';
|
|
8
8
|
import { TsConfigGenerator } from '../generator/tsconfig-generator.js';
|
|
9
|
+
import { generateArchitectureDoc } from '../generator/architecture-doc-generator.js';
|
|
9
10
|
function parsePnpmWorkspaceGlobs(content) {
|
|
10
11
|
const lines = content.split('\n');
|
|
11
12
|
const globs = [];
|
|
@@ -460,6 +461,8 @@ export async function initCommand(options = {}) {
|
|
|
460
461
|
(typeof rootPkg.devDependencies['@testspectra/matchers'] === 'string' &&
|
|
461
462
|
rootPkg.devDependencies['@testspectra/matchers'].startsWith('link:')))
|
|
462
463
|
rootPkg.devDependencies['@testspectra/matchers'] = cliDepVersion;
|
|
464
|
+
if (!rootPkg.devDependencies['nx'])
|
|
465
|
+
rootPkg.devDependencies['nx'] = '^20.8.4';
|
|
463
466
|
if (!rootPkg.devDependencies['typescript'])
|
|
464
467
|
rootPkg.devDependencies['typescript'] = '^5.4.5';
|
|
465
468
|
if (!rootPkg.devDependencies['@wdio/cli'])
|
|
@@ -508,6 +511,7 @@ export async function initCommand(options = {}) {
|
|
|
508
511
|
copyRecursive(sampleE2eSrc, targetE2eDir, {
|
|
509
512
|
'playground-e2e': e2eProjectName,
|
|
510
513
|
'packages/playground/e2e': path.relative(cwd, targetE2eDir).replace(/\\/g, '/'),
|
|
514
|
+
'testspectra:scope:playground': `testspectra:scope:${featureName}`,
|
|
511
515
|
'scope:playground': `scope:${featureName}`,
|
|
512
516
|
});
|
|
513
517
|
// Adjust project.json
|
|
@@ -519,7 +523,7 @@ export async function initCommand(options = {}) {
|
|
|
519
523
|
pObj.sourceRoot = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
|
|
520
524
|
pObj.targets.e2e.options.cwd = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
|
|
521
525
|
pObj.targets['type-check'].options.cwd = '.';
|
|
522
|
-
pObj.tags = [`scope:${featureName}
|
|
526
|
+
pObj.tags = ['testspectra:e2e', `testspectra:scope:${featureName}`];
|
|
523
527
|
fs.writeFileSync(projJsonPath, JSON.stringify(pObj, null, 2), 'utf-8');
|
|
524
528
|
}
|
|
525
529
|
catch { }
|
|
@@ -557,137 +561,7 @@ export async function initCommand(options = {}) {
|
|
|
557
561
|
// 7. Generate Ambient Types in ROOT only
|
|
558
562
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
559
563
|
ensureVsCodeExtensions(cwd);
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
## 1. Directory & File Overview
|
|
567
|
-
|
|
568
|
-
### Centralized Root Elements
|
|
569
|
-
- \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
|
|
570
|
-
- \`./.testspectra/\`: Local test project cache, runtime metadata, auto-generated master TSConfig (\`.testspectra/tsconfig.json\`), platform sub-configs (\`.testspectra/tsconfig/\`), and universal ambient types (\`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
|
|
571
|
-
- \`./nx.json\`: Nx target defaults with execution caching and inputs.
|
|
572
|
-
- \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
|
|
573
|
-
- \`./tsconfig.json\`: Solution-style TypeScript orchestrator referencing \`./.testspectra/tsconfig.json\`.
|
|
574
|
-
|
|
575
|
-
### Centralized Shared Testing Library (\`./${sharedTestingRelPath}\`)
|
|
576
|
-
Provides cross-feature reusable test entities with **100% zero-import global resolution**:
|
|
577
|
-
- \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
|
|
578
|
-
- \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
|
|
579
|
-
- \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
|
|
580
|
-
- \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
|
|
581
|
-
|
|
582
|
-
### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
|
|
583
|
-
${featureE2eDirs
|
|
584
|
-
.map((d) => {
|
|
585
|
-
const rel = path.relative(cwd, d);
|
|
586
|
-
const name = path.basename(path.dirname(d));
|
|
587
|
-
const e2eName = `${name}-${e2eFolderName}`;
|
|
588
|
-
return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
|
|
589
|
-
- \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
590
|
-
- \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
|
|
591
|
-
- \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).`;
|
|
592
|
-
})
|
|
593
|
-
.join('\n')}
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
## 2. Zero-Import Consumption Flow & Mechanics
|
|
598
|
-
|
|
599
|
-
TestSpectra completely eliminates boilerplate \`import\` statements across all test specs, steps, and page objects.
|
|
600
|
-
|
|
601
|
-
### How It Works:
|
|
602
|
-
1. **Centralized Ambient Aggregation**:
|
|
603
|
-
TestSpectra Language Service Plugin & CLI automatically scan:
|
|
604
|
-
- **Local Feature Entities**: \`<feature>/${e2eFolderName}/page-objects/\`, \`<feature>/${e2eFolderName}/steps/\`, \`<feature>/${e2eFolderName}/actions/\`
|
|
605
|
-
- **Shared Library Entities**: \`./${sharedTestingRelPath}/page-objects/\`, \`./${sharedTestingRelPath}/steps/\`, \`./${sharedTestingRelPath}/actions/\`, \`./${sharedTestingRelPath}/fixtures/\`
|
|
606
|
-
2. **Ambient Typing Generation**:
|
|
607
|
-
These are compiled into \`.testspectra/types/web.d.ts\`, \`android.d.ts\`, \`ios.d.ts\`, \`fixtures.d.ts\`.
|
|
608
|
-
3. **Instant IDE Autocomplete & Zero Clutter**:
|
|
609
|
-
Specs consume both local feature objects and shared library utilities globally.
|
|
610
|
-
|
|
611
|
-
### Code Example:
|
|
612
|
-
\`\`\`typescript
|
|
613
|
-
// packages/features/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts
|
|
614
|
-
// 🌟 NOTICE: 100% Zero-Import & Flat Execution! (No import statement, no describe wrapper)
|
|
615
|
-
|
|
616
|
-
it("should navigate via shared header and login with local POM", async () => {
|
|
617
|
-
// 1. Consumed from Shared Testing Library (${sharedTestingRelPath}/page-objects/NavigationBar)
|
|
618
|
-
await NavigationBar.goToLogin();
|
|
619
|
-
|
|
620
|
-
// 2. Consumed from Shared Testing Library (${sharedTestingRelPath}/steps/loginAsAdmin)
|
|
621
|
-
await Step.loginAsAdmin();
|
|
622
|
-
|
|
623
|
-
// 3. Consumed from Local Feature POM (page-objects/LoginPage)
|
|
624
|
-
await LoginPage.flashAlert.shouldBeVisible();
|
|
625
|
-
await LoginPage.flashAlert.shouldContainText("Welcome");
|
|
626
|
-
|
|
627
|
-
// 4. Consumed from Shared Testing Library Custom Action (${sharedTestingRelPath}/actions/dismissBanner)
|
|
628
|
-
await Spectra.dismissBanner();
|
|
629
|
-
});
|
|
630
|
-
\`\`\`
|
|
631
|
-
|
|
632
|
-
---
|
|
633
|
-
|
|
634
|
-
## 3. How to Run Tests
|
|
635
|
-
|
|
636
|
-
You can execute tests either from the **Monorepo Root via Nx** or directly inside each **Feature Directory via CLI**:
|
|
637
|
-
|
|
638
|
-
### Option A: From Root Monorepo via Nx (Recommended for CI & Team Workflows)
|
|
639
|
-
|
|
640
|
-
\`\`\`bash
|
|
641
|
-
# 1. Run E2E for a SPECIFIC feature module (e.g. auth)
|
|
642
|
-
pnpm nx run auth-${e2eFolderName}:e2e
|
|
643
|
-
|
|
644
|
-
# 2. Run E2E for a specific feature on ANDROID or IOS
|
|
645
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
|
|
646
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=ios
|
|
647
|
-
|
|
648
|
-
# 3. Run E2E for a specific feature in HEADLESS browser mode
|
|
649
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=headless
|
|
650
|
-
|
|
651
|
-
# 4. Run ALL feature E2E suites across the entire monorepo in parallel
|
|
652
|
-
pnpm nx run-many -t e2e
|
|
653
|
-
|
|
654
|
-
# 5. Run only E2E tests for features modified in current Git branch / PR
|
|
655
|
-
pnpm nx affected -t e2e
|
|
656
|
-
\`\`\`
|
|
657
|
-
|
|
658
|
-
### Option B: From Inside the Feature Directory (Direct CLI)
|
|
659
|
-
|
|
660
|
-
\`\`\`bash
|
|
661
|
-
# Navigate to the feature e2e folder
|
|
662
|
-
cd ${featureE2eDirs.length > 0 ? path.relative(cwd, featureE2eDirs[0]).replace(/\\/g, '/') : `modules/auth/${e2eFolderName}`}
|
|
663
|
-
|
|
664
|
-
# Run all specs in this feature
|
|
665
|
-
pnpm spectra run
|
|
666
|
-
|
|
667
|
-
# Run a specific test case file
|
|
668
|
-
pnpm spectra run specs/Auth/TC-AUTH-01/web.test.ts
|
|
669
|
-
|
|
670
|
-
# Target Android device or emulator
|
|
671
|
-
pnpm spectra run --target android
|
|
672
|
-
|
|
673
|
-
# Run in headed / interactive browser mode
|
|
674
|
-
pnpm spectra run --no-headless
|
|
675
|
-
\`\`\`
|
|
676
|
-
|
|
677
|
-
---
|
|
678
|
-
|
|
679
|
-
## 4. Type Checking & Validation
|
|
680
|
-
|
|
681
|
-
\`\`\`bash
|
|
682
|
-
# Type check all E2E projects via Nx
|
|
683
|
-
pnpm nx run-many -t type-check
|
|
684
|
-
|
|
685
|
-
# Or standard TypeScript project references build from root
|
|
686
|
-
pnpm type-check
|
|
687
|
-
# or: npx tsc -b
|
|
688
|
-
\`\`\`
|
|
689
|
-
`;
|
|
690
|
-
fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), archDocContent, 'utf-8');
|
|
564
|
+
generateArchitectureDoc(cwd);
|
|
691
565
|
// 8. Ensure root .gitignore ignores .testspectra/ and *.tsbuildinfo
|
|
692
566
|
const rootGitignorePath = path.join(cwd, '.gitignore');
|
|
693
567
|
const ignoreEntries = ['.testspectra/', '*.tsbuildinfo', '.nx/cache'];
|
|
@@ -741,34 +615,7 @@ pnpm type-check
|
|
|
741
615
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
742
616
|
ensureVsCodeExtensions(cwd);
|
|
743
617
|
// Standalone Architecture Documentation
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
This project is built with the TestSpectra testing framework.
|
|
747
|
-
|
|
748
|
-
## Structure & File Map
|
|
749
|
-
- \`./spectra.config.ts\`: Typed runner configuration.
|
|
750
|
-
- \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
751
|
-
- \`./page-objects/\`: Page Object models per platform.
|
|
752
|
-
- \`./steps/\`: Business flows accessible globally via \`Step.*\`.
|
|
753
|
-
- \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
|
|
754
|
-
- \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
|
|
755
|
-
- \`./global-hooks/\`: Global suite lifecycle hooks.
|
|
756
|
-
- \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
|
|
757
|
-
|
|
758
|
-
## Execution
|
|
759
|
-
\`\`\`bash
|
|
760
|
-
# Run web test suite
|
|
761
|
-
pnpm test
|
|
762
|
-
|
|
763
|
-
# Run Android / iOS
|
|
764
|
-
pnpm spectra run --target android
|
|
765
|
-
pnpm spectra run --target ios
|
|
766
|
-
|
|
767
|
-
# Type check
|
|
768
|
-
pnpm type-check
|
|
769
|
-
\`\`\`
|
|
770
|
-
`;
|
|
771
|
-
fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), standaloneArchDoc, 'utf-8');
|
|
618
|
+
generateArchitectureDoc(cwd);
|
|
772
619
|
// Safely update root README.md (do not overwrite existing content)
|
|
773
620
|
const standaloneReadmePath = path.join(cwd, 'README.md');
|
|
774
621
|
const standaloneArchSection = `\n## E2E Testing Architecture (TestSpectra)\n\nDetailed test architecture and folder map are documented in: \n👉 **[ARCHITECTURE.md](./ARCHITECTURE.md)**\n\n- **Run tests**: \`pnpm test\`\n- **Type-check**: \`pnpm type-check\`\n`;
|
package/dist/commands/install.js
CHANGED
|
@@ -17,6 +17,9 @@ export async function installCommand(item, options = {}) {
|
|
|
17
17
|
if (item) {
|
|
18
18
|
args.push(item);
|
|
19
19
|
}
|
|
20
|
+
if (options.force) {
|
|
21
|
+
args.push('--force');
|
|
22
|
+
}
|
|
20
23
|
return new Promise((resolve) => {
|
|
21
24
|
let child;
|
|
22
25
|
if (binPath === 'cargo') {
|
|
@@ -34,6 +37,7 @@ export async function installCommand(item, options = {}) {
|
|
|
34
37
|
cwd: workspaceRoot,
|
|
35
38
|
env: runnerEnv,
|
|
36
39
|
stdio: 'inherit',
|
|
40
|
+
windowsHide: true,
|
|
37
41
|
});
|
|
38
42
|
}
|
|
39
43
|
else {
|
|
@@ -41,6 +45,7 @@ export async function installCommand(item, options = {}) {
|
|
|
41
45
|
cwd,
|
|
42
46
|
env: runnerEnv,
|
|
43
47
|
stdio: 'inherit',
|
|
48
|
+
windowsHide: true,
|
|
44
49
|
});
|
|
45
50
|
}
|
|
46
51
|
child.on('close', (code) => {
|
package/dist/commands/license.js
CHANGED
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Reporter } from '../runner/reporter.js';
|
|
|
6
6
|
import { TypeGenerator } from '../types/generator.js';
|
|
7
7
|
import { DemoServer } from '../utils/demo-server.js';
|
|
8
8
|
import { DemoApiServer } from '../utils/api-server.js';
|
|
9
|
+
import { DevServerManager } from '../utils/dev-server-manager.js';
|
|
9
10
|
function renderSummaryCard(options) {
|
|
10
11
|
const width = 64;
|
|
11
12
|
const borderColor = options.isPassed ? '\x1b[38;2;52;211;153m' : '\x1b[38;2;248;113;113m';
|
|
@@ -157,9 +158,29 @@ export async function runCommand(specPaths, options = {}) {
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
const config = await ConfigLoader.loadConfig(cwd);
|
|
161
|
+
if (options.baseUrl) {
|
|
162
|
+
config.webConfig.baseUrl = options.baseUrl;
|
|
163
|
+
}
|
|
164
|
+
else if (options.host) {
|
|
165
|
+
if (config.webConfig.hosts && config.webConfig.hosts[options.host]) {
|
|
166
|
+
config.webConfig.baseUrl = config.webConfig.hosts[options.host];
|
|
167
|
+
console.log(`\x1b[36m[TestSpectra]\x1b[0m 🎯 Active Host target: \x1b[33m${options.host}\x1b[0m (\x1b[32m${config.webConfig.baseUrl}\x1b[0m)`);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
console.warn(`\x1b[33m[TestSpectra] Warning: Target host "${options.host}" is not defined in spectra.config.ts (webConfig.hosts). Falling back to ${config.webConfig.baseUrl}\x1b[0m`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
160
173
|
let demoServer = null;
|
|
161
174
|
let apiServer = null;
|
|
175
|
+
const devServerManager = new DevServerManager();
|
|
162
176
|
try {
|
|
177
|
+
const configuredDevServers = [
|
|
178
|
+
...(config.webConfig.devServers || []),
|
|
179
|
+
...(config.webConfig.devServer ? [config.webConfig.devServer] : []),
|
|
180
|
+
];
|
|
181
|
+
if (configuredDevServers.length > 0 && !options.demo) {
|
|
182
|
+
await devServerManager.startAll(configuredDevServers, options.host, cwd);
|
|
183
|
+
}
|
|
163
184
|
if (options.demo) {
|
|
164
185
|
demoServer = new DemoServer(5173);
|
|
165
186
|
try {
|
|
@@ -343,6 +364,7 @@ export async function runCommand(specPaths, options = {}) {
|
|
|
343
364
|
process.exit(1);
|
|
344
365
|
}
|
|
345
366
|
finally {
|
|
367
|
+
await devServerManager.stopAll();
|
|
346
368
|
if (demoServer) {
|
|
347
369
|
await demoServer.stop();
|
|
348
370
|
}
|
package/dist/commands/test.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface TestCommandOptions {
|
|
2
2
|
cwd?: string;
|
|
3
|
+
runner?: 'spectra' | 'vitest';
|
|
3
4
|
headed?: boolean;
|
|
4
5
|
headless?: boolean;
|
|
5
6
|
stepDelay?: string | number;
|
|
@@ -8,6 +9,18 @@ export interface TestCommandOptions {
|
|
|
8
9
|
output?: string;
|
|
9
10
|
concurrency?: string | number;
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Guarantees the `.testspectra` solution-tsconfig scaffolding exists before Vitest starts.
|
|
14
|
+
*
|
|
15
|
+
* The bundled Vitest browser runner transpiles raw TypeScript specs/page-objects Just-In-Time.
|
|
16
|
+
* When it does, `vite:esbuild` walks the nearest `tsconfig.json` and tries to read every entry in
|
|
17
|
+
* its `references` — including `./.testspectra/tsconfig.json`. That directory is gitignored, so on
|
|
18
|
+
* a clean clone / fresh CI checkout the reference points at a file that does not exist, esbuild
|
|
19
|
+
* throws `ENOENT`, Vite answers HTTP 500, and every suite dies with "Failed to fetch dynamically
|
|
20
|
+
* imported module" before a single assertion runs. Regenerating the scaffolding up front (same
|
|
21
|
+
* output as `spectra sync-types`) keeps the reference resolvable.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ensureTestSpectraScaffolding(cwd: string): void;
|
|
11
24
|
/**
|
|
12
25
|
* `spectra test` — delegates to `@testspectra/react`'s bundled Vitest browser-mode runner for
|
|
13
26
|
* component testing. Deliberately separate from `spectra run` (E2E via core/orchestrator): see
|
package/dist/commands/test.js
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import * as p from '@clack/prompts';
|
|
5
|
+
import { ConfigLoader } from '../config/loader.js';
|
|
4
6
|
import { renderSummaryView } from '../reporter/summary-view.js';
|
|
5
7
|
import { Reporter } from '../runner/reporter.js';
|
|
8
|
+
import { RustCoreBridge } from '../runner/bridge.js';
|
|
9
|
+
import { TypeGenerator } from '../generator/type-generator.js';
|
|
6
10
|
import { runSystemChecks } from './doctor.js';
|
|
11
|
+
/**
|
|
12
|
+
* Guarantees the `.testspectra` solution-tsconfig scaffolding exists before Vitest starts.
|
|
13
|
+
*
|
|
14
|
+
* The bundled Vitest browser runner transpiles raw TypeScript specs/page-objects Just-In-Time.
|
|
15
|
+
* When it does, `vite:esbuild` walks the nearest `tsconfig.json` and tries to read every entry in
|
|
16
|
+
* its `references` — including `./.testspectra/tsconfig.json`. That directory is gitignored, so on
|
|
17
|
+
* a clean clone / fresh CI checkout the reference points at a file that does not exist, esbuild
|
|
18
|
+
* throws `ENOENT`, Vite answers HTTP 500, and every suite dies with "Failed to fetch dynamically
|
|
19
|
+
* imported module" before a single assertion runs. Regenerating the scaffolding up front (same
|
|
20
|
+
* output as `spectra sync-types`) keeps the reference resolvable.
|
|
21
|
+
*/
|
|
22
|
+
export function ensureTestSpectraScaffolding(cwd) {
|
|
23
|
+
const masterConfigPath = path.join(cwd, '.testspectra', 'tsconfig.json');
|
|
24
|
+
if (fs.existsSync(masterConfigPath))
|
|
25
|
+
return;
|
|
26
|
+
TypeGenerator.generateAll(cwd);
|
|
27
|
+
}
|
|
7
28
|
/**
|
|
8
29
|
* Finds the Chrome-for-Testing binary that `spectra doctor` already installs/manages for the
|
|
9
30
|
* E2E CDP driver (`core/driver-cdp`) — same install, same binary. `@testspectra/react` must not
|
|
@@ -14,8 +35,10 @@ import { runSystemChecks } from './doctor.js';
|
|
|
14
35
|
*/
|
|
15
36
|
async function resolveBrowserExecutable(cwd) {
|
|
16
37
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
38
|
+
// `--only chrome` runs exactly one check — no adb/bun/git spawns, no Matchers lookup — since
|
|
39
|
+
// this call exists solely to locate the managed browser binary.
|
|
40
|
+
const { dependencies } = await runSystemChecks(cwd, { only: 'chrome' });
|
|
41
|
+
const chrome = dependencies.find((d) => d.category === 'web' && d.name.includes('Chrome'));
|
|
19
42
|
return chrome?.installed && chrome.location ? chrome.location : null;
|
|
20
43
|
}
|
|
21
44
|
catch {
|
|
@@ -30,6 +53,11 @@ async function resolveBrowserExecutable(cwd) {
|
|
|
30
53
|
*/
|
|
31
54
|
export async function testCommand(options = {}) {
|
|
32
55
|
const cwd = options.cwd ?? process.cwd();
|
|
56
|
+
// User-facing phase log. Deliberately describes TestSpectra's own stages only — never the
|
|
57
|
+
// bundled runtime/toolchain underneath — so the CLI surface stays implementation-agnostic.
|
|
58
|
+
const logPhase = (message) => p.log.step(chalk.dim(message));
|
|
59
|
+
logPhase('Preparing component test workspace…');
|
|
60
|
+
ensureTestSpectraScaffolding(cwd);
|
|
33
61
|
let reactPkg;
|
|
34
62
|
try {
|
|
35
63
|
reactPkg = await import('@testspectra/react');
|
|
@@ -41,6 +69,7 @@ export async function testCommand(options = {}) {
|
|
|
41
69
|
process.exit(1);
|
|
42
70
|
return;
|
|
43
71
|
}
|
|
72
|
+
logPhase('Resolving managed test browser…');
|
|
44
73
|
const browserExecutablePath = await resolveBrowserExecutable(cwd);
|
|
45
74
|
if (!browserExecutablePath) {
|
|
46
75
|
p.log.error(chalk.red('✖ TestSpectra-managed Chrome for Testing was not found.'));
|
|
@@ -64,6 +93,21 @@ export async function testCommand(options = {}) {
|
|
|
64
93
|
else if (options.headless !== undefined) {
|
|
65
94
|
headless = options.headless;
|
|
66
95
|
}
|
|
96
|
+
// Fallback to spectra.config.ts if options were not explicitly provided on CLI
|
|
97
|
+
try {
|
|
98
|
+
const config = await ConfigLoader.loadConfig(cwd);
|
|
99
|
+
if (stepDelayMs === undefined && config?.webConfig?.stepDelay !== undefined) {
|
|
100
|
+
const parsed = parseInt(String(config.webConfig.stepDelay), 10);
|
|
101
|
+
if (!isNaN(parsed) && parsed >= 0)
|
|
102
|
+
stepDelayMs = parsed;
|
|
103
|
+
}
|
|
104
|
+
if (!options.headed && options.headless === undefined && config?.webConfig?.headless !== undefined) {
|
|
105
|
+
headless = String(config.webConfig.headless) !== 'false' && Boolean(config.webConfig.headless);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
/* ignore config load failures */
|
|
110
|
+
}
|
|
67
111
|
const concurrency = options.concurrency !== undefined ? parseInt(String(options.concurrency), 10) : undefined;
|
|
68
112
|
const outputFile = options.output ? path.resolve(cwd, options.output) : undefined;
|
|
69
113
|
// Reuses the exact same semantic terminal reporter `spectra run` drives (see
|
|
@@ -72,31 +116,81 @@ export async function testCommand(options = {}) {
|
|
|
72
116
|
// / setup.ts), so the same Reporter class parses them with zero changes on either side.
|
|
73
117
|
const reporter = new Reporter();
|
|
74
118
|
const runStartedAt = Date.now();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
let ok = false;
|
|
120
|
+
if (options.runner === 'vitest') {
|
|
121
|
+
logPhase('Starting component test runner (Vitest Browser Mode)…');
|
|
122
|
+
ok = await reactPkg.runComponentTests({
|
|
123
|
+
runner: 'vitest',
|
|
124
|
+
cwd,
|
|
125
|
+
headless,
|
|
126
|
+
stepDelayMs,
|
|
127
|
+
spec: options.spec,
|
|
128
|
+
outputFile,
|
|
129
|
+
concurrency: concurrency !== undefined && !isNaN(concurrency) ? concurrency : undefined,
|
|
130
|
+
browserExecutablePath,
|
|
131
|
+
onLine: (line) => {
|
|
132
|
+
const cleanLine = line.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '').trim();
|
|
133
|
+
if (!cleanLine)
|
|
134
|
+
return;
|
|
135
|
+
if (cleanLine.startsWith('[TESTSPECTRA_')) {
|
|
136
|
+
reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message: cleanLine });
|
|
137
|
+
if (!reporter.isInteractiveMode()) {
|
|
138
|
+
console.log(cleanLine);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (reactPkg.isVitestNoise?.(cleanLine)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const sanitized = reactPkg.sanitizeErrorMessage?.(cleanLine) ?? cleanLine;
|
|
146
|
+
reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'ERROR', message: sanitized });
|
|
147
|
+
if (!reporter.isInteractiveMode()) {
|
|
148
|
+
console.error(chalk.red(sanitized));
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
logPhase('Starting component test runner (Spectra Native CDP Engine)…');
|
|
155
|
+
const bundleServer = await reactPkg.startBundleServer({
|
|
156
|
+
testRoot: cwd,
|
|
157
|
+
stepDelayMs: stepDelayMs ?? 0,
|
|
158
|
+
});
|
|
159
|
+
try {
|
|
160
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
161
|
+
const globalAppDataPath = path.join(home, '.testspectra');
|
|
162
|
+
const appDataPath = globalAppDataPath;
|
|
163
|
+
const runResult = await RustCoreBridge.run({
|
|
164
|
+
baseDir: cwd,
|
|
165
|
+
appDataPath,
|
|
166
|
+
platform: 'component',
|
|
167
|
+
config: {
|
|
168
|
+
bundleServerUrl: bundleServer.url,
|
|
169
|
+
webConfig: {
|
|
170
|
+
headless,
|
|
171
|
+
stepDelay: stepDelayMs,
|
|
172
|
+
browserExecutablePath,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
specs: options.spec ? [options.spec] : undefined,
|
|
176
|
+
concurrency: concurrency !== undefined && !isNaN(concurrency) ? concurrency : undefined,
|
|
177
|
+
outputJsonPath: outputFile,
|
|
178
|
+
}, reporter);
|
|
179
|
+
ok = runResult.status.toLowerCase() === 'passed';
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
bundleServer.stop();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
92
185
|
const durationMs = Date.now() - runStartedAt;
|
|
93
186
|
const result = reporter.generateResult(ok ? 'passed' : 'failed', `${(durationMs / 1000).toFixed(1)}s`);
|
|
94
|
-
const
|
|
187
|
+
const failedCount = !ok && result.failedCount === 0 ? 1 : result.failedCount;
|
|
188
|
+
const totalTests = result.passedCount + failedCount;
|
|
95
189
|
renderSummaryView({
|
|
96
190
|
totalSuites: totalTests,
|
|
97
191
|
totalTests,
|
|
98
192
|
passedCount: result.passedCount,
|
|
99
|
-
failedCount
|
|
193
|
+
failedCount,
|
|
100
194
|
durationMs,
|
|
101
195
|
reportLocation: outputFile ? path.relative(cwd, outputFile) : '(use -o/--output to save a report)',
|
|
102
196
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PackageUpdate {
|
|
2
|
+
pkg: string;
|
|
3
|
+
current: string | null;
|
|
4
|
+
latest: string | null;
|
|
5
|
+
needsUpdate: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function upgradeCommand(requestedPackages?: string[], options?: {
|
|
8
|
+
check?: boolean;
|
|
9
|
+
packagesOnly?: boolean;
|
|
10
|
+
driversOnly?: boolean;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
}): Promise<void>;
|