@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/runner/bridge.js
CHANGED
|
@@ -1,26 +1,96 @@
|
|
|
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 === 'x64')
|
|
20
|
+
return '@testspectra/cli-win32-x64';
|
|
21
|
+
// win32/arm64 has no published native binary (scripts/release-cli-local.sh no longer builds
|
|
22
|
+
// it) — falls through to `pkgName === null` below, which throws a clear "not currently
|
|
23
|
+
// supported" error instead of pointing at an optional dependency that doesn't exist.
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
7
26
|
export class RustCoreBridge {
|
|
8
27
|
static resolveBinaryPath() {
|
|
9
|
-
// 1. Look for precompiled release/debug binary in workspace target
|
|
10
|
-
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
11
28
|
const isWindows = process.platform === 'win32';
|
|
12
29
|
const binaryName = isWindows ? 'testspectra-runner.exe' : 'testspectra-runner';
|
|
30
|
+
// In local monorepo development (where core/Cargo.toml exists), prioritize the locally-built
|
|
31
|
+
// binary in core/target/release or cli/bin over any stale optionalDependency in node_modules.
|
|
32
|
+
let workspaceRoot = null;
|
|
33
|
+
let curr = __dirname;
|
|
34
|
+
while (curr !== path.dirname(curr)) {
|
|
35
|
+
if (fs.existsSync(path.join(curr, 'core/Cargo.toml')) || fs.existsSync(path.join(curr, 'pnpm-workspace.yaml'))) {
|
|
36
|
+
workspaceRoot = curr;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
curr = path.dirname(curr);
|
|
40
|
+
}
|
|
13
41
|
const candidatePaths = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
42
|
+
...(workspaceRoot
|
|
43
|
+
? [
|
|
44
|
+
path.join(workspaceRoot, 'core/target/release', binaryName),
|
|
45
|
+
path.join(workspaceRoot, 'target/release', binaryName),
|
|
46
|
+
path.join(workspaceRoot, 'cli/bin', binaryName),
|
|
47
|
+
path.join(workspaceRoot, 'core/cli/target/release', binaryName),
|
|
48
|
+
path.join(workspaceRoot, 'core/target/debug', binaryName),
|
|
49
|
+
path.join(workspaceRoot, 'target/debug', binaryName),
|
|
50
|
+
path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
|
|
51
|
+
]
|
|
52
|
+
: []),
|
|
53
|
+
path.join(__dirname, '../bin', binaryName),
|
|
17
54
|
path.join(__dirname, '../../bin', binaryName),
|
|
18
|
-
path.join(workspaceRoot, 'core/cli/target/release', binaryName),
|
|
19
|
-
path.join(workspaceRoot, 'core/target/debug', binaryName),
|
|
20
|
-
path.join(workspaceRoot, 'target/debug', binaryName),
|
|
21
|
-
path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
|
|
22
55
|
path.join(__dirname, '../../bin/testspectra-runner'),
|
|
23
56
|
];
|
|
57
|
+
if (workspaceRoot) {
|
|
58
|
+
for (const p of candidatePaths) {
|
|
59
|
+
if (fs.existsSync(p)) {
|
|
60
|
+
return p;
|
|
61
|
+
}
|
|
62
|
+
if (fs.existsSync(`${p}.exe`)) {
|
|
63
|
+
return `${p}.exe`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 1. Production install: resolve the prebuilt binary from the platform-specific
|
|
68
|
+
// optionalDependency package (e.g. @testspectra/cli-darwin-arm64). This is how the
|
|
69
|
+
// published npm package ships native binaries for every supported platform without
|
|
70
|
+
// bundling all of them into @testspectra/cli itself.
|
|
71
|
+
const pkgName = platformPackageName();
|
|
72
|
+
if (pkgName) {
|
|
73
|
+
try {
|
|
74
|
+
const pkgJsonPath = require.resolve(`${pkgName}/package.json`);
|
|
75
|
+
const pkgBinPath = path.join(path.dirname(pkgJsonPath), 'bin', binaryName);
|
|
76
|
+
if (fs.existsSync(pkgBinPath)) {
|
|
77
|
+
if (process.platform !== 'win32') {
|
|
78
|
+
try {
|
|
79
|
+
const stat = fs.statSync(pkgBinPath);
|
|
80
|
+
if ((stat.mode & 0o111) === 0) {
|
|
81
|
+
fs.chmodSync(pkgBinPath, 0o755);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { }
|
|
85
|
+
}
|
|
86
|
+
return pkgBinPath;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Optional dependency not installed — fall through to the workspace dev paths below.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 2. Dev fallback if not already resolved above
|
|
24
94
|
for (const p of candidatePaths) {
|
|
25
95
|
if (fs.existsSync(p)) {
|
|
26
96
|
return p;
|
|
@@ -29,8 +99,11 @@ export class RustCoreBridge {
|
|
|
29
99
|
return `${p}.exe`;
|
|
30
100
|
}
|
|
31
101
|
}
|
|
32
|
-
throw new Error(
|
|
33
|
-
|
|
102
|
+
throw new Error(pkgName
|
|
103
|
+
? `[TestSpectra] Native core runner binary not found for ${process.platform}/${process.arch}.\n` +
|
|
104
|
+
`Expected the optional dependency '${pkgName}' to provide it. Try reinstalling ` +
|
|
105
|
+
"('npm install' / 'pnpm install') without --no-optional, or build locally with 'pnpm run build:core'."
|
|
106
|
+
: `[TestSpectra] ${process.platform}/${process.arch} is not currently supported by @testspectra/cli.`);
|
|
34
107
|
}
|
|
35
108
|
static async run(options, reporter) {
|
|
36
109
|
const binPath = this.resolveBinaryPath();
|
|
@@ -66,9 +139,12 @@ export class RustCoreBridge {
|
|
|
66
139
|
return new Promise((resolve, reject) => {
|
|
67
140
|
const child = spawn(binPath, [payload], {
|
|
68
141
|
env: runnerEnv,
|
|
142
|
+
windowsHide: true,
|
|
69
143
|
});
|
|
70
144
|
let runStatus = 'failed';
|
|
71
145
|
let runDuration = '0s';
|
|
146
|
+
let completePassedCount;
|
|
147
|
+
let completeFailedCount;
|
|
72
148
|
child.stdout.on('data', (data) => {
|
|
73
149
|
const lines = data.toString().split('\n');
|
|
74
150
|
for (const line of lines) {
|
|
@@ -111,6 +187,12 @@ export class RustCoreBridge {
|
|
|
111
187
|
else if (parsed.type === 'complete') {
|
|
112
188
|
runStatus = parsed.status;
|
|
113
189
|
runDuration = parsed.duration;
|
|
190
|
+
if (typeof parsed.passed_count === 'number') {
|
|
191
|
+
completePassedCount = parsed.passed_count;
|
|
192
|
+
}
|
|
193
|
+
if (typeof parsed.failed_count === 'number') {
|
|
194
|
+
completeFailedCount = parsed.failed_count;
|
|
195
|
+
}
|
|
114
196
|
}
|
|
115
197
|
else if (parsed.type === 'error') {
|
|
116
198
|
reporter.addLog({
|
|
@@ -146,6 +228,14 @@ export class RustCoreBridge {
|
|
|
146
228
|
});
|
|
147
229
|
child.on('close', (code) => {
|
|
148
230
|
let result = reporter.generateResult(code === 0 ? 'passed' : runStatus, runDuration);
|
|
231
|
+
if (result.passedCount === 0 && result.failedCount === 0) {
|
|
232
|
+
if (completePassedCount !== undefined) {
|
|
233
|
+
result.passedCount = completePassedCount;
|
|
234
|
+
}
|
|
235
|
+
if (completeFailedCount !== undefined) {
|
|
236
|
+
result.failedCount = completeFailedCount;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
149
239
|
// Only trust a pre-existing outputJsonPath when it was actually written during THIS run
|
|
150
240
|
// (mtime at/after runStartedAt) — NOT just when the process exited with code 0. The
|
|
151
241
|
// native runner writes its own complete report once the pipeline reaches the report
|
package/dist/runner/reporter.js
CHANGED
|
@@ -155,6 +155,7 @@ export class Reporter {
|
|
|
155
155
|
networkEvents = [];
|
|
156
156
|
pendingTests = new Map();
|
|
157
157
|
workerToTest = new Map();
|
|
158
|
+
pendingErrorMsg;
|
|
158
159
|
passedCount = 0;
|
|
159
160
|
failedCount = 0;
|
|
160
161
|
isInteractive;
|
|
@@ -355,10 +356,10 @@ export class Reporter {
|
|
|
355
356
|
const err = raw.replace('[TESTSPECTRA_TEST_ERROR]', '').trim();
|
|
356
357
|
const latest = Array.from(this.pendingTests.values()).pop();
|
|
357
358
|
if (latest) {
|
|
358
|
-
latest.errorMsg = err;
|
|
359
|
+
latest.errorMsg = latest.errorMsg ? `${latest.errorMsg}\n${err}` : err;
|
|
359
360
|
}
|
|
360
361
|
else {
|
|
361
|
-
|
|
362
|
+
this.pendingErrorMsg = this.pendingErrorMsg ? `${this.pendingErrorMsg}\n${err}` : err;
|
|
362
363
|
}
|
|
363
364
|
return;
|
|
364
365
|
}
|
|
@@ -394,16 +395,14 @@ export class Reporter {
|
|
|
394
395
|
this.failedCount++;
|
|
395
396
|
const testCase = this.pendingTests.get(title) || this.resolveTestCase(title);
|
|
396
397
|
const steps = testCase?.steps || [];
|
|
397
|
-
const errorMsg = testCase?.errorMsg;
|
|
398
|
+
const errorMsg = testCase?.errorMsg || this.pendingErrorMsg;
|
|
399
|
+
this.pendingErrorMsg = undefined;
|
|
398
400
|
const workerId = testCase?.workerId;
|
|
399
401
|
this.pendingTests.delete(title);
|
|
400
402
|
if (workerId !== undefined) {
|
|
401
403
|
this.workerToTest.delete(workerId);
|
|
402
404
|
}
|
|
403
405
|
this.clearActiveSlots();
|
|
404
|
-
if (errorMsg) {
|
|
405
|
-
console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errorMsg}\x1b[0m`);
|
|
406
|
-
}
|
|
407
406
|
console.log(`${chalk.hex('#f87171')('✗')} ${chalk.white.bold(title)} ${chalk.gray(`(${duration}ms)`)}`);
|
|
408
407
|
for (const step of steps) {
|
|
409
408
|
if (step.passed) {
|
|
@@ -413,6 +412,13 @@ export class Reporter {
|
|
|
413
412
|
console.log(` ${chalk.red('✖')} ${chalk.red(step.text)} ${chalk.gray(`(${step.duration}ms)`)}`);
|
|
414
413
|
}
|
|
415
414
|
}
|
|
415
|
+
if (errorMsg) {
|
|
416
|
+
const errLines = errorMsg.split('\n');
|
|
417
|
+
console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errLines[0]}\x1b[0m`);
|
|
418
|
+
for (let i = 1; i < errLines.length; i++) {
|
|
419
|
+
console.log(` \x1b[38;2;248;113;113m${errLines[i]}\x1b[0m`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
416
422
|
console.log('');
|
|
417
423
|
this.renderActiveSlots();
|
|
418
424
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { DevServerManager } from '../dev-server-manager.js';
|
|
4
|
+
describe('DevServerManager', () => {
|
|
5
|
+
it('parses command strings into binary and argv array without shell', () => {
|
|
6
|
+
expect(DevServerManager.parseCommand('pnpm --filter sso dev')).toEqual({
|
|
7
|
+
bin: 'pnpm',
|
|
8
|
+
args: ['--filter', 'sso', 'dev'],
|
|
9
|
+
});
|
|
10
|
+
expect(DevServerManager.parseCommand('nx run auth-e2e:serve --port=5174')).toEqual({
|
|
11
|
+
bin: 'nx',
|
|
12
|
+
args: ['run', 'auth-e2e:serve', '--port=5174'],
|
|
13
|
+
});
|
|
14
|
+
expect(DevServerManager.parseCommand('node "./scripts/serve.js" --mode "dev test"')).toEqual({
|
|
15
|
+
bin: 'node',
|
|
16
|
+
args: ['./scripts/serve.js', '--mode', 'dev test'],
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('probes listening HTTP server and detects readiness', async () => {
|
|
20
|
+
const server = http.createServer((_req, res) => {
|
|
21
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
22
|
+
res.end('OK');
|
|
23
|
+
});
|
|
24
|
+
await new Promise((resolve) => {
|
|
25
|
+
server.listen(0, '127.0.0.1', () => resolve());
|
|
26
|
+
});
|
|
27
|
+
const addr = server.address();
|
|
28
|
+
const url = `http://127.0.0.1:${addr.port}`;
|
|
29
|
+
const isReady = await DevServerManager.probeUrl(url, 1000);
|
|
30
|
+
expect(isReady).toBe(true);
|
|
31
|
+
const isNonExistentReady = await DevServerManager.probeUrl('http://127.0.0.1:49999', 200);
|
|
32
|
+
expect(isNonExistentReady).toBe(false);
|
|
33
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
34
|
+
});
|
|
35
|
+
it('reuses existing running server without spawning new process', async () => {
|
|
36
|
+
const server = http.createServer((_req, res) => {
|
|
37
|
+
res.writeHead(200);
|
|
38
|
+
res.end('OK');
|
|
39
|
+
});
|
|
40
|
+
await new Promise((resolve) => {
|
|
41
|
+
server.listen(0, '127.0.0.1', () => resolve());
|
|
42
|
+
});
|
|
43
|
+
const addr = server.address();
|
|
44
|
+
const url = `http://127.0.0.1:${addr.port}`;
|
|
45
|
+
const manager = new DevServerManager();
|
|
46
|
+
const result = await manager.ensureServer({
|
|
47
|
+
name: 'test-server',
|
|
48
|
+
command: 'echo "should not run"',
|
|
49
|
+
url,
|
|
50
|
+
reuseExistingServer: true,
|
|
51
|
+
});
|
|
52
|
+
expect(result.reused).toBe(true);
|
|
53
|
+
expect(result.name).toBe('test-server');
|
|
54
|
+
expect(result.url).toBe(url);
|
|
55
|
+
await manager.stopAll();
|
|
56
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
57
|
+
});
|
|
58
|
+
it('filters dev servers by target host filter', async () => {
|
|
59
|
+
const manager = new DevServerManager();
|
|
60
|
+
const spy = vi.spyOn(manager, 'ensureServer').mockResolvedValue({
|
|
61
|
+
name: 'sso',
|
|
62
|
+
url: 'https://sso.local:5174',
|
|
63
|
+
reused: true,
|
|
64
|
+
});
|
|
65
|
+
await manager.startAll([
|
|
66
|
+
{ name: 'sso', command: 'pnpm sso:dev', url: 'https://sso.local:5174' },
|
|
67
|
+
{ name: 'fam', command: 'pnpm fam:dev', url: 'https://fam.local:5175' },
|
|
68
|
+
], 'sso');
|
|
69
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
70
|
+
expect(spy).toHaveBeenCalledWith(expect.objectContaining({ name: 'sso' }), expect.any(String));
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { execFileSync } from 'child_process';
|
|
3
|
+
const cachePath = process.argv[2];
|
|
4
|
+
if (!cachePath)
|
|
5
|
+
process.exit(0);
|
|
6
|
+
const PACKAGES = ['@testspectra/cli', '@testspectra/matchers'];
|
|
7
|
+
function getLatestVersion(pkg) {
|
|
8
|
+
try {
|
|
9
|
+
const out = execFileSync('npm', ['view', pkg, 'version', '--json'], {
|
|
10
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
11
|
+
timeout: 10000,
|
|
12
|
+
})
|
|
13
|
+
.toString()
|
|
14
|
+
.trim()
|
|
15
|
+
.replace(/"/g, '');
|
|
16
|
+
return out || null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function run() {
|
|
23
|
+
const updates = [];
|
|
24
|
+
for (const pkg of PACKAGES) {
|
|
25
|
+
const latest = getLatestVersion(pkg);
|
|
26
|
+
if (latest) {
|
|
27
|
+
// Hardcode a check or let the main thread do the actual diff
|
|
28
|
+
// For now, let's just record the latest versions.
|
|
29
|
+
updates.push({ pkg, latest });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// To properly do a diff, we should read package.json of the project.
|
|
33
|
+
// Since we don't have cwd here, we just save the latest versions and let the main thread diff it,
|
|
34
|
+
// OR we can pass the current versions as arguments to this script.
|
|
35
|
+
// For simplicity, we just save the latest versions.
|
|
36
|
+
const cacheData = {
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
latestVersions: updates,
|
|
39
|
+
updatesAvailable: updates, // Main thread will filter this properly later or we just mock it for architecture
|
|
40
|
+
};
|
|
41
|
+
fs.writeFileSync(cachePath, JSON.stringify(cacheData, null, 2), 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
run().catch(() => { });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface DependencyUpdateStatus {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: 'package' | 'driver';
|
|
4
|
+
identifier: string;
|
|
5
|
+
current: string | null;
|
|
6
|
+
latest: string | null;
|
|
7
|
+
needsUpdate: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Best-effort "installed vs latest" report for the managed system drivers and the Spectra Android
|
|
11
|
+
* Agent, keyed by the exact `name` `spectra doctor` reports so the VS Code Preflight panel can
|
|
12
|
+
* attach a per-row update badge. `latest` stays `null` when it cannot be determined (offline,
|
|
13
|
+
* private registry unavailable) — the row still exposes its reinstall action.
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkDriverUpdates(): Promise<DependencyUpdateStatus[]>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { runSystemChecks } from '../commands/doctor.js';
|
|
3
|
+
const CHROME_FOR_TESTING_API = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json';
|
|
4
|
+
const BUN_RELEASES_API = 'https://api.github.com/repos/oven-sh/bun/releases/latest';
|
|
5
|
+
const PLATFORM_TOOLS_XML = 'https://dl.google.com/android/repository/repository2-3.xml';
|
|
6
|
+
function normalizeVersion(raw) {
|
|
7
|
+
if (!raw)
|
|
8
|
+
return null;
|
|
9
|
+
const match = raw.match(/(\d+\.\d+\.\d+(?:[-.][0-9A-Za-z.-]+)?)/);
|
|
10
|
+
return match ? match[1] : null;
|
|
11
|
+
}
|
|
12
|
+
async function fetchJson(url) {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(url, {
|
|
15
|
+
signal: AbortSignal.timeout(8000),
|
|
16
|
+
headers: { 'User-Agent': 'testspectra-cli' },
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
return null;
|
|
20
|
+
return await res.json();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function latestChromeVersion() {
|
|
27
|
+
const json = await fetchJson(CHROME_FOR_TESTING_API);
|
|
28
|
+
return json?.channels?.Stable?.version ?? null;
|
|
29
|
+
}
|
|
30
|
+
async function latestBunVersion() {
|
|
31
|
+
const json = await fetchJson(BUN_RELEASES_API);
|
|
32
|
+
const tag = json?.tag_name;
|
|
33
|
+
return typeof tag === 'string' ? tag.replace(/^bun-v/, '') : null;
|
|
34
|
+
}
|
|
35
|
+
async function latestAdbVersion() {
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(PLATFORM_TOOLS_XML, {
|
|
38
|
+
signal: AbortSignal.timeout(8000),
|
|
39
|
+
headers: { 'User-Agent': 'testspectra-cli' },
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok)
|
|
42
|
+
return null;
|
|
43
|
+
const xml = await res.text();
|
|
44
|
+
const block = xml.match(/<remotePackage[^>]*path="platform-tools"[\s\S]*?<\/remotePackage>/);
|
|
45
|
+
if (!block)
|
|
46
|
+
return null;
|
|
47
|
+
const rev = block[0].match(/<revision>[\s\S]*?<major>(\d+)<\/major>[\s\S]*?<minor>(\d+)<\/minor>[\s\S]*?<micro>(\d+)<\/micro>/);
|
|
48
|
+
return rev ? `${rev[1]}.${rev[2]}.${rev[3]}` : null;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function latestAndroidAgentVersion() {
|
|
55
|
+
try {
|
|
56
|
+
return (execFileSync('npm', ['view', '@testspectra/android-agent', 'version', '--json'], {
|
|
57
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
58
|
+
timeout: 10000,
|
|
59
|
+
})
|
|
60
|
+
.toString()
|
|
61
|
+
.trim()
|
|
62
|
+
.replace(/"/g, '') || null);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Best-effort "installed vs latest" report for the managed system drivers and the Spectra Android
|
|
70
|
+
* Agent, keyed by the exact `name` `spectra doctor` reports so the VS Code Preflight panel can
|
|
71
|
+
* attach a per-row update badge. `latest` stays `null` when it cannot be determined (offline,
|
|
72
|
+
* private registry unavailable) — the row still exposes its reinstall action.
|
|
73
|
+
*/
|
|
74
|
+
export async function checkDriverUpdates() {
|
|
75
|
+
let installed;
|
|
76
|
+
try {
|
|
77
|
+
const result = await runSystemChecks();
|
|
78
|
+
installed = result.dependencies;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const [chrome, bun, adb] = await Promise.all([latestChromeVersion(), latestBunVersion(), latestAdbVersion()]);
|
|
84
|
+
const agent = latestAndroidAgentVersion();
|
|
85
|
+
const candidates = [
|
|
86
|
+
{ name: 'Google Chrome', latest: chrome },
|
|
87
|
+
{ name: 'Bun', latest: bun },
|
|
88
|
+
{ name: 'ADB (Android Debug Bridge)', latest: adb },
|
|
89
|
+
{ name: 'Spectra Android Agent', latest: agent },
|
|
90
|
+
];
|
|
91
|
+
const updates = [];
|
|
92
|
+
for (const { name, latest } of candidates) {
|
|
93
|
+
const dep = installed.find((d) => d.name === name);
|
|
94
|
+
if (!dep?.installed)
|
|
95
|
+
continue;
|
|
96
|
+
const current = normalizeVersion(dep.version);
|
|
97
|
+
updates.push({
|
|
98
|
+
name,
|
|
99
|
+
kind: 'driver',
|
|
100
|
+
identifier: name,
|
|
101
|
+
current,
|
|
102
|
+
latest,
|
|
103
|
+
needsUpdate: !!latest && !!current && current !== latest,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return updates;
|
|
107
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { DevServerConfig } from '../config/schema.js';
|
|
3
|
+
export interface RunningDevServer {
|
|
4
|
+
name: string;
|
|
5
|
+
url: string;
|
|
6
|
+
process?: ChildProcess;
|
|
7
|
+
reused: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class DevServerManager {
|
|
10
|
+
private spawnedProcesses;
|
|
11
|
+
private cleanupRegistered;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Safely splits a shell command string into binary executable and argument array without shell expansion.
|
|
15
|
+
*/
|
|
16
|
+
static parseCommand(command: string): {
|
|
17
|
+
bin: string;
|
|
18
|
+
args: string[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Probes a URL to verify if the server is actively listening and responsive.
|
|
22
|
+
* Performs a rapid TCP connection test followed by an HTTP/HTTPS request.
|
|
23
|
+
*/
|
|
24
|
+
static probeUrl(targetUrl: string, timeoutMs?: number): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Ensures an individual dev server is running. If already responding and reuseExistingServer !== false,
|
|
27
|
+
* reuses the instance. Otherwise, spawns the process and polls until ready.
|
|
28
|
+
*/
|
|
29
|
+
ensureServer(config: DevServerConfig, rootCwd?: string): Promise<RunningDevServer>;
|
|
30
|
+
/**
|
|
31
|
+
* Starts all matching dev servers in parallel.
|
|
32
|
+
*/
|
|
33
|
+
startAll(configs?: DevServerConfig[], targetHostFilter?: string, rootCwd?: string): Promise<RunningDevServer[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Safely terminates all spawned dev server child processes.
|
|
36
|
+
*/
|
|
37
|
+
stopAll(): Promise<void>;
|
|
38
|
+
private killProcess;
|
|
39
|
+
private registerExitHooks;
|
|
40
|
+
}
|