@elench/testkit 0.1.127 → 0.1.129
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/lib/config-api/build-assets.mjs +80 -0
- package/lib/runner/template-steps.mjs +7 -0
- package/lib/shared/build-config.mjs +19 -0
- package/lib/ui/provisioning.mjs +25 -2
- package/lib/ui/sandbox.mjs +22 -1
- 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 +5 -5
- package/node_modules/es-toolkit/CHANGELOG.md +0 -801
- package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +0 -1
- package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +0 -3
- package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +0 -4
- package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +0 -4
- package/node_modules/esprima/ChangeLog +0 -235
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_ASSET_EXTENSIONS = new Set([
|
|
5
|
+
".csv",
|
|
6
|
+
".css",
|
|
7
|
+
".gql",
|
|
8
|
+
".graphql",
|
|
9
|
+
".html",
|
|
10
|
+
".json",
|
|
11
|
+
".md",
|
|
12
|
+
".sql",
|
|
13
|
+
".txt",
|
|
14
|
+
".wasm",
|
|
15
|
+
".yaml",
|
|
16
|
+
".yml",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
export async function copyTscRuntimeAssets(context) {
|
|
20
|
+
const sourceDir = normalizeRelativePath(context.args?.sourceDir || "src", "sourceDir");
|
|
21
|
+
const outDir = normalizeRelativePath(context.args?.outDir || "dist", "outDir");
|
|
22
|
+
const extensions = normalizeExtensions(context.args?.extensions);
|
|
23
|
+
const sourceRoot = path.resolve(context.cwd, sourceDir);
|
|
24
|
+
const outputRoot = path.resolve(context.prepareDir, outDir);
|
|
25
|
+
|
|
26
|
+
if (!context.prepareDir) {
|
|
27
|
+
throw new Error("copyTscRuntimeAssets requires a runtime prepare directory");
|
|
28
|
+
}
|
|
29
|
+
if (!fs.existsSync(sourceRoot)) return;
|
|
30
|
+
|
|
31
|
+
for (const filePath of walkFiles(sourceRoot)) {
|
|
32
|
+
const relative = path.relative(sourceRoot, filePath);
|
|
33
|
+
if (shouldSkipAsset(relative, extensions)) continue;
|
|
34
|
+
const destination = path.join(outputRoot, relative);
|
|
35
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
36
|
+
fs.copyFileSync(filePath, destination);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeRelativePath(value, label) {
|
|
41
|
+
const normalized = String(value || "").trim();
|
|
42
|
+
if (!normalized) throw new Error(`${label} must be a non-empty relative path`);
|
|
43
|
+
if (path.isAbsolute(normalized) || normalized.split(path.sep).includes("..")) {
|
|
44
|
+
throw new Error(`${label} must be a relative path inside the service cwd`);
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeExtensions(value) {
|
|
50
|
+
if (value == null) return DEFAULT_ASSET_EXTENSIONS;
|
|
51
|
+
if (!Array.isArray(value)) throw new Error("extensions must be an array");
|
|
52
|
+
return new Set(
|
|
53
|
+
value.map((entry, index) => {
|
|
54
|
+
const normalized = String(entry || "").trim().toLowerCase();
|
|
55
|
+
if (!/^\.[a-z0-9]+$/i.test(normalized)) {
|
|
56
|
+
throw new Error(`extensions[${index}] must look like ".json"`);
|
|
57
|
+
}
|
|
58
|
+
return normalized;
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function shouldSkipAsset(relativePath, extensions) {
|
|
64
|
+
const normalized = relativePath.split(path.sep).join("/");
|
|
65
|
+
if (normalized.includes("/__testkit__/") || normalized.startsWith("__testkit__/")) return true;
|
|
66
|
+
if (/(^|\/)[^/]+\.test\.[^/]+$/i.test(normalized)) return true;
|
|
67
|
+
if (/(^|\/)[^/]+\.spec\.[^/]+$/i.test(normalized)) return true;
|
|
68
|
+
return !extensions.has(path.extname(normalized).toLowerCase());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function* walkFiles(root) {
|
|
72
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
73
|
+
const entryPath = path.join(root, entry.name);
|
|
74
|
+
if (entry.isDirectory()) {
|
|
75
|
+
yield* walkFiles(entryPath);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (entry.isFile()) yield entryPath;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -29,6 +29,12 @@ const CONFIG_DATABASE_STEPS_ENTRY = path.join(
|
|
|
29
29
|
"config-api",
|
|
30
30
|
"database-steps.mjs"
|
|
31
31
|
);
|
|
32
|
+
const CONFIG_BUILD_ASSETS_ENTRY = path.join(
|
|
33
|
+
PACKAGE_ROOT,
|
|
34
|
+
"lib",
|
|
35
|
+
"config-api",
|
|
36
|
+
"build-assets.mjs"
|
|
37
|
+
);
|
|
32
38
|
const CONFIG_NEXT_TSCONFIG_ENTRY = path.join(
|
|
33
39
|
PACKAGE_ROOT,
|
|
34
40
|
"lib",
|
|
@@ -274,6 +280,7 @@ function resolvePackageSubpath(specifier) {
|
|
|
274
280
|
const subpath = specifier.slice("@elench/testkit".length);
|
|
275
281
|
if (!subpath) return ROOT_ENTRY;
|
|
276
282
|
if (subpath === "/config") return CONFIG_ENTRY;
|
|
283
|
+
if (subpath === "/config/build-assets") return CONFIG_BUILD_ASSETS_ENTRY;
|
|
277
284
|
if (subpath === "/config/database-steps") return CONFIG_DATABASE_STEPS_ENTRY;
|
|
278
285
|
if (subpath === "/config/next-runtime-tsconfig") return CONFIG_NEXT_TSCONFIG_ENTRY;
|
|
279
286
|
if (subpath === "/drizzle") return DRIZZLE_ENTRY;
|
|
@@ -146,6 +146,7 @@ export function buildConfigToPrepare(build) {
|
|
|
146
146
|
|
|
147
147
|
const inputs = build.inputs.length > 0 ? [...build.inputs] : defaultTscInputs(build);
|
|
148
148
|
const cmd = `./node_modules/.bin/tsc -p ${build.tsconfig} --outDir "{prepareDir}/${build.outDir}"`;
|
|
149
|
+
const sourceDir = sourceAssetRootFromEntry(build.entry);
|
|
149
150
|
return {
|
|
150
151
|
inputs,
|
|
151
152
|
steps: [
|
|
@@ -155,6 +156,16 @@ export function buildConfigToPrepare(build) {
|
|
|
155
156
|
cwd: build.cwd || undefined,
|
|
156
157
|
inputs: [],
|
|
157
158
|
},
|
|
159
|
+
{
|
|
160
|
+
kind: "module",
|
|
161
|
+
specifier: "@elench/testkit/config/build-assets#copyTscRuntimeAssets",
|
|
162
|
+
cwd: build.cwd || undefined,
|
|
163
|
+
inputs: [],
|
|
164
|
+
args: {
|
|
165
|
+
sourceDir,
|
|
166
|
+
outDir: build.outDir,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
158
169
|
],
|
|
159
170
|
};
|
|
160
171
|
}
|
|
@@ -173,6 +184,14 @@ function sourceEntryToOutputPath(entry) {
|
|
|
173
184
|
return withoutExtension;
|
|
174
185
|
}
|
|
175
186
|
|
|
187
|
+
function sourceAssetRootFromEntry(entry) {
|
|
188
|
+
const normalized = String(entry).split(path.sep).join("/");
|
|
189
|
+
if (normalized.startsWith("src/")) return "src";
|
|
190
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
191
|
+
if (parts.length > 1) return parts[0];
|
|
192
|
+
return ".";
|
|
193
|
+
}
|
|
194
|
+
|
|
176
195
|
function defaultTscInputs(build) {
|
|
177
196
|
const inputs = new Set([
|
|
178
197
|
prefixConfiguredPath(build.cwd, build.tsconfig),
|
package/lib/ui/provisioning.mjs
CHANGED
|
@@ -246,7 +246,11 @@ function renderTemplate(value, context) {
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
function renderSql(statement, context) {
|
|
249
|
-
|
|
249
|
+
const source = String(statement);
|
|
250
|
+
return source.replace(/\{([A-Za-z0-9_.[\]-]+)\}/g, (match, key, offset) => {
|
|
251
|
+
const value = readPath(context, key);
|
|
252
|
+
return isInsideSingleQuotedSqlString(source, offset) ? toSqlStringContent(value) : toSqlLiteral(value);
|
|
253
|
+
});
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
function readPath(source, path) {
|
|
@@ -262,7 +266,26 @@ function toSqlLiteral(value) {
|
|
|
262
266
|
if (Array.isArray(value)) return `array[${value.map((entry) => toSqlLiteral(entry)).join(", ")}]`;
|
|
263
267
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
264
268
|
if (typeof value === "boolean") return value ? "true" : "false";
|
|
265
|
-
return `'${
|
|
269
|
+
return `'${toSqlStringContent(value)}'`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function toSqlStringContent(value) {
|
|
273
|
+
if (value === null || value === undefined) return "";
|
|
274
|
+
if (Array.isArray(value)) return value.map((entry) => String(entry)).join(",");
|
|
275
|
+
return String(value).replaceAll("'", "''");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function isInsideSingleQuotedSqlString(source, offset) {
|
|
279
|
+
let inString = false;
|
|
280
|
+
for (let index = 0; index < offset; index += 1) {
|
|
281
|
+
if (source[index] !== "'") continue;
|
|
282
|
+
if (inString && source[index + 1] === "'") {
|
|
283
|
+
index += 1;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
inString = !inString;
|
|
287
|
+
}
|
|
288
|
+
return inString;
|
|
266
289
|
}
|
|
267
290
|
|
|
268
291
|
function isExpectedStatus(status, expected) {
|
package/lib/ui/sandbox.mjs
CHANGED
|
@@ -53,16 +53,24 @@ export function createSandboxId(options = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function resolveFrontendBaseUrl(options = {}) {
|
|
56
|
+
const config = readUiAuthConfig();
|
|
56
57
|
return resolveBaseUrl(
|
|
57
58
|
options.frontendBaseUrl ||
|
|
58
59
|
options.baseUrl ||
|
|
60
|
+
config.frontendBaseUrl ||
|
|
59
61
|
process.env.TESTKIT_FRONTEND_BASE_URL ||
|
|
60
62
|
process.env.BASE_URL
|
|
61
63
|
);
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export function resolveBackendBaseUrl(options = {}) {
|
|
65
|
-
|
|
67
|
+
const config = readUiAuthConfig();
|
|
68
|
+
return resolveBaseUrl(
|
|
69
|
+
options.backendBaseUrl ||
|
|
70
|
+
config.backendBaseUrl ||
|
|
71
|
+
process.env.TESTKIT_BACKEND_BASE_URL ||
|
|
72
|
+
process.env.API_BASE_URL
|
|
73
|
+
);
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
export function assertSafeUiTarget(url, options = {}) {
|
|
@@ -243,6 +251,19 @@ function resolveBaseUrl(value) {
|
|
|
243
251
|
return assertSafeUiTarget(normalized);
|
|
244
252
|
}
|
|
245
253
|
|
|
254
|
+
function readUiAuthConfig(env = process.env) {
|
|
255
|
+
const raw = env.TESTKIT_UI_CONFIG_JSON;
|
|
256
|
+
if (!raw) return {};
|
|
257
|
+
try {
|
|
258
|
+
const parsed = JSON.parse(raw);
|
|
259
|
+
const root = parsed && typeof parsed === "object" ? parsed : {};
|
|
260
|
+
const auth = root.auth || root;
|
|
261
|
+
return auth && typeof auth === "object" ? auth : {};
|
|
262
|
+
} catch {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
246
267
|
function parseUrl(value) {
|
|
247
268
|
try {
|
|
248
269
|
return new URL(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.129",
|
|
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.129"
|
|
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.129",
|
|
4
4
|
"description": "Assistant-first CLI for running, inspecting, and debugging local testkit suites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -95,10 +95,10 @@
|
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
97
|
"@babel/code-frame": "^7.29.0",
|
|
98
|
-
"@elench/next-analysis": "0.1.
|
|
99
|
-
"@elench/testkit-bridge": "0.1.
|
|
100
|
-
"@elench/testkit-protocol": "0.1.
|
|
101
|
-
"@elench/ts-analysis": "0.1.
|
|
98
|
+
"@elench/next-analysis": "0.1.129",
|
|
99
|
+
"@elench/testkit-bridge": "0.1.129",
|
|
100
|
+
"@elench/testkit-protocol": "0.1.129",
|
|
101
|
+
"@elench/ts-analysis": "0.1.129",
|
|
102
102
|
"@oclif/core": "^4.10.6",
|
|
103
103
|
"@playwright/test": "^1.52.0",
|
|
104
104
|
"esbuild": "^0.25.11",
|