@elench/testkit 0.1.68 → 0.1.71
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 +70 -10
- package/lib/app/doctor.mjs +1 -1
- package/lib/app/typecheck.mjs +8 -4
- package/lib/bundler/index.mjs +17 -17
- package/lib/cli/command-helpers.mjs +1 -1
- package/lib/cli/commands/doctor.mjs +1 -1
- package/lib/cli/commands/typecheck.mjs +1 -1
- package/lib/cli/known-failures.mjs +4 -4
- package/lib/cli/presentation/discovery-reporter.mjs +2 -2
- package/lib/config/{setup-loader.mjs → config-loader.mjs} +32 -32
- package/lib/config/index.mjs +16 -16
- package/lib/config/runtime.mjs +1 -1
- package/lib/config/telemetry.mjs +4 -4
- package/lib/config/validation.mjs +1 -1
- package/lib/{setup → config-api}/index.d.ts +7 -7
- package/lib/{setup → config-api}/index.mjs +10 -10
- package/lib/{setup → config-api}/index.test.mjs +16 -3
- package/lib/{setup → config-api}/runtime.mjs +10 -10
- package/lib/coverage/index.test.mjs +3 -3
- package/lib/discovery/file-metadata.mjs +1 -1
- package/lib/discovery/file-metadata.test.mjs +2 -2
- package/lib/discovery/index.d.ts +1 -1
- package/lib/discovery/index.mjs +28 -28
- package/lib/discovery/index.test.mjs +10 -10
- package/lib/drizzle/index.d.ts +14 -0
- package/lib/drizzle/index.mjs +15 -0
- package/lib/drizzle/index.test.mjs +33 -0
- package/lib/env/index.d.ts +17 -0
- package/lib/env/index.mjs +65 -0
- package/lib/env/index.test.mjs +82 -0
- package/lib/known-failures/github.mjs +4 -4
- package/lib/package.test.mjs +24 -4
- package/lib/playwright/index.d.ts +21 -0
- package/lib/playwright/index.mjs +53 -0
- package/lib/playwright/index.test.mjs +43 -0
- package/lib/runner/template-steps.mjs +26 -6
- package/lib/runner/template.mjs +2 -2
- package/lib/runtime-src/k6/scenario-suite.js +1 -1
- package/lib/runtime-src/k6/suite.js +1 -1
- package/lib/shared/build-config.mjs +13 -13
- package/lib/shared/build-config.test.mjs +4 -22
- package/lib/shared/configured-steps.mjs +24 -5
- package/lib/shared/configured-steps.test.mjs +29 -0
- package/lib/toolchains/index.mjs +2 -2
- package/lib/vitest/index.d.ts +12 -0
- package/lib/vitest/index.mjs +31 -0
- package/lib/vitest/index.test.mjs +20 -0
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +24 -8
- /package/lib/{setup → config-api}/next-runtime-tsconfig.mjs +0 -0
- /package/lib/{setup → config-api}/next-runtime-tsconfig.test.mjs +0 -0
package/lib/package.test.mjs
CHANGED
|
@@ -14,14 +14,30 @@ describe("package metadata", () => {
|
|
|
14
14
|
types: "./lib/index.d.ts",
|
|
15
15
|
default: "./lib/index.mjs",
|
|
16
16
|
});
|
|
17
|
-
expect(packageJson.exports["./
|
|
18
|
-
types: "./lib/
|
|
19
|
-
default: "./lib/
|
|
17
|
+
expect(packageJson.exports["./config"]).toEqual({
|
|
18
|
+
types: "./lib/config-api/index.d.ts",
|
|
19
|
+
default: "./lib/config-api/index.mjs",
|
|
20
|
+
});
|
|
21
|
+
expect(packageJson.exports["./drizzle"]).toEqual({
|
|
22
|
+
types: "./lib/drizzle/index.d.ts",
|
|
23
|
+
default: "./lib/drizzle/index.mjs",
|
|
24
|
+
});
|
|
25
|
+
expect(packageJson.exports["./env"]).toEqual({
|
|
26
|
+
types: "./lib/env/index.d.ts",
|
|
27
|
+
default: "./lib/env/index.mjs",
|
|
28
|
+
});
|
|
29
|
+
expect(packageJson.exports["./playwright"]).toEqual({
|
|
30
|
+
types: "./lib/playwright/index.d.ts",
|
|
31
|
+
default: "./lib/playwright/index.mjs",
|
|
20
32
|
});
|
|
21
33
|
expect(packageJson.exports["./runtime"]).toEqual({
|
|
22
34
|
types: "./lib/runtime/index.d.ts",
|
|
23
35
|
default: "./lib/runtime/index.mjs",
|
|
24
36
|
});
|
|
37
|
+
expect(packageJson.exports["./vitest"]).toEqual({
|
|
38
|
+
types: "./lib/vitest/index.d.ts",
|
|
39
|
+
default: "./lib/vitest/index.mjs",
|
|
40
|
+
});
|
|
25
41
|
expect(packageJson.exports["./discovery"]).toEqual({
|
|
26
42
|
types: "./lib/discovery/index.d.ts",
|
|
27
43
|
default: "./lib/discovery/index.mjs",
|
|
@@ -31,8 +47,12 @@ describe("package metadata", () => {
|
|
|
31
47
|
default: "./lib/known-failures/index.mjs",
|
|
32
48
|
});
|
|
33
49
|
expect(fs.existsSync(path.join(rootDir, "lib", "index.d.ts"))).toBe(true);
|
|
34
|
-
expect(fs.existsSync(path.join(rootDir, "lib", "
|
|
50
|
+
expect(fs.existsSync(path.join(rootDir, "lib", "config-api", "index.d.ts"))).toBe(true);
|
|
51
|
+
expect(fs.existsSync(path.join(rootDir, "lib", "drizzle", "index.d.ts"))).toBe(true);
|
|
52
|
+
expect(fs.existsSync(path.join(rootDir, "lib", "env", "index.d.ts"))).toBe(true);
|
|
53
|
+
expect(fs.existsSync(path.join(rootDir, "lib", "playwright", "index.d.ts"))).toBe(true);
|
|
35
54
|
expect(fs.existsSync(path.join(rootDir, "lib", "runtime", "index.d.ts"))).toBe(true);
|
|
55
|
+
expect(fs.existsSync(path.join(rootDir, "lib", "vitest", "index.d.ts"))).toBe(true);
|
|
36
56
|
expect(fs.existsSync(path.join(rootDir, "lib", "discovery", "index.d.ts"))).toBe(true);
|
|
37
57
|
expect(fs.existsSync(path.join(rootDir, "lib", "known-failures", "index.d.ts"))).toBe(true);
|
|
38
58
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface PlaywrightConfigOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
defaultFileTimeoutSeconds?: number;
|
|
4
|
+
dotenvFiles?: string[];
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
onlyWhenAllowed?: boolean;
|
|
7
|
+
override?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function defineConfig<T extends Record<string, unknown>>(
|
|
11
|
+
config: T,
|
|
12
|
+
options?: PlaywrightConfigOptions
|
|
13
|
+
): T & {
|
|
14
|
+
timeout: number;
|
|
15
|
+
expect: Record<string, unknown> & { timeout: number };
|
|
16
|
+
use: Record<string, unknown> & {
|
|
17
|
+
actionTimeout: number;
|
|
18
|
+
navigationTimeout: number;
|
|
19
|
+
};
|
|
20
|
+
webServer?: unknown;
|
|
21
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_FILE_TIMEOUT_SECONDS,
|
|
3
|
+
INTERNAL_FILE_TIMEOUT_SECONDS_ENV,
|
|
4
|
+
} from "../shared/file-timeout.mjs";
|
|
5
|
+
import { loadDotenvFiles } from "../env/index.mjs";
|
|
6
|
+
|
|
7
|
+
export function defineConfig(config = {}, options = {}) {
|
|
8
|
+
const env = options.env || process.env;
|
|
9
|
+
loadDotenvFiles({
|
|
10
|
+
cwd: options.cwd,
|
|
11
|
+
env,
|
|
12
|
+
files: options.dotenvFiles || [".env.local"],
|
|
13
|
+
onlyWhenAllowed: options.onlyWhenAllowed,
|
|
14
|
+
override: options.override,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const timeoutSeconds = readTimeoutSeconds(env, options.defaultFileTimeoutSeconds);
|
|
18
|
+
const timeoutMs = timeoutSeconds * 1000;
|
|
19
|
+
const managed = env?.TESTKIT_MANAGED_SERVERS === "1";
|
|
20
|
+
const baseURL = env.BASE_URL || config.use?.baseURL;
|
|
21
|
+
const use = {
|
|
22
|
+
...(config.use || {}),
|
|
23
|
+
...(baseURL ? { baseURL } : {}),
|
|
24
|
+
actionTimeout: timeoutMs,
|
|
25
|
+
navigationTimeout: timeoutMs,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...config,
|
|
30
|
+
timeout: timeoutMs,
|
|
31
|
+
expect: {
|
|
32
|
+
...(config.expect || {}),
|
|
33
|
+
timeout: timeoutMs,
|
|
34
|
+
},
|
|
35
|
+
use,
|
|
36
|
+
...(managed ? { webServer: undefined } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readTimeoutSeconds(env, fallbackSeconds) {
|
|
41
|
+
const rawValue = env?.[INTERNAL_FILE_TIMEOUT_SECONDS_ENV];
|
|
42
|
+
const normalizedFallback = normalizeTimeoutSeconds(fallbackSeconds ?? DEFAULT_FILE_TIMEOUT_SECONDS);
|
|
43
|
+
if (rawValue == null) return normalizedFallback;
|
|
44
|
+
return normalizeTimeoutSeconds(rawValue);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeTimeoutSeconds(value) {
|
|
48
|
+
const normalized = Number(value);
|
|
49
|
+
if (!Number.isInteger(normalized) || normalized <= 0) {
|
|
50
|
+
throw new Error("Playwright file timeout seconds must be a positive integer.");
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defineConfig } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
describe("testkit playwright defineConfig", () => {
|
|
5
|
+
it("derives timeouts from the managed file-timeout env", () => {
|
|
6
|
+
const config = defineConfig(
|
|
7
|
+
{
|
|
8
|
+
use: {
|
|
9
|
+
trace: "on-first-retry",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
env: {
|
|
14
|
+
TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS: "45",
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
expect(config.timeout).toBe(45_000);
|
|
20
|
+
expect(config.expect.timeout).toBe(45_000);
|
|
21
|
+
expect(config.use.actionTimeout).toBe(45_000);
|
|
22
|
+
expect(config.use.trace).toBe("on-first-retry");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("disables webServer during managed runs and respects BASE_URL", () => {
|
|
26
|
+
const config = defineConfig(
|
|
27
|
+
{
|
|
28
|
+
webServer: [{ command: "npm run dev" }],
|
|
29
|
+
use: {},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
env: {
|
|
33
|
+
TESTKIT_ACTIVE: "1",
|
|
34
|
+
TESTKIT_MANAGED_SERVERS: "1",
|
|
35
|
+
BASE_URL: "http://127.0.0.1:3000",
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(config.webServer).toBeUndefined();
|
|
41
|
+
expect(config.use.baseURL).toBe("http://127.0.0.1:3000");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
import fs from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import { build } from "esbuild";
|
|
5
6
|
import { execa, execaCommand } from "execa";
|
|
6
7
|
import { fileURLToPath } from "url";
|
|
7
8
|
import {
|
|
8
9
|
collectConfiguredInputs,
|
|
10
|
+
isBareModuleSpecifier,
|
|
9
11
|
parseModuleSpecifier,
|
|
10
12
|
resolveConfiguredCwd,
|
|
11
13
|
resolveConfiguredPath,
|
|
@@ -20,11 +22,11 @@ import {
|
|
|
20
22
|
|
|
21
23
|
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
22
24
|
const ROOT_ENTRY = path.join(PACKAGE_ROOT, "lib", "index.mjs");
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
+
const CONFIG_ENTRY = path.join(PACKAGE_ROOT, "lib", "config-api", "index.mjs");
|
|
26
|
+
const CONFIG_NEXT_TSCONFIG_ENTRY = path.join(
|
|
25
27
|
PACKAGE_ROOT,
|
|
26
28
|
"lib",
|
|
27
|
-
"
|
|
29
|
+
"config-api",
|
|
28
30
|
"next-runtime-tsconfig.mjs"
|
|
29
31
|
);
|
|
30
32
|
const RUNTIME_ENTRY = path.join(PACKAGE_ROOT, "lib", "runtime", "index.mjs");
|
|
@@ -181,7 +183,7 @@ async function runConfiguredStep(config, step, env, resolvedToolchain, options =
|
|
|
181
183
|
|
|
182
184
|
async function bundleConfiguredModule(productDir, step) {
|
|
183
185
|
const { modulePath } = parseModuleSpecifier(step.specifier);
|
|
184
|
-
const absoluteModulePath =
|
|
186
|
+
const absoluteModulePath = resolveConfiguredModuleEntry(productDir, step);
|
|
185
187
|
const bundleDir = path.join(productDir, ".testkit", "_template-steps");
|
|
186
188
|
fs.mkdirSync(bundleDir, { recursive: true });
|
|
187
189
|
|
|
@@ -210,6 +212,24 @@ async function bundleConfiguredModule(productDir, step) {
|
|
|
210
212
|
};
|
|
211
213
|
}
|
|
212
214
|
|
|
215
|
+
function resolveConfiguredModuleEntry(productDir, step) {
|
|
216
|
+
const { modulePath } = parseModuleSpecifier(step.specifier);
|
|
217
|
+
if (modulePath.startsWith("@elench/testkit")) {
|
|
218
|
+
return resolvePackageSubpath(modulePath);
|
|
219
|
+
}
|
|
220
|
+
const candidatePath = resolveConfiguredPath(productDir, step.cwd, modulePath);
|
|
221
|
+
if (fs.existsSync(candidatePath)) {
|
|
222
|
+
return candidatePath;
|
|
223
|
+
}
|
|
224
|
+
if (!isBareModuleSpecifier(modulePath)) {
|
|
225
|
+
return candidatePath;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const cwd = resolveConfiguredCwd(productDir, step.cwd);
|
|
229
|
+
const requireFromCwd = createRequire(path.join(cwd, "__testkit__.cjs"));
|
|
230
|
+
return requireFromCwd.resolve(modulePath);
|
|
231
|
+
}
|
|
232
|
+
|
|
213
233
|
function buildModuleCacheKey(modulePath) {
|
|
214
234
|
const content = fs.readFileSync(modulePath, "utf8");
|
|
215
235
|
return crypto.createHash("sha256").update(modulePath).update("\0").update(content).digest("hex");
|
|
@@ -230,8 +250,8 @@ function testkitAliasPlugin() {
|
|
|
230
250
|
function resolvePackageSubpath(specifier) {
|
|
231
251
|
const subpath = specifier.slice("@elench/testkit".length);
|
|
232
252
|
if (!subpath) return ROOT_ENTRY;
|
|
233
|
-
if (subpath === "/
|
|
234
|
-
if (subpath === "/
|
|
253
|
+
if (subpath === "/config") return CONFIG_ENTRY;
|
|
254
|
+
if (subpath === "/config/next-runtime-tsconfig") return CONFIG_NEXT_TSCONFIG_ENTRY;
|
|
235
255
|
if (subpath === "/runtime") return RUNTIME_ENTRY;
|
|
236
256
|
if (subpath === "/known-failures") return KNOWN_FAILURES_ENTRY;
|
|
237
257
|
|
package/lib/runner/template.mjs
CHANGED
|
@@ -85,14 +85,14 @@ export function buildPortMap(runtimeConfigs, runtimeId, namespace = {}) {
|
|
|
85
85
|
if (actualPort > 65_535) {
|
|
86
86
|
throw new Error(
|
|
87
87
|
`Runtime port resolution exceeded 65535 for service "${config.name}" (${actualPort}). ` +
|
|
88
|
-
`Reduce runtime instances or choose lower local.port/baseUrl ports in testkit.
|
|
88
|
+
`Reduce runtime instances or choose lower local.port/baseUrl ports in testkit.config.ts.`
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
const existing = seen.get(actualPort);
|
|
92
92
|
if (existing) {
|
|
93
93
|
throw new Error(
|
|
94
94
|
`Runtime port collision: services "${existing}" and "${config.name}" both resolve to ${actualPort}. ` +
|
|
95
|
-
`Assign distinct local.port/baseUrl ports in testkit.
|
|
95
|
+
`Assign distinct local.port/baseUrl ports in testkit.config.ts.`
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
seen.set(actualPort, config.name);
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
clearRuntimeContext,
|
|
17
17
|
registerRuntimeContext,
|
|
18
18
|
resolveHttpProfile,
|
|
19
|
-
} from "../../
|
|
19
|
+
} from "../../config-api/runtime.mjs";
|
|
20
20
|
import { createScenarioRuntime } from "./scenario-runtime.js";
|
|
21
21
|
|
|
22
22
|
export function defineScenarioSuite(configOrRun, maybeRun) {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
clearRuntimeContext,
|
|
17
17
|
registerRuntimeContext,
|
|
18
18
|
resolveHttpProfile,
|
|
19
|
-
} from "../../
|
|
19
|
+
} from "../../config-api/runtime.mjs";
|
|
20
20
|
|
|
21
21
|
export function defineHttpSuite(configOrRun, maybeRun) {
|
|
22
22
|
const { config, run } = normalizeSuiteArgs(configOrRun, maybeRun);
|
|
@@ -117,7 +117,7 @@ export function buildConfigToPrepare(build) {
|
|
|
117
117
|
kind: "command",
|
|
118
118
|
cmd: build.script,
|
|
119
119
|
cwd: build.cwd || undefined,
|
|
120
|
-
inputs: [
|
|
120
|
+
inputs: [],
|
|
121
121
|
},
|
|
122
122
|
],
|
|
123
123
|
};
|
|
@@ -130,15 +130,15 @@ export function buildConfigToPrepare(build) {
|
|
|
130
130
|
steps: [
|
|
131
131
|
{
|
|
132
132
|
kind: "module",
|
|
133
|
-
specifier: "@elench/testkit/
|
|
133
|
+
specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
|
|
134
134
|
cwd: build.cwd || undefined,
|
|
135
|
-
inputs,
|
|
135
|
+
inputs: [],
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
kind: "command",
|
|
139
139
|
cmd: "./node_modules/.bin/next build",
|
|
140
140
|
cwd: build.cwd || undefined,
|
|
141
|
-
inputs,
|
|
141
|
+
inputs: [],
|
|
142
142
|
},
|
|
143
143
|
],
|
|
144
144
|
};
|
|
@@ -149,15 +149,15 @@ export function buildConfigToPrepare(build) {
|
|
|
149
149
|
return {
|
|
150
150
|
inputs,
|
|
151
151
|
steps: [
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
152
|
+
{
|
|
153
|
+
kind: "command",
|
|
154
|
+
cmd,
|
|
155
|
+
cwd: build.cwd || undefined,
|
|
156
|
+
inputs: [],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
161
|
|
|
162
162
|
export function compiledEntryFromBuild(build) {
|
|
163
163
|
if (!build || build.kind !== "tsc") return null;
|
|
@@ -32,7 +32,7 @@ describe("shared build config helpers", () => {
|
|
|
32
32
|
kind: "command",
|
|
33
33
|
cmd: './node_modules/.bin/tsc -p tsconfig.json --outDir "{prepareDir}/dist"',
|
|
34
34
|
cwd: undefined,
|
|
35
|
-
inputs: [
|
|
35
|
+
inputs: [],
|
|
36
36
|
},
|
|
37
37
|
],
|
|
38
38
|
});
|
|
@@ -116,33 +116,15 @@ describe("shared build config helpers", () => {
|
|
|
116
116
|
steps: [
|
|
117
117
|
{
|
|
118
118
|
kind: "module",
|
|
119
|
-
specifier: "@elench/testkit/
|
|
119
|
+
specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
|
|
120
120
|
cwd: "frontend",
|
|
121
|
-
inputs: [
|
|
122
|
-
"frontend/tsconfig.json",
|
|
123
|
-
"frontend/package.json",
|
|
124
|
-
"frontend/src",
|
|
125
|
-
"frontend/app",
|
|
126
|
-
"frontend/public",
|
|
127
|
-
"frontend/next.config.js",
|
|
128
|
-
"frontend/next.config.mjs",
|
|
129
|
-
"frontend/next.config.ts",
|
|
130
|
-
],
|
|
121
|
+
inputs: [],
|
|
131
122
|
},
|
|
132
123
|
{
|
|
133
124
|
kind: "command",
|
|
134
125
|
cmd: "./node_modules/.bin/next build",
|
|
135
126
|
cwd: "frontend",
|
|
136
|
-
inputs: [
|
|
137
|
-
"frontend/tsconfig.json",
|
|
138
|
-
"frontend/package.json",
|
|
139
|
-
"frontend/src",
|
|
140
|
-
"frontend/app",
|
|
141
|
-
"frontend/public",
|
|
142
|
-
"frontend/next.config.js",
|
|
143
|
-
"frontend/next.config.mjs",
|
|
144
|
-
"frontend/next.config.ts",
|
|
145
|
-
],
|
|
127
|
+
inputs: [],
|
|
146
128
|
},
|
|
147
129
|
],
|
|
148
130
|
});
|
|
@@ -94,6 +94,12 @@ export function parseModuleSpecifier(specifier) {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
export function isBareModuleSpecifier(modulePath) {
|
|
98
|
+
if (typeof modulePath !== "string" || modulePath.length === 0) return false;
|
|
99
|
+
if (modulePath.startsWith("./") || modulePath.startsWith("../")) return false;
|
|
100
|
+
return !path.isAbsolute(modulePath);
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
export function resolveConfiguredCwd(productDir, stepCwd) {
|
|
98
104
|
return path.resolve(productDir, stepCwd || ".");
|
|
99
105
|
}
|
|
@@ -112,7 +118,15 @@ export function collectConfiguredInputs(productDir, { inputs = [], steps = [] }
|
|
|
112
118
|
collected.add(resolveConfiguredPath(productDir, step.cwd, step.path));
|
|
113
119
|
}
|
|
114
120
|
if (step.kind === "module") {
|
|
115
|
-
|
|
121
|
+
const { modulePath } = parseModuleSpecifier(step.specifier);
|
|
122
|
+
const candidatePath = resolveConfiguredPath(productDir, step.cwd, modulePath);
|
|
123
|
+
if (modulePath.startsWith("@elench/testkit") || fs.existsSync(candidatePath)) {
|
|
124
|
+
if (!modulePath.startsWith("@elench/testkit")) {
|
|
125
|
+
collected.add(candidatePath);
|
|
126
|
+
}
|
|
127
|
+
} else if (!isBareModuleSpecifier(modulePath)) {
|
|
128
|
+
collected.add(candidatePath);
|
|
129
|
+
}
|
|
116
130
|
}
|
|
117
131
|
for (const input of step.inputs || []) {
|
|
118
132
|
collected.add(resolveConfiguredPath(productDir, step.cwd, input));
|
|
@@ -160,10 +174,15 @@ export function validateConfiguredCollection({ productDir, label, inputs = [], s
|
|
|
160
174
|
ensureExistingPath(resolveConfiguredPath(productDir, step.cwd, step.path), `${label} sql file`);
|
|
161
175
|
}
|
|
162
176
|
if (step.kind === "module") {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
177
|
+
const { modulePath } = parseModuleSpecifier(step.specifier);
|
|
178
|
+
const candidatePath = resolveConfiguredPath(productDir, step.cwd, modulePath);
|
|
179
|
+
if (modulePath.startsWith("@elench/testkit")) {
|
|
180
|
+
// Internal testkit module steps are resolved by the runtime alias map.
|
|
181
|
+
} else if (fs.existsSync(candidatePath)) {
|
|
182
|
+
ensureExistingPath(candidatePath, `${label} module`);
|
|
183
|
+
} else if (!isBareModuleSpecifier(modulePath)) {
|
|
184
|
+
ensureExistingPath(candidatePath, `${label} module`);
|
|
185
|
+
}
|
|
167
186
|
}
|
|
168
187
|
for (const input of step.inputs || []) {
|
|
169
188
|
ensureExistingPath(resolveConfiguredPath(productDir, step.cwd, input), `${label} step input`);
|
|
@@ -5,6 +5,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
|
|
5
5
|
import {
|
|
6
6
|
collectConfiguredInputs,
|
|
7
7
|
finalizeConfiguredStep,
|
|
8
|
+
isBareModuleSpecifier,
|
|
8
9
|
normalizeConfiguredStep,
|
|
9
10
|
parseModuleSpecifier,
|
|
10
11
|
validateConfiguredCollection,
|
|
@@ -70,4 +71,32 @@ describe("shared configured steps", () => {
|
|
|
70
71
|
})
|
|
71
72
|
).not.toThrow();
|
|
72
73
|
});
|
|
74
|
+
|
|
75
|
+
it("treats bare module specifiers as modules, not product-relative file paths", () => {
|
|
76
|
+
const productDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-configured-steps-"));
|
|
77
|
+
tempDirs.push(productDir);
|
|
78
|
+
fs.mkdirSync(path.join(productDir, "frontend"), { recursive: true });
|
|
79
|
+
|
|
80
|
+
const collection = {
|
|
81
|
+
inputs: [],
|
|
82
|
+
steps: [
|
|
83
|
+
{
|
|
84
|
+
kind: "module",
|
|
85
|
+
specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
|
|
86
|
+
cwd: "frontend",
|
|
87
|
+
inputs: [],
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
expect(isBareModuleSpecifier("@elench/testkit/config/next-runtime-tsconfig")).toBe(true);
|
|
93
|
+
expect(collectConfiguredInputs(productDir, collection)).toEqual([]);
|
|
94
|
+
expect(() =>
|
|
95
|
+
validateConfiguredCollection({
|
|
96
|
+
productDir,
|
|
97
|
+
label: 'Service "frontend" runtime.prepare',
|
|
98
|
+
...collection,
|
|
99
|
+
})
|
|
100
|
+
).not.toThrow();
|
|
101
|
+
});
|
|
73
102
|
});
|
package/lib/toolchains/index.mjs
CHANGED
|
@@ -29,12 +29,12 @@ export function nodeToolchain(options = {}) {
|
|
|
29
29
|
export function normalizeToolchainRegistry(value) {
|
|
30
30
|
if (value == null) return {};
|
|
31
31
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
32
|
-
throw new Error("testkit.
|
|
32
|
+
throw new Error("testkit.config.ts toolchains must be an object");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const normalized = {};
|
|
36
36
|
for (const [name, config] of Object.entries(value)) {
|
|
37
|
-
normalized[name] = normalizeToolchainConfig(config, `testkit.
|
|
37
|
+
normalized[name] = normalizeToolchainConfig(config, `testkit.config.ts toolchains.${name}`);
|
|
38
38
|
}
|
|
39
39
|
return normalized;
|
|
40
40
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface VitestConfigOptions {
|
|
2
|
+
exclude?: string[];
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export declare function defineConfig<T extends Record<string, unknown>>(
|
|
6
|
+
config: T,
|
|
7
|
+
options?: VitestConfigOptions
|
|
8
|
+
): T & {
|
|
9
|
+
test: Record<string, unknown> & { exclude: string[] };
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export declare function testkitExcludes(): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const TESTKIT_SUITE_GLOBS = [
|
|
2
|
+
"**/*.int.testkit.ts",
|
|
3
|
+
"**/*.e2e.testkit.ts",
|
|
4
|
+
"**/*.scenario.testkit.ts",
|
|
5
|
+
"**/*.dal.testkit.ts",
|
|
6
|
+
"**/*.load.testkit.ts",
|
|
7
|
+
"**/*.pw.testkit.ts",
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export function defineConfig(config = {}, options = {}) {
|
|
11
|
+
const test = {
|
|
12
|
+
...(config.test || {}),
|
|
13
|
+
};
|
|
14
|
+
const exclude = new Set([
|
|
15
|
+
...(Array.isArray(test.exclude) ? test.exclude : []),
|
|
16
|
+
...(Array.isArray(options.exclude) ? options.exclude : []),
|
|
17
|
+
...TESTKIT_SUITE_GLOBS,
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
...config,
|
|
22
|
+
test: {
|
|
23
|
+
...test,
|
|
24
|
+
exclude: [...exclude],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function testkitExcludes() {
|
|
30
|
+
return [...TESTKIT_SUITE_GLOBS];
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defineConfig, testkitExcludes } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
describe("testkit vitest defineConfig", () => {
|
|
5
|
+
it("appends testkit suite excludes", () => {
|
|
6
|
+
const config = defineConfig({
|
|
7
|
+
test: {
|
|
8
|
+
exclude: ["dist"],
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(config.test.exclude).toEqual(
|
|
13
|
+
expect.arrayContaining(["dist", "**/*.int.testkit.ts", "**/*.pw.testkit.ts"])
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("exports the default testkit excludes", () => {
|
|
18
|
+
expect(testkitExcludes()).toContain("**/*.scenario.testkit.ts");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.71",
|
|
4
4
|
"description": "Browser bridge helpers for testkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@elench/testkit-protocol": "0.1.
|
|
25
|
+
"@elench/testkit-protocol": "0.1.71"
|
|
26
26
|
},
|
|
27
27
|
"private": false
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.71",
|
|
4
4
|
"description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -12,9 +12,25 @@
|
|
|
12
12
|
"types": "./lib/index.d.ts",
|
|
13
13
|
"default": "./lib/index.mjs"
|
|
14
14
|
},
|
|
15
|
-
"./
|
|
16
|
-
"types": "./lib/
|
|
17
|
-
"default": "./lib/
|
|
15
|
+
"./config": {
|
|
16
|
+
"types": "./lib/config-api/index.d.ts",
|
|
17
|
+
"default": "./lib/config-api/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./drizzle": {
|
|
20
|
+
"types": "./lib/drizzle/index.d.ts",
|
|
21
|
+
"default": "./lib/drizzle/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"./env": {
|
|
24
|
+
"types": "./lib/env/index.d.ts",
|
|
25
|
+
"default": "./lib/env/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./playwright": {
|
|
28
|
+
"types": "./lib/playwright/index.d.ts",
|
|
29
|
+
"default": "./lib/playwright/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./vitest": {
|
|
32
|
+
"types": "./lib/vitest/index.d.ts",
|
|
33
|
+
"default": "./lib/vitest/index.mjs"
|
|
18
34
|
},
|
|
19
35
|
"./runtime": {
|
|
20
36
|
"types": "./lib/runtime/index.d.ts",
|
|
@@ -64,10 +80,10 @@
|
|
|
64
80
|
"vitest": "^3.2.4"
|
|
65
81
|
},
|
|
66
82
|
"dependencies": {
|
|
67
|
-
"@elench/next-analysis": "0.1.
|
|
68
|
-
"@elench/ts-analysis": "0.1.
|
|
69
|
-
"@elench/testkit-bridge": "0.1.
|
|
70
|
-
"@elench/testkit-protocol": "0.1.
|
|
83
|
+
"@elench/next-analysis": "0.1.71",
|
|
84
|
+
"@elench/ts-analysis": "0.1.71",
|
|
85
|
+
"@elench/testkit-bridge": "0.1.71",
|
|
86
|
+
"@elench/testkit-protocol": "0.1.71",
|
|
71
87
|
"@babel/code-frame": "^7.29.0",
|
|
72
88
|
"@oclif/core": "^4.10.6",
|
|
73
89
|
"esbuild": "^0.25.11",
|
|
File without changes
|
|
File without changes
|