@elench/testkit 0.1.69 → 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.
Files changed (53) hide show
  1. package/README.md +70 -10
  2. package/lib/app/doctor.mjs +1 -1
  3. package/lib/app/typecheck.mjs +8 -4
  4. package/lib/bundler/index.mjs +17 -17
  5. package/lib/cli/command-helpers.mjs +1 -1
  6. package/lib/cli/commands/doctor.mjs +1 -1
  7. package/lib/cli/commands/typecheck.mjs +1 -1
  8. package/lib/cli/known-failures.mjs +4 -4
  9. package/lib/cli/presentation/discovery-reporter.mjs +2 -2
  10. package/lib/config/{setup-loader.mjs → config-loader.mjs} +32 -32
  11. package/lib/config/index.mjs +16 -16
  12. package/lib/config/runtime.mjs +1 -1
  13. package/lib/config/telemetry.mjs +4 -4
  14. package/lib/config/validation.mjs +1 -1
  15. package/lib/{setup → config-api}/index.d.ts +7 -7
  16. package/lib/{setup → config-api}/index.mjs +10 -10
  17. package/lib/{setup → config-api}/index.test.mjs +16 -3
  18. package/lib/{setup → config-api}/runtime.mjs +10 -10
  19. package/lib/coverage/index.test.mjs +3 -3
  20. package/lib/discovery/file-metadata.mjs +1 -1
  21. package/lib/discovery/file-metadata.test.mjs +2 -2
  22. package/lib/discovery/index.d.ts +1 -1
  23. package/lib/discovery/index.mjs +28 -28
  24. package/lib/discovery/index.test.mjs +10 -10
  25. package/lib/drizzle/index.d.ts +14 -0
  26. package/lib/drizzle/index.mjs +15 -0
  27. package/lib/drizzle/index.test.mjs +33 -0
  28. package/lib/env/index.d.ts +17 -0
  29. package/lib/env/index.mjs +65 -0
  30. package/lib/env/index.test.mjs +82 -0
  31. package/lib/known-failures/github.mjs +4 -4
  32. package/lib/package.test.mjs +24 -4
  33. package/lib/playwright/index.d.ts +21 -0
  34. package/lib/playwright/index.mjs +53 -0
  35. package/lib/playwright/index.test.mjs +43 -0
  36. package/lib/runner/template-steps.mjs +5 -5
  37. package/lib/runner/template.mjs +2 -2
  38. package/lib/runtime-src/k6/scenario-suite.js +1 -1
  39. package/lib/runtime-src/k6/suite.js +1 -1
  40. package/lib/shared/build-config.mjs +1 -1
  41. package/lib/shared/build-config.test.mjs +1 -1
  42. package/lib/shared/configured-steps.test.mjs +2 -2
  43. package/lib/toolchains/index.mjs +2 -2
  44. package/lib/vitest/index.d.ts +12 -0
  45. package/lib/vitest/index.mjs +31 -0
  46. package/lib/vitest/index.test.mjs +20 -0
  47. package/node_modules/@elench/next-analysis/package.json +1 -1
  48. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  49. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  50. package/node_modules/@elench/ts-analysis/package.json +1 -1
  51. package/package.json +24 -8
  52. /package/lib/{setup → config-api}/next-runtime-tsconfig.mjs +0 -0
  53. /package/lib/{setup → config-api}/next-runtime-tsconfig.test.mjs +0 -0
@@ -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["./setup"]).toEqual({
18
- types: "./lib/setup/index.d.ts",
19
- default: "./lib/setup/index.mjs",
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", "setup", "index.d.ts"))).toBe(true);
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
+ });
@@ -22,11 +22,11 @@ import {
22
22
 
23
23
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
24
24
  const ROOT_ENTRY = path.join(PACKAGE_ROOT, "lib", "index.mjs");
25
- const SETUP_ENTRY = path.join(PACKAGE_ROOT, "lib", "setup", "index.mjs");
26
- const SETUP_NEXT_TSCONFIG_ENTRY = path.join(
25
+ const CONFIG_ENTRY = path.join(PACKAGE_ROOT, "lib", "config-api", "index.mjs");
26
+ const CONFIG_NEXT_TSCONFIG_ENTRY = path.join(
27
27
  PACKAGE_ROOT,
28
28
  "lib",
29
- "setup",
29
+ "config-api",
30
30
  "next-runtime-tsconfig.mjs"
31
31
  );
32
32
  const RUNTIME_ENTRY = path.join(PACKAGE_ROOT, "lib", "runtime", "index.mjs");
@@ -250,8 +250,8 @@ function testkitAliasPlugin() {
250
250
  function resolvePackageSubpath(specifier) {
251
251
  const subpath = specifier.slice("@elench/testkit".length);
252
252
  if (!subpath) return ROOT_ENTRY;
253
- if (subpath === "/setup") return SETUP_ENTRY;
254
- if (subpath === "/setup/next-runtime-tsconfig") return SETUP_NEXT_TSCONFIG_ENTRY;
253
+ if (subpath === "/config") return CONFIG_ENTRY;
254
+ if (subpath === "/config/next-runtime-tsconfig") return CONFIG_NEXT_TSCONFIG_ENTRY;
255
255
  if (subpath === "/runtime") return RUNTIME_ENTRY;
256
256
  if (subpath === "/known-failures") return KNOWN_FAILURES_ENTRY;
257
257
 
@@ -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.setup.ts.`
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.setup.ts.`
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 "../../setup/runtime.mjs";
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 "../../setup/runtime.mjs";
19
+ } from "../../config-api/runtime.mjs";
20
20
 
21
21
  export function defineHttpSuite(configOrRun, maybeRun) {
22
22
  const { config, run } = normalizeSuiteArgs(configOrRun, maybeRun);
@@ -130,7 +130,7 @@ export function buildConfigToPrepare(build) {
130
130
  steps: [
131
131
  {
132
132
  kind: "module",
133
- specifier: "@elench/testkit/setup/next-runtime-tsconfig#writeNextRuntimeTsconfig",
133
+ specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
134
134
  cwd: build.cwd || undefined,
135
135
  inputs: [],
136
136
  },
@@ -116,7 +116,7 @@ describe("shared build config helpers", () => {
116
116
  steps: [
117
117
  {
118
118
  kind: "module",
119
- specifier: "@elench/testkit/setup/next-runtime-tsconfig#writeNextRuntimeTsconfig",
119
+ specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
120
120
  cwd: "frontend",
121
121
  inputs: [],
122
122
  },
@@ -82,14 +82,14 @@ describe("shared configured steps", () => {
82
82
  steps: [
83
83
  {
84
84
  kind: "module",
85
- specifier: "@elench/testkit/setup/next-runtime-tsconfig#writeNextRuntimeTsconfig",
85
+ specifier: "@elench/testkit/config/next-runtime-tsconfig#writeNextRuntimeTsconfig",
86
86
  cwd: "frontend",
87
87
  inputs: [],
88
88
  },
89
89
  ],
90
90
  };
91
91
 
92
- expect(isBareModuleSpecifier("@elench/testkit/setup/next-runtime-tsconfig")).toBe(true);
92
+ expect(isBareModuleSpecifier("@elench/testkit/config/next-runtime-tsconfig")).toBe(true);
93
93
  expect(collectConfiguredInputs(productDir, collection)).toEqual([]);
94
94
  expect(() =>
95
95
  validateConfiguredCollection({
@@ -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.setup.ts toolchains must be an object");
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.setup.ts toolchains.${name}`);
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/next-analysis",
3
- "version": "0.1.69",
3
+ "version": "0.1.71",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.69",
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.69"
25
+ "@elench/testkit-protocol": "0.1.71"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.69",
3
+ "version": "0.1.71",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.69",
3
+ "version": "0.1.71",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.69",
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
- "./setup": {
16
- "types": "./lib/setup/index.d.ts",
17
- "default": "./lib/setup/index.mjs"
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.69",
68
- "@elench/ts-analysis": "0.1.69",
69
- "@elench/testkit-bridge": "0.1.69",
70
- "@elench/testkit-protocol": "0.1.69",
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",