intellitester 0.2.43 → 0.2.45
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/dist/{chunk-GDPJLAXU.cjs → chunk-MZ7OCAGA.cjs} +386 -227
- package/dist/chunk-MZ7OCAGA.cjs.map +1 -0
- package/dist/{chunk-66ECN3LB.js → chunk-PBSXURXK.js} +369 -225
- package/dist/chunk-PBSXURXK.js.map +1 -0
- package/dist/cli/index.cjs +44 -24
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +33 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +11 -19
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-66ECN3LB.js.map +0 -1
- package/dist/chunk-GDPJLAXU.cjs.map +0 -1
|
@@ -7,8 +7,7 @@ var uniqueNamesGenerator = require('unique-names-generator');
|
|
|
7
7
|
var crypto4 = require('crypto');
|
|
8
8
|
var libphonenumberJs = require('libphonenumber-js');
|
|
9
9
|
var fs = require('fs/promises');
|
|
10
|
-
var
|
|
11
|
-
var child_process = require('child_process');
|
|
10
|
+
var path4 = require('path');
|
|
12
11
|
var playwright = require('playwright');
|
|
13
12
|
var prompts = require('prompts');
|
|
14
13
|
var nodeAppwrite = require('node-appwrite');
|
|
@@ -16,12 +15,31 @@ var anthropic = require('@llamaindex/anthropic');
|
|
|
16
15
|
var openai = require('@llamaindex/openai');
|
|
17
16
|
var ollama = require('@llamaindex/ollama');
|
|
18
17
|
var http = require('http');
|
|
18
|
+
var child_process = require('child_process');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
21
|
|
|
22
|
+
function _interopNamespace(e) {
|
|
23
|
+
if (e && e.__esModule) return e;
|
|
24
|
+
var n = Object.create(null);
|
|
25
|
+
if (e) {
|
|
26
|
+
Object.keys(e).forEach(function (k) {
|
|
27
|
+
if (k !== 'default') {
|
|
28
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
29
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () { return e[k]; }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
n.default = e;
|
|
37
|
+
return Object.freeze(n);
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
var crypto4__default = /*#__PURE__*/_interopDefault(crypto4);
|
|
23
|
-
var
|
|
24
|
-
var
|
|
41
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
42
|
+
var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
|
|
25
43
|
var prompts__default = /*#__PURE__*/_interopDefault(prompts);
|
|
26
44
|
|
|
27
45
|
function generateRandomUsername() {
|
|
@@ -881,12 +899,12 @@ var TRACK_DIR = ".intellitester/track";
|
|
|
881
899
|
var ACTIVE_TESTS_FILE = "ACTIVE_TESTS.json";
|
|
882
900
|
var DEFAULT_STALE_MS = 2 * 60 * 60 * 1e3;
|
|
883
901
|
var HEARTBEAT_MS = 15e3;
|
|
884
|
-
var getTrackDir = (cwd, trackDir) => trackDir ?
|
|
885
|
-
var getActiveTestsPath = (cwd, trackDir) =>
|
|
902
|
+
var getTrackDir = (cwd, trackDir) => trackDir ? path4__namespace.default.resolve(cwd, trackDir) : path4__namespace.default.join(cwd, TRACK_DIR);
|
|
903
|
+
var getActiveTestsPath = (cwd, trackDir) => path4__namespace.default.join(getTrackDir(cwd, trackDir), ACTIVE_TESTS_FILE);
|
|
886
904
|
var loadActiveTests = async (cwd, trackDir) => {
|
|
887
905
|
const filePath = getActiveTestsPath(cwd, trackDir);
|
|
888
906
|
try {
|
|
889
|
-
const content = await
|
|
907
|
+
const content = await fs__namespace.default.readFile(filePath, "utf8");
|
|
890
908
|
const parsed = JSON.parse(content);
|
|
891
909
|
if (!parsed.sessions || typeof parsed.sessions !== "object") {
|
|
892
910
|
throw new Error("Invalid ACTIVE_TESTS structure");
|
|
@@ -903,7 +921,7 @@ var loadActiveTests = async (cwd, trackDir) => {
|
|
|
903
921
|
var saveActiveTests = async (cwd, state, trackDir) => {
|
|
904
922
|
const filePath = getActiveTestsPath(cwd, trackDir);
|
|
905
923
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
906
|
-
await
|
|
924
|
+
await fs__namespace.default.writeFile(filePath, JSON.stringify(state, null, 2), "utf8");
|
|
907
925
|
};
|
|
908
926
|
var isStale = (entry, staleMs) => {
|
|
909
927
|
const last = new Date(entry.lastUpdated).getTime();
|
|
@@ -912,7 +930,7 @@ var isStale = (entry, staleMs) => {
|
|
|
912
930
|
};
|
|
913
931
|
var readTrackedResources = async (trackFile) => {
|
|
914
932
|
try {
|
|
915
|
-
const content = await
|
|
933
|
+
const content = await fs__namespace.default.readFile(trackFile, "utf8");
|
|
916
934
|
if (!content.trim()) return [];
|
|
917
935
|
return content.split("\n").filter(Boolean).map((line) => {
|
|
918
936
|
try {
|
|
@@ -948,19 +966,19 @@ var cleanupStaleSession = async (entry, cleanupConfig) => {
|
|
|
948
966
|
var cleanupOrphanedTrackFiles = async (cwd, state, trackDir) => {
|
|
949
967
|
const dir = getTrackDir(cwd, trackDir);
|
|
950
968
|
try {
|
|
951
|
-
const files = await
|
|
952
|
-
const activeFiles = new Set(Object.values(state.sessions).map((s) =>
|
|
969
|
+
const files = await fs__namespace.default.readdir(dir);
|
|
970
|
+
const activeFiles = new Set(Object.values(state.sessions).map((s) => path4__namespace.default.basename(s.trackFile)));
|
|
953
971
|
for (const file of files) {
|
|
954
972
|
if (!file.endsWith(".jsonl")) continue;
|
|
955
973
|
if (activeFiles.has(file)) continue;
|
|
956
|
-
const filePath =
|
|
974
|
+
const filePath = path4__namespace.default.join(dir, file);
|
|
957
975
|
try {
|
|
958
|
-
const
|
|
976
|
+
const stat2 = await fs__namespace.default.stat(filePath);
|
|
959
977
|
const staleMs = Number(process.env.INTELLITESTER_STALE_TEST_MS ?? DEFAULT_STALE_MS);
|
|
960
|
-
const isOld = Date.now() -
|
|
961
|
-
const isEmpty =
|
|
978
|
+
const isOld = Date.now() - stat2.mtimeMs > staleMs;
|
|
979
|
+
const isEmpty = stat2.size === 0;
|
|
962
980
|
if (isEmpty || isOld) {
|
|
963
|
-
await
|
|
981
|
+
await fs__namespace.default.rm(filePath, { force: true });
|
|
964
982
|
}
|
|
965
983
|
} catch {
|
|
966
984
|
}
|
|
@@ -976,8 +994,8 @@ var pruneStaleTests = async (cwd, cleanupConfig, trackDir) => {
|
|
|
976
994
|
let missingFile = false;
|
|
977
995
|
let isEmpty = false;
|
|
978
996
|
try {
|
|
979
|
-
const
|
|
980
|
-
isEmpty =
|
|
997
|
+
const stat2 = await fs__namespace.default.stat(entry.trackFile);
|
|
998
|
+
isEmpty = stat2.size === 0;
|
|
981
999
|
} catch {
|
|
982
1000
|
missingFile = true;
|
|
983
1001
|
}
|
|
@@ -985,7 +1003,7 @@ var pruneStaleTests = async (cwd, cleanupConfig, trackDir) => {
|
|
|
985
1003
|
changed = true;
|
|
986
1004
|
await cleanupStaleSession(entry, cleanupConfig);
|
|
987
1005
|
try {
|
|
988
|
-
await
|
|
1006
|
+
await fs__namespace.default.rm(entry.trackFile, { force: true });
|
|
989
1007
|
} catch {
|
|
990
1008
|
}
|
|
991
1009
|
delete state.sessions[sessionId];
|
|
@@ -998,10 +1016,10 @@ var pruneStaleTests = async (cwd, cleanupConfig, trackDir) => {
|
|
|
998
1016
|
async function initFileTracking(options) {
|
|
999
1017
|
const cwd = options.cwd ?? process.cwd();
|
|
1000
1018
|
const trackDir = options.trackDir;
|
|
1001
|
-
await
|
|
1019
|
+
await fs__namespace.default.mkdir(getTrackDir(cwd, trackDir), { recursive: true });
|
|
1002
1020
|
await pruneStaleTests(cwd, options.cleanupConfig, trackDir);
|
|
1003
|
-
const trackFile =
|
|
1004
|
-
await
|
|
1021
|
+
const trackFile = path4__namespace.default.join(getTrackDir(cwd, trackDir), `TEST_SESSION_${options.sessionId}.jsonl`);
|
|
1022
|
+
await fs__namespace.default.writeFile(trackFile, "", "utf8");
|
|
1005
1023
|
const state = await loadActiveTests(cwd, trackDir);
|
|
1006
1024
|
state.sessions[options.sessionId] = {
|
|
1007
1025
|
sessionId: options.sessionId,
|
|
@@ -1029,7 +1047,7 @@ async function initFileTracking(options) {
|
|
|
1029
1047
|
delete current.sessions[options.sessionId];
|
|
1030
1048
|
await saveActiveTests(cwd, current, trackDir);
|
|
1031
1049
|
try {
|
|
1032
|
-
await
|
|
1050
|
+
await fs__namespace.default.rm(trackFile, { force: true });
|
|
1033
1051
|
} catch {
|
|
1034
1052
|
}
|
|
1035
1053
|
};
|
|
@@ -1058,9 +1076,321 @@ async function mergeFileTrackedResources(trackFile, target, allowedTypes) {
|
|
|
1058
1076
|
}
|
|
1059
1077
|
}
|
|
1060
1078
|
}
|
|
1079
|
+
async function isServerRunning(url) {
|
|
1080
|
+
try {
|
|
1081
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
1082
|
+
return response.ok || response.status < 500;
|
|
1083
|
+
} catch {
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
async function readPackageJson(cwd) {
|
|
1088
|
+
try {
|
|
1089
|
+
const packagePath = path4__namespace.join(cwd, "package.json");
|
|
1090
|
+
const content = await fs__namespace.readFile(packagePath, "utf-8");
|
|
1091
|
+
return JSON.parse(content);
|
|
1092
|
+
} catch {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function detectFramework(pkg) {
|
|
1097
|
+
if (!pkg) return null;
|
|
1098
|
+
const deps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
|
|
1099
|
+
if (deps["next"]) {
|
|
1100
|
+
return { name: "next", buildCommand: "npx -y next start", devCommand: "next dev" };
|
|
1101
|
+
}
|
|
1102
|
+
if (deps["nuxt"]) {
|
|
1103
|
+
return { name: "nuxt", buildCommand: "node .output/server/index.mjs", devCommand: "nuxi dev" };
|
|
1104
|
+
}
|
|
1105
|
+
if (deps["astro"]) {
|
|
1106
|
+
return { name: "astro", buildCommand: "npx -y astro dev", devCommand: "astro dev" };
|
|
1107
|
+
}
|
|
1108
|
+
if (deps["@sveltejs/kit"]) {
|
|
1109
|
+
return { name: "sveltekit", buildCommand: "npx -y vite preview", devCommand: "vite dev" };
|
|
1110
|
+
}
|
|
1111
|
+
if (deps["@remix-run/serve"] || deps["@remix-run/dev"]) {
|
|
1112
|
+
return { name: "remix", buildCommand: "npx -y remix-serve build/server/index.js", devCommand: "remix vite:dev" };
|
|
1113
|
+
}
|
|
1114
|
+
if (deps["vite"]) {
|
|
1115
|
+
return { name: "vite", buildCommand: "npx -y vite preview", devCommand: "vite dev" };
|
|
1116
|
+
}
|
|
1117
|
+
if (deps["react-scripts"]) {
|
|
1118
|
+
return { name: "cra", buildCommand: "npx -y serve -s build", devCommand: "react-scripts start" };
|
|
1119
|
+
}
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
async function detectPackageManager(cwd) {
|
|
1123
|
+
const hasDenoLock = await fs__namespace.stat(path4__namespace.join(cwd, "deno.lock")).catch(() => null);
|
|
1124
|
+
const hasBunLock = await fs__namespace.stat(path4__namespace.join(cwd, "bun.lockb")).catch(() => null);
|
|
1125
|
+
const hasPnpmLock = await fs__namespace.stat(path4__namespace.join(cwd, "pnpm-lock.yaml")).catch(() => null);
|
|
1126
|
+
const hasYarnLock = await fs__namespace.stat(path4__namespace.join(cwd, "yarn.lock")).catch(() => null);
|
|
1127
|
+
if (hasDenoLock) return "deno";
|
|
1128
|
+
if (hasBunLock) return "bun";
|
|
1129
|
+
if (hasPnpmLock) return "pnpm";
|
|
1130
|
+
if (hasYarnLock) return "yarn";
|
|
1131
|
+
return "npm";
|
|
1132
|
+
}
|
|
1133
|
+
function getDevCommand(pm, script) {
|
|
1134
|
+
switch (pm) {
|
|
1135
|
+
case "deno":
|
|
1136
|
+
return `deno task ${script}`;
|
|
1137
|
+
case "bun":
|
|
1138
|
+
return `bun run ${script}`;
|
|
1139
|
+
case "pnpm":
|
|
1140
|
+
return `pnpm ${script}`;
|
|
1141
|
+
case "yarn":
|
|
1142
|
+
return `yarn ${script}`;
|
|
1143
|
+
case "npm":
|
|
1144
|
+
return `npm run ${script}`;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
async function detectBuildDirectory(cwd) {
|
|
1148
|
+
const commonDirs = [
|
|
1149
|
+
".next",
|
|
1150
|
+
".output",
|
|
1151
|
+
".svelte-kit",
|
|
1152
|
+
"dist",
|
|
1153
|
+
"build",
|
|
1154
|
+
"out"
|
|
1155
|
+
];
|
|
1156
|
+
for (const dir of commonDirs) {
|
|
1157
|
+
const fullPath = path4__namespace.join(cwd, dir);
|
|
1158
|
+
try {
|
|
1159
|
+
const stat2 = await fs__namespace.stat(fullPath);
|
|
1160
|
+
if (stat2.isDirectory()) {
|
|
1161
|
+
return dir;
|
|
1162
|
+
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
async function detectServerCommand(cwd) {
|
|
1169
|
+
const pkg = await readPackageJson(cwd);
|
|
1170
|
+
const framework = detectFramework(pkg);
|
|
1171
|
+
const pm = await detectPackageManager(cwd);
|
|
1172
|
+
const buildDir = await detectBuildDirectory(cwd);
|
|
1173
|
+
if (buildDir) {
|
|
1174
|
+
if (framework) {
|
|
1175
|
+
console.log(`Detected ${framework.name} project with build at ${buildDir}`);
|
|
1176
|
+
return framework.buildCommand;
|
|
1177
|
+
}
|
|
1178
|
+
console.log(`Detected build directory at ${buildDir}, using static server`);
|
|
1179
|
+
return `npx -y serve ${buildDir}`;
|
|
1180
|
+
}
|
|
1181
|
+
const scripts = pkg?.scripts;
|
|
1182
|
+
if (scripts?.dev) {
|
|
1183
|
+
if (framework) {
|
|
1184
|
+
console.log(`Detected ${framework.name} project, running dev server`);
|
|
1185
|
+
}
|
|
1186
|
+
return getDevCommand(pm, "dev");
|
|
1187
|
+
}
|
|
1188
|
+
if (scripts?.start) {
|
|
1189
|
+
return getDevCommand(pm, "start");
|
|
1190
|
+
}
|
|
1191
|
+
throw new Error("Could not auto-detect server command. Please specify command explicitly.");
|
|
1192
|
+
}
|
|
1193
|
+
var WebServerManager = class _WebServerManager {
|
|
1194
|
+
constructor() {
|
|
1195
|
+
this.serverProcess = null;
|
|
1196
|
+
this.currentUrl = null;
|
|
1197
|
+
this.currentCwd = null;
|
|
1198
|
+
this.stopping = false;
|
|
1199
|
+
}
|
|
1200
|
+
static getInstance() {
|
|
1201
|
+
if (!_WebServerManager.instance) {
|
|
1202
|
+
_WebServerManager.instance = new _WebServerManager();
|
|
1203
|
+
}
|
|
1204
|
+
return _WebServerManager.instance;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Check if the managed server is currently running
|
|
1208
|
+
*/
|
|
1209
|
+
isRunning() {
|
|
1210
|
+
return this.serverProcess !== null && !this.serverProcess.killed;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Get the current server URL if running
|
|
1214
|
+
*/
|
|
1215
|
+
getUrl() {
|
|
1216
|
+
return this.currentUrl;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Start a web server with the given config.
|
|
1220
|
+
*
|
|
1221
|
+
* - If a server is already running at the same URL, reuses it (unless reuseExistingServer=false)
|
|
1222
|
+
* - If a server is running at a different URL, stops it first
|
|
1223
|
+
* - Properly waits for any stopping server to fully terminate
|
|
1224
|
+
*/
|
|
1225
|
+
async start(config) {
|
|
1226
|
+
const { url, reuseExistingServer = true, timeout = 3e4, idleTimeout = 2e4 } = config;
|
|
1227
|
+
const cwd = config.workdir ?? config.cwd ?? process.cwd();
|
|
1228
|
+
while (this.stopping) {
|
|
1229
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
1230
|
+
}
|
|
1231
|
+
if (this.serverProcess && !this.serverProcess.killed && this.currentUrl === url) {
|
|
1232
|
+
if (await isServerRunning(url)) {
|
|
1233
|
+
if (reuseExistingServer) {
|
|
1234
|
+
console.log(`Server already running at ${url}`);
|
|
1235
|
+
return this.serverProcess;
|
|
1236
|
+
} else {
|
|
1237
|
+
await this.stop();
|
|
1238
|
+
}
|
|
1239
|
+
} else {
|
|
1240
|
+
await this.stop();
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
if (this.serverProcess && !this.serverProcess.killed && this.currentUrl !== url) {
|
|
1244
|
+
await this.stop();
|
|
1245
|
+
}
|
|
1246
|
+
if (reuseExistingServer && await isServerRunning(url)) {
|
|
1247
|
+
console.log(`Server already running at ${url}`);
|
|
1248
|
+
this.currentUrl = url;
|
|
1249
|
+
this.currentCwd = cwd;
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
let command;
|
|
1253
|
+
if (config.command) {
|
|
1254
|
+
command = config.command;
|
|
1255
|
+
} else if (config.static) {
|
|
1256
|
+
const port = config.port ?? new URL(url).port ?? "3000";
|
|
1257
|
+
command = `npx -y serve ${config.static} -l ${port}`;
|
|
1258
|
+
} else if (config.auto) {
|
|
1259
|
+
command = await detectServerCommand(cwd);
|
|
1260
|
+
} else {
|
|
1261
|
+
throw new Error("WebServerConfig requires command, auto: true, or static directory");
|
|
1262
|
+
}
|
|
1263
|
+
console.log(`Starting server: ${command}`);
|
|
1264
|
+
this.serverProcess = child_process.spawn(command, {
|
|
1265
|
+
shell: true,
|
|
1266
|
+
stdio: "pipe",
|
|
1267
|
+
cwd,
|
|
1268
|
+
detached: false
|
|
1269
|
+
});
|
|
1270
|
+
this.currentUrl = url;
|
|
1271
|
+
this.currentCwd = cwd;
|
|
1272
|
+
let stderrOutput = "";
|
|
1273
|
+
let lastOutputTime = Date.now();
|
|
1274
|
+
this.serverProcess.stdout?.on("data", (data) => {
|
|
1275
|
+
lastOutputTime = Date.now();
|
|
1276
|
+
process.stdout.write(`[server] ${data}`);
|
|
1277
|
+
});
|
|
1278
|
+
this.serverProcess.stderr?.on("data", (data) => {
|
|
1279
|
+
lastOutputTime = Date.now();
|
|
1280
|
+
stderrOutput += data.toString();
|
|
1281
|
+
process.stderr.write(`[server] ${data}`);
|
|
1282
|
+
});
|
|
1283
|
+
await new Promise((resolve, reject) => {
|
|
1284
|
+
let resolved = false;
|
|
1285
|
+
const startTime = Date.now();
|
|
1286
|
+
const cleanup = () => {
|
|
1287
|
+
resolved = true;
|
|
1288
|
+
clearInterval(pollInterval);
|
|
1289
|
+
};
|
|
1290
|
+
this.serverProcess.on("close", (code) => {
|
|
1291
|
+
if (!resolved && code !== 0 && code !== null) {
|
|
1292
|
+
cleanup();
|
|
1293
|
+
this.serverProcess = null;
|
|
1294
|
+
this.currentUrl = null;
|
|
1295
|
+
reject(new Error(`Server exited with code ${code}
|
|
1296
|
+
${stderrOutput}`));
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
this.serverProcess.on("error", (err) => {
|
|
1300
|
+
if (!resolved) {
|
|
1301
|
+
cleanup();
|
|
1302
|
+
this.serverProcess = null;
|
|
1303
|
+
this.currentUrl = null;
|
|
1304
|
+
reject(err);
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
const pollInterval = setInterval(async () => {
|
|
1308
|
+
if (resolved) return;
|
|
1309
|
+
if (await isServerRunning(url)) {
|
|
1310
|
+
cleanup();
|
|
1311
|
+
resolve();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
if (Date.now() - startTime > timeout) {
|
|
1315
|
+
cleanup();
|
|
1316
|
+
reject(new Error(`Server at ${url} not ready after ${timeout}ms`));
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (Date.now() - lastOutputTime > idleTimeout) {
|
|
1320
|
+
cleanup();
|
|
1321
|
+
this.serverProcess?.kill("SIGTERM");
|
|
1322
|
+
this.serverProcess = null;
|
|
1323
|
+
this.currentUrl = null;
|
|
1324
|
+
const lastOutput = stderrOutput.slice(-500);
|
|
1325
|
+
reject(new Error(`Server stalled - no output for ${idleTimeout}ms. Last output:
|
|
1326
|
+
${lastOutput}`));
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
}, 500);
|
|
1330
|
+
});
|
|
1331
|
+
console.log(`Server ready at ${url}`);
|
|
1332
|
+
return this.serverProcess;
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Stop the managed server and wait for it to fully terminate.
|
|
1336
|
+
* This prevents race conditions where a dying server still responds to health checks.
|
|
1337
|
+
*/
|
|
1338
|
+
async stop() {
|
|
1339
|
+
if (!this.serverProcess || this.serverProcess.killed) {
|
|
1340
|
+
this.serverProcess = null;
|
|
1341
|
+
this.currentUrl = null;
|
|
1342
|
+
this.currentCwd = null;
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
this.stopping = true;
|
|
1346
|
+
console.log("Stopping server...");
|
|
1347
|
+
const process2 = this.serverProcess;
|
|
1348
|
+
const exitPromise = new Promise((resolve) => {
|
|
1349
|
+
const onExit = () => {
|
|
1350
|
+
process2.removeListener("close", onExit);
|
|
1351
|
+
process2.removeListener("exit", onExit);
|
|
1352
|
+
resolve();
|
|
1353
|
+
};
|
|
1354
|
+
process2.on("close", onExit);
|
|
1355
|
+
process2.on("exit", onExit);
|
|
1356
|
+
if (process2.killed || process2.exitCode !== null) {
|
|
1357
|
+
resolve();
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
process2.kill("SIGTERM");
|
|
1361
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1362
|
+
setTimeout(() => {
|
|
1363
|
+
if (!process2.killed && process2.exitCode === null) {
|
|
1364
|
+
console.log("Server did not stop gracefully, sending SIGKILL...");
|
|
1365
|
+
process2.kill("SIGKILL");
|
|
1366
|
+
}
|
|
1367
|
+
resolve();
|
|
1368
|
+
}, 5e3);
|
|
1369
|
+
});
|
|
1370
|
+
await Promise.race([exitPromise, timeoutPromise]);
|
|
1371
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1372
|
+
this.serverProcess = null;
|
|
1373
|
+
this.currentUrl = null;
|
|
1374
|
+
this.currentCwd = null;
|
|
1375
|
+
this.stopping = false;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Synchronous kill for signal handlers - doesn't wait for termination
|
|
1379
|
+
*/
|
|
1380
|
+
kill() {
|
|
1381
|
+
if (this.serverProcess && !this.serverProcess.killed) {
|
|
1382
|
+
console.log("Stopping server...");
|
|
1383
|
+
this.serverProcess.kill("SIGTERM");
|
|
1384
|
+
}
|
|
1385
|
+
this.serverProcess = null;
|
|
1386
|
+
this.currentUrl = null;
|
|
1387
|
+
this.currentCwd = null;
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
var webServerManager = WebServerManager.getInstance();
|
|
1061
1391
|
|
|
1062
1392
|
// src/executors/web/playwrightExecutor.ts
|
|
1063
|
-
var defaultScreenshotDir =
|
|
1393
|
+
var defaultScreenshotDir = path4__namespace.default.join(process.cwd(), "artifacts", "screenshots");
|
|
1064
1394
|
var resolveUrl = (value, baseUrl) => {
|
|
1065
1395
|
if (!baseUrl) return value;
|
|
1066
1396
|
try {
|
|
@@ -1131,7 +1461,7 @@ var checkErrorIf = async (page, locator, errorIf) => {
|
|
|
1131
1461
|
}
|
|
1132
1462
|
};
|
|
1133
1463
|
async function ensureScreenshotDir(dir) {
|
|
1134
|
-
await
|
|
1464
|
+
await fs__namespace.default.mkdir(dir, { recursive: true });
|
|
1135
1465
|
}
|
|
1136
1466
|
var runNavigate = async (page, value, baseUrl, context) => {
|
|
1137
1467
|
const interpolated = interpolateVariables(value, context.variables);
|
|
@@ -1192,7 +1522,7 @@ var runScreenshot = async (page, name, screenshotDir, stepIndex) => {
|
|
|
1192
1522
|
await page.waitForLoadState("networkidle", { timeout: 5e3 }).catch(() => {
|
|
1193
1523
|
});
|
|
1194
1524
|
const filename = name ?? `step-${stepIndex + 1}.png`;
|
|
1195
|
-
const filePath =
|
|
1525
|
+
const filePath = path4__namespace.default.join(screenshotDir, filename);
|
|
1196
1526
|
await page.screenshot({ path: filePath, fullPage: true });
|
|
1197
1527
|
return filePath;
|
|
1198
1528
|
};
|
|
@@ -1206,186 +1536,18 @@ var getBrowser = (browser) => {
|
|
|
1206
1536
|
return playwright.chromium;
|
|
1207
1537
|
}
|
|
1208
1538
|
};
|
|
1209
|
-
async function isServerRunning(url) {
|
|
1210
|
-
try {
|
|
1211
|
-
const response = await fetch(url, { method: "HEAD" });
|
|
1212
|
-
return response.ok || response.status < 500;
|
|
1213
|
-
} catch {
|
|
1214
|
-
return false;
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
async function waitForServer(url, timeout) {
|
|
1218
|
-
const start = Date.now();
|
|
1219
|
-
while (Date.now() - start < timeout) {
|
|
1220
|
-
if (await isServerRunning(url)) return;
|
|
1221
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
1222
|
-
}
|
|
1223
|
-
throw new Error(`Server at ${url} not ready after ${timeout}ms`);
|
|
1224
|
-
}
|
|
1225
|
-
async function detectBuildDirectory(cwd) {
|
|
1226
|
-
const commonDirs = [
|
|
1227
|
-
".next",
|
|
1228
|
-
// Next.js
|
|
1229
|
-
".output",
|
|
1230
|
-
// Nuxt 3
|
|
1231
|
-
".svelte-kit",
|
|
1232
|
-
// SvelteKit
|
|
1233
|
-
"dist",
|
|
1234
|
-
// Vite, Astro, Rollup, generic
|
|
1235
|
-
"build",
|
|
1236
|
-
// CRA, Remix, generic
|
|
1237
|
-
"out"
|
|
1238
|
-
// Next.js static export
|
|
1239
|
-
];
|
|
1240
|
-
for (const dir of commonDirs) {
|
|
1241
|
-
const fullPath = path2__default.default.join(cwd, dir);
|
|
1242
|
-
try {
|
|
1243
|
-
const stat = await fs__default.default.stat(fullPath);
|
|
1244
|
-
if (stat.isDirectory()) {
|
|
1245
|
-
return dir;
|
|
1246
|
-
}
|
|
1247
|
-
} catch {
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
return null;
|
|
1251
|
-
}
|
|
1252
|
-
async function readPackageJson(cwd) {
|
|
1253
|
-
try {
|
|
1254
|
-
const packagePath = path2__default.default.join(cwd, "package.json");
|
|
1255
|
-
const content = await fs__default.default.readFile(packagePath, "utf-8");
|
|
1256
|
-
return JSON.parse(content);
|
|
1257
|
-
} catch {
|
|
1258
|
-
return null;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
function detectFramework(pkg) {
|
|
1262
|
-
if (!pkg) return null;
|
|
1263
|
-
const deps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
|
|
1264
|
-
if (deps["next"]) {
|
|
1265
|
-
return { name: "next", buildCommand: "npx -y next start", devCommand: "next dev" };
|
|
1266
|
-
}
|
|
1267
|
-
if (deps["nuxt"]) {
|
|
1268
|
-
return { name: "nuxt", buildCommand: "node .output/server/index.mjs", devCommand: "nuxi dev" };
|
|
1269
|
-
}
|
|
1270
|
-
if (deps["astro"]) {
|
|
1271
|
-
return { name: "astro", buildCommand: "npx -y astro dev", devCommand: "astro dev" };
|
|
1272
|
-
}
|
|
1273
|
-
if (deps["@sveltejs/kit"]) {
|
|
1274
|
-
return { name: "sveltekit", buildCommand: "npx -y vite preview", devCommand: "vite dev" };
|
|
1275
|
-
}
|
|
1276
|
-
if (deps["@remix-run/serve"] || deps["@remix-run/dev"]) {
|
|
1277
|
-
return { name: "remix", buildCommand: "npx -y remix-serve build/server/index.js", devCommand: "remix vite:dev" };
|
|
1278
|
-
}
|
|
1279
|
-
if (deps["vite"]) {
|
|
1280
|
-
return { name: "vite", buildCommand: "npx -y vite preview", devCommand: "vite dev" };
|
|
1281
|
-
}
|
|
1282
|
-
if (deps["react-scripts"]) {
|
|
1283
|
-
return { name: "cra", buildCommand: "npx -y serve -s build", devCommand: "react-scripts start" };
|
|
1284
|
-
}
|
|
1285
|
-
return null;
|
|
1286
|
-
}
|
|
1287
|
-
async function detectPackageManager(cwd) {
|
|
1288
|
-
const hasDenoLock = await fs__default.default.stat(path2__default.default.join(cwd, "deno.lock")).catch(() => null);
|
|
1289
|
-
const hasBunLock = await fs__default.default.stat(path2__default.default.join(cwd, "bun.lockb")).catch(() => null);
|
|
1290
|
-
const hasPnpmLock = await fs__default.default.stat(path2__default.default.join(cwd, "pnpm-lock.yaml")).catch(() => null);
|
|
1291
|
-
const hasYarnLock = await fs__default.default.stat(path2__default.default.join(cwd, "yarn.lock")).catch(() => null);
|
|
1292
|
-
if (hasDenoLock) return "deno";
|
|
1293
|
-
if (hasBunLock) return "bun";
|
|
1294
|
-
if (hasPnpmLock) return "pnpm";
|
|
1295
|
-
if (hasYarnLock) return "yarn";
|
|
1296
|
-
return "npm";
|
|
1297
|
-
}
|
|
1298
|
-
function getDevCommand(pm, script) {
|
|
1299
|
-
switch (pm) {
|
|
1300
|
-
case "deno":
|
|
1301
|
-
return `deno task ${script}`;
|
|
1302
|
-
case "bun":
|
|
1303
|
-
return `bun run ${script}`;
|
|
1304
|
-
case "pnpm":
|
|
1305
|
-
return `pnpm ${script}`;
|
|
1306
|
-
case "yarn":
|
|
1307
|
-
return `yarn ${script}`;
|
|
1308
|
-
case "npm":
|
|
1309
|
-
return `npm run ${script}`;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
async function detectServerCommand(cwd) {
|
|
1313
|
-
const pkg = await readPackageJson(cwd);
|
|
1314
|
-
const framework = detectFramework(pkg);
|
|
1315
|
-
const pm = await detectPackageManager(cwd);
|
|
1316
|
-
const buildDir = await detectBuildDirectory(cwd);
|
|
1317
|
-
if (buildDir) {
|
|
1318
|
-
if (framework) {
|
|
1319
|
-
console.log(`Detected ${framework.name} project with build at ${buildDir}`);
|
|
1320
|
-
return framework.buildCommand;
|
|
1321
|
-
}
|
|
1322
|
-
console.log(`Detected build directory at ${buildDir}, using static server`);
|
|
1323
|
-
return `npx -y serve ${buildDir}`;
|
|
1324
|
-
}
|
|
1325
|
-
if (pkg?.scripts?.dev) {
|
|
1326
|
-
if (framework) {
|
|
1327
|
-
console.log(`Detected ${framework.name} project, running dev server`);
|
|
1328
|
-
}
|
|
1329
|
-
return getDevCommand(pm, "dev");
|
|
1330
|
-
}
|
|
1331
|
-
if (pkg?.scripts?.start) {
|
|
1332
|
-
return getDevCommand(pm, "start");
|
|
1333
|
-
}
|
|
1334
|
-
throw new Error("Could not auto-detect server command. Please specify command explicitly.");
|
|
1335
|
-
}
|
|
1336
|
-
async function startWebServer(config) {
|
|
1337
|
-
const { url, reuseExistingServer = true, timeout = 3e4 } = config;
|
|
1338
|
-
const cwd = config.workdir ?? config.cwd ?? process.cwd();
|
|
1339
|
-
if (reuseExistingServer && await isServerRunning(url)) {
|
|
1340
|
-
console.log(`Server already running at ${url}`);
|
|
1341
|
-
return null;
|
|
1342
|
-
}
|
|
1343
|
-
let command;
|
|
1344
|
-
if (config.command) {
|
|
1345
|
-
command = config.command;
|
|
1346
|
-
} else if (config.static) {
|
|
1347
|
-
const port = config.port ?? new URL(url).port ?? "3000";
|
|
1348
|
-
command = `npx -y serve ${config.static} -l ${port}`;
|
|
1349
|
-
} else if (config.auto) {
|
|
1350
|
-
command = await detectServerCommand(cwd);
|
|
1351
|
-
} else {
|
|
1352
|
-
throw new Error("WebServerConfig requires command, auto: true, or static directory");
|
|
1353
|
-
}
|
|
1354
|
-
console.log(`Starting server: ${command}`);
|
|
1355
|
-
const serverProcess = child_process.spawn(command, {
|
|
1356
|
-
shell: true,
|
|
1357
|
-
stdio: "pipe",
|
|
1358
|
-
cwd,
|
|
1359
|
-
detached: false
|
|
1360
|
-
});
|
|
1361
|
-
serverProcess.stdout?.on("data", (data) => {
|
|
1362
|
-
process.stdout.write(`[server] ${data}`);
|
|
1363
|
-
});
|
|
1364
|
-
serverProcess.stderr?.on("data", (data) => {
|
|
1365
|
-
process.stderr.write(`[server] ${data}`);
|
|
1366
|
-
});
|
|
1367
|
-
await waitForServer(url, timeout);
|
|
1368
|
-
console.log(`Server ready at ${url}`);
|
|
1369
|
-
return serverProcess;
|
|
1370
|
-
}
|
|
1371
|
-
function killServer(serverProcess) {
|
|
1372
|
-
if (serverProcess && !serverProcess.killed) {
|
|
1373
|
-
console.log("Stopping server...");
|
|
1374
|
-
serverProcess.kill("SIGTERM");
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
1539
|
async function handleInteractiveError(page, action, error, screenshotDir, stepIndex, aiConfig) {
|
|
1378
1540
|
console.error(`
|
|
1379
1541
|
\u274C Action failed: ${action.type}`);
|
|
1380
1542
|
console.error(` Error: ${error.message}
|
|
1381
1543
|
`);
|
|
1382
1544
|
await ensureScreenshotDir(screenshotDir);
|
|
1383
|
-
const screenshotPath =
|
|
1545
|
+
const screenshotPath = path4__namespace.default.join(screenshotDir, `error-step-${stepIndex + 1}.png`);
|
|
1384
1546
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
1385
1547
|
const pageContent = await page.content();
|
|
1386
1548
|
if (aiConfig) {
|
|
1387
1549
|
console.log("\u{1F916} Analyzing error with AI...\n");
|
|
1388
|
-
const screenshot = await
|
|
1550
|
+
const screenshot = await fs__namespace.default.readFile(screenshotPath);
|
|
1389
1551
|
const suggestion = await getAISuggestion(error.message, action, pageContent, screenshot, aiConfig);
|
|
1390
1552
|
if (suggestion.hasSuggestion && suggestion.suggestedSelector) {
|
|
1391
1553
|
console.log("\u{1F916} AI Suggestion:");
|
|
@@ -1793,8 +1955,8 @@ async function executeActionWithRetry(page, action, index, options) {
|
|
|
1793
1955
|
}
|
|
1794
1956
|
} else {
|
|
1795
1957
|
const { loadWorkflowDefinition, loadTestDefinition: loadTestDefinition2 } = await import('./loader-QG5BFIY6.cjs');
|
|
1796
|
-
const workflowPath =
|
|
1797
|
-
const workflowDir =
|
|
1958
|
+
const workflowPath = path4__namespace.default.resolve(process.cwd(), branchToExecute.workflow);
|
|
1959
|
+
const workflowDir = path4__namespace.default.dirname(workflowPath);
|
|
1798
1960
|
if (debugMode) {
|
|
1799
1961
|
console.log(`[DEBUG] Executing workflow: ${workflowPath}`);
|
|
1800
1962
|
}
|
|
@@ -1806,7 +1968,7 @@ async function executeActionWithRetry(page, action, index, options) {
|
|
|
1806
1968
|
}
|
|
1807
1969
|
}
|
|
1808
1970
|
for (const testRef of workflow.tests) {
|
|
1809
|
-
const testFilePath =
|
|
1971
|
+
const testFilePath = path4__namespace.default.resolve(workflowDir, testRef.file);
|
|
1810
1972
|
if (debugMode) {
|
|
1811
1973
|
console.log(`[DEBUG] Loading test from workflow: ${testFilePath}`);
|
|
1812
1974
|
}
|
|
@@ -1905,7 +2067,6 @@ var runWebTest = async (test, options = {}) => {
|
|
|
1905
2067
|
} : void 0
|
|
1906
2068
|
});
|
|
1907
2069
|
process.env.INTELLITESTER_TRACK_FILE = fileTracking.trackFile;
|
|
1908
|
-
let serverProcess = null;
|
|
1909
2070
|
if (options.webServer) {
|
|
1910
2071
|
const requiresTrackingEnv = Boolean(
|
|
1911
2072
|
test.config?.appwrite?.cleanup || test.config?.appwrite?.cleanupOnFailure
|
|
@@ -1914,11 +2075,11 @@ var runWebTest = async (test, options = {}) => {
|
|
|
1914
2075
|
if (requiresTrackingEnv && options.webServer.reuseExistingServer !== false) {
|
|
1915
2076
|
console.log("[Intellitester] Appwrite cleanup enabled; restarting server to inject tracking env.");
|
|
1916
2077
|
}
|
|
1917
|
-
|
|
2078
|
+
await webServerManager.start(webServerConfig);
|
|
1918
2079
|
}
|
|
1919
2080
|
const cleanup = () => {
|
|
1920
2081
|
trackingServer.stop();
|
|
1921
|
-
|
|
2082
|
+
webServerManager.kill();
|
|
1922
2083
|
void fileTracking.stop();
|
|
1923
2084
|
delete process.env.INTELLITESTER_TRACK_FILE;
|
|
1924
2085
|
process.exit(1);
|
|
@@ -2228,7 +2389,7 @@ var runWebTest = async (test, options = {}) => {
|
|
|
2228
2389
|
await browserContext.close();
|
|
2229
2390
|
await browser.close();
|
|
2230
2391
|
trackingServer.stop();
|
|
2231
|
-
|
|
2392
|
+
await webServerManager.stop();
|
|
2232
2393
|
}
|
|
2233
2394
|
return {
|
|
2234
2395
|
status: results.every((step) => step.status === "passed") ? "passed" : "failed",
|
|
@@ -2236,7 +2397,7 @@ var runWebTest = async (test, options = {}) => {
|
|
|
2236
2397
|
variables: executionContext.variables
|
|
2237
2398
|
};
|
|
2238
2399
|
};
|
|
2239
|
-
var defaultScreenshotDir2 =
|
|
2400
|
+
var defaultScreenshotDir2 = path4__namespace.default.join(process.cwd(), "artifacts", "screenshots");
|
|
2240
2401
|
var getBrowser2 = (browser) => {
|
|
2241
2402
|
switch (browser) {
|
|
2242
2403
|
case "firefox":
|
|
@@ -2248,14 +2409,14 @@ var getBrowser2 = (browser) => {
|
|
|
2248
2409
|
}
|
|
2249
2410
|
};
|
|
2250
2411
|
function interpolateWorkflowVariables(value, currentVariables, testResults) {
|
|
2251
|
-
return value.replace(/\{\{([^}]+)\}\}/g, (match,
|
|
2252
|
-
if (
|
|
2253
|
-
const [testId, _varName] =
|
|
2412
|
+
return value.replace(/\{\{([^}]+)\}\}/g, (match, path5) => {
|
|
2413
|
+
if (path5.includes(".") && !path5.includes(":")) {
|
|
2414
|
+
const [testId, _varName] = path5.split(".", 2);
|
|
2254
2415
|
testResults.find((t) => t.id === testId);
|
|
2255
|
-
console.warn(`Cross-test variable interpolation {{${
|
|
2416
|
+
console.warn(`Cross-test variable interpolation {{${path5}}} not yet fully implemented`);
|
|
2256
2417
|
return match;
|
|
2257
2418
|
}
|
|
2258
|
-
const result = interpolateVariables(`{{${
|
|
2419
|
+
const result = interpolateVariables(`{{${path5}}}`, currentVariables);
|
|
2259
2420
|
return result;
|
|
2260
2421
|
});
|
|
2261
2422
|
}
|
|
@@ -2422,7 +2583,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2422
2583
|
await page.waitForTimeout(waitBefore);
|
|
2423
2584
|
}
|
|
2424
2585
|
const filename = ssAction.name ?? `step-${index + 1}.png`;
|
|
2425
|
-
const filePath =
|
|
2586
|
+
const filePath = path4__namespace.default.join(screenshotDir, filename);
|
|
2426
2587
|
await page.screenshot({ path: filePath, fullPage: true });
|
|
2427
2588
|
results.push({ action, status: "passed", screenshotPath: filePath });
|
|
2428
2589
|
const trackedPayload2 = buildTrackPayload(action, index, { screenshotPath: filePath });
|
|
@@ -2596,7 +2757,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2596
2757
|
switch (nestedAction.type) {
|
|
2597
2758
|
case "screenshot": {
|
|
2598
2759
|
const filename = nestedAction.name ?? `conditional-step.png`;
|
|
2599
|
-
const filePath =
|
|
2760
|
+
const filePath = path4__namespace.default.join(screenshotDir, filename);
|
|
2600
2761
|
await page.screenshot({ path: filePath, fullPage: true });
|
|
2601
2762
|
results.push({ action: nestedAction, status: "passed", screenshotPath: filePath });
|
|
2602
2763
|
const trackedPayload2 = buildTrackPayload(nestedAction, index, { screenshotPath: filePath });
|
|
@@ -2686,7 +2847,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2686
2847
|
await page.waitForTimeout(nestedWaitBefore);
|
|
2687
2848
|
}
|
|
2688
2849
|
const filename = nestedSsAction.name ?? `waitForBranch-step.png`;
|
|
2689
|
-
const filePath =
|
|
2850
|
+
const filePath = path4__namespace.default.join(screenshotDir, filename);
|
|
2690
2851
|
await page.screenshot({ path: filePath, fullPage: true });
|
|
2691
2852
|
results.push({ action: nestedAction, status: "passed", screenshotPath: filePath });
|
|
2692
2853
|
const trackedPayload2 = buildTrackPayload(nestedAction, index, { screenshotPath: filePath });
|
|
@@ -2744,7 +2905,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2744
2905
|
results.push({ action: nestedAction, status: "passed" });
|
|
2745
2906
|
}
|
|
2746
2907
|
} else if (typeof branch === "object" && "workflow" in branch) {
|
|
2747
|
-
const workflowPath =
|
|
2908
|
+
const workflowPath = path4__namespace.default.resolve(_workflowDir, branch.workflow);
|
|
2748
2909
|
if (debugMode) {
|
|
2749
2910
|
console.log(` [DEBUG] waitForBranch: loading workflow from ${workflowPath}`);
|
|
2750
2911
|
}
|
|
@@ -2757,7 +2918,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2757
2918
|
}
|
|
2758
2919
|
}
|
|
2759
2920
|
for (const testRef of nestedWorkflow.tests) {
|
|
2760
|
-
const testFilePath =
|
|
2921
|
+
const testFilePath = path4__namespace.default.resolve(path4__namespace.default.dirname(workflowPath), testRef.file);
|
|
2761
2922
|
const nestedTest = await chunk2ZBKD7WW_cjs.loadTestDefinition(testFilePath);
|
|
2762
2923
|
if (nestedTest.variables) {
|
|
2763
2924
|
for (const [key, value] of Object.entries(nestedTest.variables)) {
|
|
@@ -2770,7 +2931,7 @@ async function runTestInWorkflow(test, page, context, options, _workflowDir, wor
|
|
|
2770
2931
|
page,
|
|
2771
2932
|
context,
|
|
2772
2933
|
options,
|
|
2773
|
-
|
|
2934
|
+
path4__namespace.default.dirname(workflowPath),
|
|
2774
2935
|
nestedWorkflow.config?.web?.baseUrl ?? workflowBaseUrl
|
|
2775
2936
|
);
|
|
2776
2937
|
results.push(...nestedResult.steps);
|
|
@@ -2985,7 +3146,7 @@ function inferCleanupConfig(config) {
|
|
|
2985
3146
|
}
|
|
2986
3147
|
async function runWorkflowWithContext(workflow, workflowFilePath, options) {
|
|
2987
3148
|
const { page, executionContext, skipCleanup = false, sessionId: providedSessionId, testStartTime: providedTestStartTime } = options;
|
|
2988
|
-
const workflowDir =
|
|
3149
|
+
const workflowDir = path4__namespace.default.dirname(workflowFilePath);
|
|
2989
3150
|
const sessionId = providedSessionId ?? crypto4__default.default.randomUUID();
|
|
2990
3151
|
const testStartTime = providedTestStartTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2991
3152
|
console.log(`
|
|
@@ -3013,7 +3174,7 @@ Starting workflow: ${workflow.name}`);
|
|
|
3013
3174
|
const testResults = [];
|
|
3014
3175
|
let workflowFailed = false;
|
|
3015
3176
|
for (const [index, testRef] of workflow.tests.entries()) {
|
|
3016
|
-
const testFilePath =
|
|
3177
|
+
const testFilePath = path4__namespace.default.resolve(workflowDir, testRef.file);
|
|
3017
3178
|
console.log(`
|
|
3018
3179
|
[${index + 1}/${workflow.tests.length}] Running: ${testRef.file}`);
|
|
3019
3180
|
if (testRef.id) {
|
|
@@ -3174,7 +3335,7 @@ ${"=".repeat(60)}`);
|
|
|
3174
3335
|
};
|
|
3175
3336
|
}
|
|
3176
3337
|
async function runWorkflow(workflow, workflowFilePath, options = {}) {
|
|
3177
|
-
const workflowDir =
|
|
3338
|
+
const workflowDir = path4__namespace.default.dirname(workflowFilePath);
|
|
3178
3339
|
const sessionId = options.sessionId ?? crypto4__default.default.randomUUID();
|
|
3179
3340
|
const testStartTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
3180
3341
|
const cleanupConfig = inferCleanupConfig(workflow.config);
|
|
@@ -3201,7 +3362,6 @@ async function runWorkflow(workflow, workflowFilePath, options = {}) {
|
|
|
3201
3362
|
} : void 0
|
|
3202
3363
|
});
|
|
3203
3364
|
process.env.INTELLITESTER_TRACK_FILE = fileTracking.trackFile;
|
|
3204
|
-
let serverProcess = null;
|
|
3205
3365
|
const webServerConfig = workflow.config?.webServer ?? options.webServer;
|
|
3206
3366
|
if (webServerConfig) {
|
|
3207
3367
|
try {
|
|
@@ -3213,9 +3373,9 @@ async function runWorkflow(workflow, workflowFilePath, options = {}) {
|
|
|
3213
3373
|
if (requiresTrackingEnv && webServerConfig.reuseExistingServer !== false) {
|
|
3214
3374
|
console.log("[Intellitester] Appwrite cleanup enabled; restarting server to inject tracking env.");
|
|
3215
3375
|
}
|
|
3216
|
-
|
|
3376
|
+
await webServerManager.start({
|
|
3217
3377
|
...effectiveWebServerConfig,
|
|
3218
|
-
workdir:
|
|
3378
|
+
workdir: path4__namespace.default.resolve(serverCwd, effectiveWebServerConfig.workdir ?? effectiveWebServerConfig.cwd ?? ".")
|
|
3219
3379
|
});
|
|
3220
3380
|
} catch (error) {
|
|
3221
3381
|
console.error("Failed to start web server:", error);
|
|
@@ -3225,7 +3385,7 @@ async function runWorkflow(workflow, workflowFilePath, options = {}) {
|
|
|
3225
3385
|
}
|
|
3226
3386
|
const signalCleanup = async () => {
|
|
3227
3387
|
console.log("\n\nInterrupted - cleaning up...");
|
|
3228
|
-
|
|
3388
|
+
webServerManager.kill();
|
|
3229
3389
|
if (trackingServer) await trackingServer.stop();
|
|
3230
3390
|
await fileTracking.stop();
|
|
3231
3391
|
delete process.env.INTELLITESTER_TRACK_FILE;
|
|
@@ -3365,7 +3525,7 @@ Collected ${serverResources.length} server-tracked resources`);
|
|
|
3365
3525
|
process.off("SIGTERM", signalCleanup);
|
|
3366
3526
|
await browserContext.close();
|
|
3367
3527
|
await browser.close();
|
|
3368
|
-
|
|
3528
|
+
await webServerManager.stop();
|
|
3369
3529
|
if (trackingServer) {
|
|
3370
3530
|
await trackingServer.stop();
|
|
3371
3531
|
}
|
|
@@ -3386,13 +3546,12 @@ exports.generateRandomUsername = generateRandomUsername;
|
|
|
3386
3546
|
exports.getBrowserLaunchOptions = getBrowserLaunchOptions;
|
|
3387
3547
|
exports.initFileTracking = initFileTracking;
|
|
3388
3548
|
exports.interpolateVariables = interpolateVariables;
|
|
3389
|
-
exports.killServer = killServer;
|
|
3390
3549
|
exports.mergeFileTrackedResources = mergeFileTrackedResources;
|
|
3391
3550
|
exports.runWebTest = runWebTest;
|
|
3392
3551
|
exports.runWorkflow = runWorkflow;
|
|
3393
3552
|
exports.runWorkflowWithContext = runWorkflowWithContext;
|
|
3394
3553
|
exports.setupAppwriteTracking = setupAppwriteTracking;
|
|
3395
3554
|
exports.startTrackingServer = startTrackingServer;
|
|
3396
|
-
exports.
|
|
3397
|
-
//# sourceMappingURL=chunk-
|
|
3398
|
-
//# sourceMappingURL=chunk-
|
|
3555
|
+
exports.webServerManager = webServerManager;
|
|
3556
|
+
//# sourceMappingURL=chunk-MZ7OCAGA.cjs.map
|
|
3557
|
+
//# sourceMappingURL=chunk-MZ7OCAGA.cjs.map
|