@qawolf/cli 1.3.0 → 1.3.2
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/cli.js +238 -142
- package/package.json +3 -2
- package/skills/qawolf-cli/SKILL.md +5 -0
package/dist/cli.js
CHANGED
|
@@ -8514,7 +8514,7 @@ var require_BufferList = __commonJS((exports, module) => {
|
|
|
8514
8514
|
this.head = this.tail = null;
|
|
8515
8515
|
this.length = 0;
|
|
8516
8516
|
};
|
|
8517
|
-
BufferList.prototype.join = function
|
|
8517
|
+
BufferList.prototype.join = function join33(s3) {
|
|
8518
8518
|
if (this.length === 0)
|
|
8519
8519
|
return "";
|
|
8520
8520
|
var p3 = this.head;
|
|
@@ -23070,7 +23070,7 @@ var require_BufferList2 = __commonJS((exports, module) => {
|
|
|
23070
23070
|
this.head = this.tail = null;
|
|
23071
23071
|
this.length = 0;
|
|
23072
23072
|
};
|
|
23073
|
-
BufferList.prototype.join = function
|
|
23073
|
+
BufferList.prototype.join = function join33(s3) {
|
|
23074
23074
|
if (this.length === 0)
|
|
23075
23075
|
return "";
|
|
23076
23076
|
var p3 = this.head;
|
|
@@ -155181,7 +155181,7 @@ var authMessages = {
|
|
|
155181
155181
|
rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
|
|
155182
155182
|
notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
|
|
155183
155183
|
failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
|
|
155184
|
-
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and
|
|
155184
|
+
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
|
|
155185
155185
|
unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
|
|
155186
155186
|
},
|
|
155187
155187
|
bundle: {
|
|
@@ -155626,6 +155626,15 @@ function makeDefaultFs() {
|
|
|
155626
155626
|
};
|
|
155627
155627
|
}
|
|
155628
155628
|
|
|
155629
|
+
// src/shell/resolveHostUrl.ts
|
|
155630
|
+
var defaultHostUrl = "https://app.qawolf.com";
|
|
155631
|
+
function resolveHostUrl(env2) {
|
|
155632
|
+
const configuredHostUrl = env2["QAWOLF_HOST_URL"]?.trim();
|
|
155633
|
+
if (!configuredHostUrl)
|
|
155634
|
+
return defaultHostUrl;
|
|
155635
|
+
return configuredHostUrl.replace(/\/+$/, "");
|
|
155636
|
+
}
|
|
155637
|
+
|
|
155629
155638
|
// src/domains/auth/resolve.ts
|
|
155630
155639
|
import { Entry as Entry3 } from "@napi-rs/keyring";
|
|
155631
155640
|
|
|
@@ -173377,7 +173386,7 @@ function buildBaseContext(command, signals) {
|
|
|
173377
173386
|
logPath: defaultLogPath(),
|
|
173378
173387
|
...verboseWrite ? { verboseWrite } : {}
|
|
173379
173388
|
});
|
|
173380
|
-
const apiBaseUrl = env2
|
|
173389
|
+
const apiBaseUrl = resolveHostUrl(env2);
|
|
173381
173390
|
return {
|
|
173382
173391
|
ctx: {
|
|
173383
173392
|
ui: createUI(outputMode, {
|
|
@@ -173589,7 +173598,7 @@ function registerAuthCommand(program2, signals) {
|
|
|
173589
173598
|
// package.json
|
|
173590
173599
|
var package_default = {
|
|
173591
173600
|
name: "@qawolf/cli",
|
|
173592
|
-
version: "1.3.
|
|
173601
|
+
version: "1.3.2",
|
|
173593
173602
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
173594
173603
|
keywords: [
|
|
173595
173604
|
"automation",
|
|
@@ -173652,13 +173661,14 @@ var package_default = {
|
|
|
173652
173661
|
"@qawolf/api-contracts": "0.2.0",
|
|
173653
173662
|
"@qawolf/emails": "1.1.1",
|
|
173654
173663
|
"@qawolf/flow-targets": "1.0.0",
|
|
173655
|
-
"@qawolf/flows": "0.1.
|
|
173664
|
+
"@qawolf/flows": "0.1.4",
|
|
173656
173665
|
"@qawolf/testkit": "1.1.1",
|
|
173657
173666
|
appium: "2.11.3",
|
|
173658
173667
|
"appium-uiautomator2-driver": "3.7.0",
|
|
173659
173668
|
"appium-xcuitest-driver": "9.10.5",
|
|
173660
173669
|
commander: "14.0.3",
|
|
173661
173670
|
"env-paths": "4.0.0",
|
|
173671
|
+
"expect-webdriverio": "5.6.5",
|
|
173662
173672
|
picomatch: "4.0.4",
|
|
173663
173673
|
pino: "10.3.1",
|
|
173664
173674
|
playwright: "1.58.2",
|
|
@@ -175132,13 +175142,14 @@ import { createHash } from "node:crypto";
|
|
|
175132
175142
|
import { join as join12, resolve as resolve3 } from "node:path";
|
|
175133
175143
|
|
|
175134
175144
|
// src/generated/dependencyVersions.ts
|
|
175135
|
-
var flowsVersion = "0.1.
|
|
175145
|
+
var flowsVersion = "0.1.4";
|
|
175136
175146
|
var playwrightVersion = "1.58.2";
|
|
175137
175147
|
var emailsVersion = "1.1.1";
|
|
175138
175148
|
var testkitVersion = "1.1.1";
|
|
175139
175149
|
var appiumVersion = "2.11.3";
|
|
175140
175150
|
var appiumXcuitestDriverVersion = "9.10.5";
|
|
175141
175151
|
var appiumUiautomator2DriverVersion = "3.7.0";
|
|
175152
|
+
var expectWebdriverioVersion = "5.6.5";
|
|
175142
175153
|
|
|
175143
175154
|
// src/domains/runtimeEnv/pinnedPackages.ts
|
|
175144
175155
|
var pinnedPackages = [
|
|
@@ -175151,7 +175162,8 @@ var pinnedPackages = [
|
|
|
175151
175162
|
{
|
|
175152
175163
|
name: "appium-uiautomator2-driver",
|
|
175153
175164
|
version: appiumUiautomator2DriverVersion
|
|
175154
|
-
}
|
|
175165
|
+
},
|
|
175166
|
+
{ name: "expect-webdriverio", version: expectWebdriverioVersion }
|
|
175155
175167
|
];
|
|
175156
175168
|
|
|
175157
175169
|
// src/domains/runtimeEnv/managedEnvDir.ts
|
|
@@ -180059,7 +180071,7 @@ async function defaultRunWebFlowDeps(cwd = process.cwd(), signals) {
|
|
|
180059
180071
|
|
|
180060
180072
|
// src/domains/runtimeEnv/prepareRunDir.ts
|
|
180061
180073
|
import { createHash as createHash3 } from "node:crypto";
|
|
180062
|
-
import { basename as basename4, dirname as dirname10, join as
|
|
180074
|
+
import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve8, sep as sep4 } from "node:path";
|
|
180063
180075
|
|
|
180064
180076
|
// src/shell/copyDir.ts
|
|
180065
180077
|
import { cp, readdir as readdir2 } from "node:fs/promises";
|
|
@@ -180100,6 +180112,9 @@ async function readPackageJson(pkgPath, fs3) {
|
|
|
180100
180112
|
}
|
|
180101
180113
|
|
|
180102
180114
|
// src/domains/runtimeEnv/innerHop.ts
|
|
180115
|
+
import { join as join27 } from "node:path";
|
|
180116
|
+
|
|
180117
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180103
180118
|
import { dirname as dirname8, join as join26 } from "node:path";
|
|
180104
180119
|
|
|
180105
180120
|
// src/domains/runtimeEnv/symlinkDir.ts
|
|
@@ -180109,25 +180124,35 @@ async function createDirSymlink(source, target) {
|
|
|
180109
180124
|
await symlink(source, target, linkType);
|
|
180110
180125
|
}
|
|
180111
180126
|
|
|
180112
|
-
// src/domains/runtimeEnv/
|
|
180113
|
-
async function
|
|
180114
|
-
const { depsRoot,
|
|
180127
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180128
|
+
async function linkPinnedPackages(args) {
|
|
180129
|
+
const { depsRoot, nodeModulesDir, fs: fs3 } = args;
|
|
180115
180130
|
const managedModules = join26(depsRoot, "node_modules");
|
|
180116
|
-
|
|
180117
|
-
await fs3.mkdir(innerModules, { recursive: true });
|
|
180131
|
+
await fs3.mkdir(nodeModulesDir, { recursive: true });
|
|
180118
180132
|
for (const { name } of pinnedPackages) {
|
|
180119
180133
|
const segments = name.split("/");
|
|
180120
|
-
const target = join26(
|
|
180134
|
+
const target = join26(nodeModulesDir, ...segments);
|
|
180121
180135
|
if (segments.length > 1) {
|
|
180122
180136
|
await fs3.mkdir(dirname8(target), { recursive: true });
|
|
180123
180137
|
}
|
|
180138
|
+
await fs3.rm(target, { recursive: true, force: true });
|
|
180124
180139
|
await createDirSymlink(join26(managedModules, ...segments), target);
|
|
180125
180140
|
}
|
|
180126
180141
|
}
|
|
180127
180142
|
|
|
180143
|
+
// src/domains/runtimeEnv/innerHop.ts
|
|
180144
|
+
async function populateInnerHop(args) {
|
|
180145
|
+
const { depsRoot, execDir, fs: fs3 } = args;
|
|
180146
|
+
await linkPinnedPackages({
|
|
180147
|
+
depsRoot,
|
|
180148
|
+
nodeModulesDir: join27(execDir, "node_modules"),
|
|
180149
|
+
fs: fs3
|
|
180150
|
+
});
|
|
180151
|
+
}
|
|
180152
|
+
|
|
180128
180153
|
// src/domains/runtimeEnv/outerHop.ts
|
|
180129
180154
|
import { lstat } from "node:fs/promises";
|
|
180130
|
-
import { dirname as dirname9, join as
|
|
180155
|
+
import { dirname as dirname9, join as join28 } from "node:path";
|
|
180131
180156
|
var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
|
|
180132
180157
|
async function populateOuterHop(args) {
|
|
180133
180158
|
const { projectDir, runDir, fs: fs3 } = args;
|
|
@@ -180137,23 +180162,28 @@ async function populateOuterHop(args) {
|
|
|
180137
180162
|
const depNames = Object.keys(installableDeps);
|
|
180138
180163
|
const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
|
|
180139
180164
|
if (satisfying !== undefined) {
|
|
180140
|
-
await createDirSymlink(satisfying,
|
|
180165
|
+
await createDirSymlink(satisfying, join28(runDir, "node_modules"));
|
|
180141
180166
|
return { mode: "symlink", nodeModulesDir: satisfying };
|
|
180142
180167
|
}
|
|
180143
180168
|
if (depNames.length === 0)
|
|
180144
180169
|
return { mode: "none" };
|
|
180145
180170
|
args.onInstallStart?.(depNames.length);
|
|
180146
180171
|
await installOuterDeps(runDir, installableDeps, fs3, args.install ?? spawnNpmInstall);
|
|
180172
|
+
await linkPinnedPackages({
|
|
180173
|
+
depsRoot: args.depsRoot,
|
|
180174
|
+
nodeModulesDir: join28(runDir, "node_modules"),
|
|
180175
|
+
fs: fs3
|
|
180176
|
+
});
|
|
180147
180177
|
return { mode: "install", depCount: depNames.length, rejected };
|
|
180148
180178
|
}
|
|
180149
180179
|
async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
|
|
180150
180180
|
const rejected = [];
|
|
180151
180181
|
let dir = startDir;
|
|
180152
180182
|
while (true) {
|
|
180153
|
-
const candidate =
|
|
180183
|
+
const candidate = join28(dir, "node_modules");
|
|
180154
180184
|
const stats = await lstatSafe(candidate);
|
|
180155
180185
|
if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
180156
|
-
const missing = depNames.filter((name) => !fs3.existsSync(
|
|
180186
|
+
const missing = depNames.filter((name) => !fs3.existsSync(join28(candidate, name, "package.json")));
|
|
180157
180187
|
if (missing.length === 0)
|
|
180158
180188
|
return { satisfying: candidate, rejected };
|
|
180159
180189
|
rejected.push({ dir: candidate, missing });
|
|
@@ -180174,7 +180204,7 @@ async function lstatSafe(path3) {
|
|
|
180174
180204
|
async function readInstallableDeps(projectDir, fs3) {
|
|
180175
180205
|
let content;
|
|
180176
180206
|
try {
|
|
180177
|
-
content = await fs3.readFile(
|
|
180207
|
+
content = await fs3.readFile(join28(projectDir, "package.json"));
|
|
180178
180208
|
} catch {
|
|
180179
180209
|
return {};
|
|
180180
180210
|
}
|
|
@@ -180192,8 +180222,8 @@ async function readInstallableDeps(projectDir, fs3) {
|
|
|
180192
180222
|
return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
|
|
180193
180223
|
}
|
|
180194
180224
|
async function installOuterDeps(runDir, deps, fs3, install) {
|
|
180195
|
-
await fs3.writeFile(
|
|
180196
|
-
await fs3.writeFile(
|
|
180225
|
+
await fs3.writeFile(join28(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
|
|
180226
|
+
await fs3.writeFile(join28(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
|
|
180197
180227
|
`);
|
|
180198
180228
|
const result = await install(runDir);
|
|
180199
180229
|
if (result.exitCode !== 0) {
|
|
@@ -180207,10 +180237,10 @@ async function prepareRunDir(args) {
|
|
|
180207
180237
|
const { files, projectDir, depsRoot, runRoot } = args;
|
|
180208
180238
|
const fs3 = args.fs ?? makeDefaultFs();
|
|
180209
180239
|
const runId = buildRunId(projectDir, files);
|
|
180210
|
-
const runDir =
|
|
180240
|
+
const runDir = join29(runRoot, runId);
|
|
180211
180241
|
await fs3.rm(runDir, { recursive: true, force: true });
|
|
180212
180242
|
await fs3.mkdir(runDir, { recursive: true });
|
|
180213
|
-
const execDir =
|
|
180243
|
+
const execDir = join29(runDir, "exec");
|
|
180214
180244
|
await fs3.mkdir(execDir, { recursive: true });
|
|
180215
180245
|
await populateInnerHop({ depsRoot, execDir, fs: fs3 });
|
|
180216
180246
|
const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs3 });
|
|
@@ -180220,6 +180250,7 @@ async function prepareRunDir(args) {
|
|
|
180220
180250
|
const outerHop = await populateOuterHop({
|
|
180221
180251
|
projectDir,
|
|
180222
180252
|
runDir,
|
|
180253
|
+
depsRoot,
|
|
180223
180254
|
fs: fs3,
|
|
180224
180255
|
...args.onInstallStart !== undefined ? { onInstallStart: args.onInstallStart } : {}
|
|
180225
180256
|
});
|
|
@@ -180247,37 +180278,37 @@ async function stageFlowFiles(args) {
|
|
|
180247
180278
|
if (files.length > 1) {
|
|
180248
180279
|
return Promise.all(files.map(async (f3) => {
|
|
180249
180280
|
const dirHash = createHash3("sha256").update(dirname10(f3)).digest("hex").slice(0, 8);
|
|
180250
|
-
const subDir =
|
|
180281
|
+
const subDir = join29(execDir, dirHash);
|
|
180251
180282
|
await fs3.mkdir(subDir, { recursive: true });
|
|
180252
|
-
const dest =
|
|
180283
|
+
const dest = join29(subDir, basename4(f3));
|
|
180253
180284
|
await fs3.copyFile(f3, dest);
|
|
180254
180285
|
return dest;
|
|
180255
180286
|
}));
|
|
180256
180287
|
}
|
|
180257
|
-
await Promise.all(files.map((f3) => fs3.copyFile(f3,
|
|
180258
|
-
return files.map((f3) =>
|
|
180288
|
+
await Promise.all(files.map((f3) => fs3.copyFile(f3, join29(execDir, basename4(f3)))));
|
|
180289
|
+
return files.map((f3) => join29(execDir, basename4(f3)));
|
|
180259
180290
|
}
|
|
180260
180291
|
function remapPath(file2, projectDir, execDir) {
|
|
180261
180292
|
if (file2 === projectDir)
|
|
180262
180293
|
return execDir;
|
|
180263
180294
|
if (file2.startsWith(projectDir + sep4)) {
|
|
180264
|
-
return
|
|
180295
|
+
return join29(execDir, file2.slice(projectDir.length + 1));
|
|
180265
180296
|
}
|
|
180266
180297
|
return file2;
|
|
180267
180298
|
}
|
|
180268
180299
|
|
|
180269
180300
|
// src/shell/testkit.ts
|
|
180270
|
-
import { join as
|
|
180301
|
+
import { join as join30 } from "node:path";
|
|
180271
180302
|
function notAvailableLocally(name) {
|
|
180272
180303
|
throw new Error(runnerMessages.notAvailableLocally(name));
|
|
180273
180304
|
}
|
|
180274
180305
|
async function loadSdkDeps(cwd) {
|
|
180275
|
-
const testkitDir =
|
|
180306
|
+
const testkitDir = join30(cwd, "node_modules", "@qawolf", "testkit");
|
|
180276
180307
|
try {
|
|
180277
180308
|
const clientMod = await import(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
|
|
180278
180309
|
if (typeof clientMod.createTestkitClient !== "function")
|
|
180279
180310
|
throw new Error("createTestkitClient is not a function");
|
|
180280
|
-
const scopeMod = await import(
|
|
180311
|
+
const scopeMod = await import(join30(testkitDir, "dist", "clientScope.js"));
|
|
180281
180312
|
if (typeof scopeMod.configureTestkitClient !== "function")
|
|
180282
180313
|
throw new Error("configureTestkitClient is not a function");
|
|
180283
180314
|
return {
|
|
@@ -180334,9 +180365,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
|
|
|
180334
180365
|
}
|
|
180335
180366
|
|
|
180336
180367
|
// src/commands/flows/envVarDeps.ts
|
|
180337
|
-
import { join as
|
|
180368
|
+
import { join as join31 } from "node:path";
|
|
180338
180369
|
function envFilePath(envDir) {
|
|
180339
|
-
return
|
|
180370
|
+
return join31(envDir, ".env");
|
|
180340
180371
|
}
|
|
180341
180372
|
async function readEnv(envDir, fs3) {
|
|
180342
180373
|
try {
|
|
@@ -180445,7 +180476,7 @@ async function createFlowRuntimeDeps({
|
|
|
180445
180476
|
}
|
|
180446
180477
|
|
|
180447
180478
|
// src/shell/appium/createAppiumServer.ts
|
|
180448
|
-
import { join as
|
|
180479
|
+
import { join as join32 } from "node:path";
|
|
180449
180480
|
|
|
180450
180481
|
// src/shell/appium/spawnAppium.ts
|
|
180451
180482
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -180520,7 +180551,7 @@ async function createAppiumServer(envDir, signals, params) {
|
|
|
180520
180551
|
const spawnFn = params?.deps?.spawn ?? defaultSpawnAppium;
|
|
180521
180552
|
const findFreePortFn = params?.deps?.findFreePort ?? findFreePort;
|
|
180522
180553
|
const resolveAppiumBinFn = params?.deps?.resolveAppiumBin ?? resolveAppiumBin;
|
|
180523
|
-
const appiumHome2 = params?.options?.appiumHome ??
|
|
180554
|
+
const appiumHome2 = params?.options?.appiumHome ?? join32(envPaths("qawolf").data, "appium");
|
|
180524
180555
|
const timeoutMs3 = params?.options?.startTimeoutMs ?? defaultStartTimeoutMs;
|
|
180525
180556
|
const bin = resolveAppiumBinFn(envDir);
|
|
180526
180557
|
const port = await findFreePortFn();
|
|
@@ -180939,7 +180970,7 @@ function formatError2(browser, result) {
|
|
|
180939
180970
|
}
|
|
180940
180971
|
|
|
180941
180972
|
// src/domains/runner/runAndroidFlow.ts
|
|
180942
|
-
import
|
|
180973
|
+
import path12 from "node:path";
|
|
180943
180974
|
|
|
180944
180975
|
// src/shell/appium/createAndroidLaunchContext.ts
|
|
180945
180976
|
import { writeFile } from "node:fs/promises";
|
|
@@ -181285,66 +181316,6 @@ function createRunner({
|
|
|
181285
181316
|
}
|
|
181286
181317
|
|
|
181287
181318
|
// src/domains/runner/runWebFlowUtils.ts
|
|
181288
|
-
import crypto from "node:crypto";
|
|
181289
|
-
import os6 from "node:os";
|
|
181290
|
-
import path12 from "node:path";
|
|
181291
|
-
function createLaunch({
|
|
181292
|
-
browsers,
|
|
181293
|
-
contextSetup,
|
|
181294
|
-
launchBrowserOpts,
|
|
181295
|
-
signals,
|
|
181296
|
-
timeout,
|
|
181297
|
-
traceMode = "off",
|
|
181298
|
-
tracePath
|
|
181299
|
-
}) {
|
|
181300
|
-
const openBrowsers = [];
|
|
181301
|
-
const openContexts = [];
|
|
181302
|
-
const tracingEnabled = traceMode !== "off";
|
|
181303
|
-
let closed = false;
|
|
181304
|
-
const closeAll = async () => {
|
|
181305
|
-
if (closed)
|
|
181306
|
-
return;
|
|
181307
|
-
closed = true;
|
|
181308
|
-
if (tracingEnabled && tracePath !== undefined) {
|
|
181309
|
-
await Promise.allSettled(openContexts.map((c2) => c2.tracing.stop({ path: tracePath })));
|
|
181310
|
-
}
|
|
181311
|
-
await Promise.allSettled(openContexts.map((c2) => c2.close()));
|
|
181312
|
-
await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
|
|
181313
|
-
};
|
|
181314
|
-
const unregister = signals.register(closeAll);
|
|
181315
|
-
const trackContext = async (ctx) => {
|
|
181316
|
-
openContexts.push(ctx);
|
|
181317
|
-
if (tracingEnabled) {
|
|
181318
|
-
await ctx.tracing.start({ screenshots: true, snapshots: true });
|
|
181319
|
-
}
|
|
181320
|
-
};
|
|
181321
|
-
const launch = async (launchOpts) => {
|
|
181322
|
-
const browserName = normalizeBrowserName(launchOpts?.browser);
|
|
181323
|
-
const bt2 = browsers[browserName];
|
|
181324
|
-
if (launchOpts?.persistentContext === true) {
|
|
181325
|
-
const userDataDir = launchOpts.userDataDir ?? path12.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
|
|
181326
|
-
const context2 = await bt2.launchPersistentContext(userDataDir, {
|
|
181327
|
-
...launchBrowserOpts,
|
|
181328
|
-
...contextSetup
|
|
181329
|
-
});
|
|
181330
|
-
context2.setDefaultTimeout(timeout);
|
|
181331
|
-
await trackContext(context2);
|
|
181332
|
-
const page = await context2.newPage();
|
|
181333
|
-
return { browserType: browserName, context: context2, page };
|
|
181334
|
-
}
|
|
181335
|
-
const browser = await bt2.launch(launchBrowserOpts);
|
|
181336
|
-
openBrowsers.push(browser);
|
|
181337
|
-
const context = await browser.newContext(contextSetup);
|
|
181338
|
-
context.setDefaultTimeout(timeout);
|
|
181339
|
-
await trackContext(context);
|
|
181340
|
-
return { browser, browserType: browserName, context };
|
|
181341
|
-
};
|
|
181342
|
-
const cleanup = async () => {
|
|
181343
|
-
unregister();
|
|
181344
|
-
await closeAll();
|
|
181345
|
-
};
|
|
181346
|
-
return { launch, cleanup };
|
|
181347
|
-
}
|
|
181348
181319
|
function normalizeBrowserName(browser) {
|
|
181349
181320
|
if (browser === "chrome" || browser === "msedge")
|
|
181350
181321
|
return "chromium";
|
|
@@ -181384,7 +181355,7 @@ async function runAndroidFlow({
|
|
|
181384
181355
|
throw new Error("runAndroidFlow: legacy flow functions are not supported; use flow() from @qawolf/flows/android");
|
|
181385
181356
|
}
|
|
181386
181357
|
} catch (err) {
|
|
181387
|
-
const flowName2 =
|
|
181358
|
+
const flowName2 = path12.basename(flowPath, path12.extname(flowPath));
|
|
181388
181359
|
return {
|
|
181389
181360
|
passed: false,
|
|
181390
181361
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181452,13 +181423,13 @@ async function runAndroidFlow({
|
|
|
181452
181423
|
}
|
|
181453
181424
|
|
|
181454
181425
|
// src/domains/runner/runWebFlow.ts
|
|
181455
|
-
import
|
|
181426
|
+
import path17 from "node:path";
|
|
181456
181427
|
|
|
181457
181428
|
// src/domains/runner/initFlowRuntime.ts
|
|
181458
|
-
import
|
|
181429
|
+
import path13 from "node:path";
|
|
181459
181430
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
181460
181431
|
async function runnerPathInDir(dir, fs9) {
|
|
181461
|
-
const pkgPath =
|
|
181432
|
+
const pkgPath = path13.join(dir, "node_modules", "@qawolf", "flows", "package.json");
|
|
181462
181433
|
try {
|
|
181463
181434
|
const pkg = JSON.parse(await fs9.readFile(pkgPath));
|
|
181464
181435
|
const entry = pkg.exports?.["./_runner"];
|
|
@@ -181466,7 +181437,7 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181466
181437
|
if (typeof importPath !== "string") {
|
|
181467
181438
|
throw new Error(`@qawolf/flows at ${pkgPath} does not export "./_runner" with an "import" condition`);
|
|
181468
181439
|
}
|
|
181469
|
-
return
|
|
181440
|
+
return path13.resolve(path13.dirname(pkgPath), importPath);
|
|
181470
181441
|
} catch (err) {
|
|
181471
181442
|
if (!isNoEntError(err))
|
|
181472
181443
|
throw err;
|
|
@@ -181474,12 +181445,12 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181474
181445
|
}
|
|
181475
181446
|
}
|
|
181476
181447
|
async function findFlowsRunnerPath(flowPath, fs9) {
|
|
181477
|
-
let dir =
|
|
181448
|
+
let dir = path13.dirname(flowPath);
|
|
181478
181449
|
while (true) {
|
|
181479
181450
|
const result = await runnerPathInDir(dir, fs9);
|
|
181480
181451
|
if (result !== undefined)
|
|
181481
181452
|
return result;
|
|
181482
|
-
const parent2 =
|
|
181453
|
+
const parent2 = path13.dirname(dir);
|
|
181483
181454
|
if (parent2 === dir)
|
|
181484
181455
|
throw new Error(`@qawolf/flows not found in node_modules above: ${flowPath}`);
|
|
181485
181456
|
dir = parent2;
|
|
@@ -181511,7 +181482,7 @@ async function doInit(flowPath, timeout, fs9, depsRoot) {
|
|
|
181511
181482
|
});
|
|
181512
181483
|
}
|
|
181513
181484
|
function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
181514
|
-
const startDir =
|
|
181485
|
+
const startDir = path13.dirname(flowPath);
|
|
181515
181486
|
let p3 = initCache.get(startDir);
|
|
181516
181487
|
if (!p3) {
|
|
181517
181488
|
p3 = doInit(flowPath, options.timeout, fs9, options.depsRoot);
|
|
@@ -181520,13 +181491,137 @@ function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
|
181520
181491
|
return p3;
|
|
181521
181492
|
}
|
|
181522
181493
|
|
|
181523
|
-
// src/domains/runner/
|
|
181494
|
+
// src/domains/runner/createLaunch.ts
|
|
181495
|
+
import crypto from "node:crypto";
|
|
181496
|
+
import os6 from "node:os";
|
|
181524
181497
|
import path15 from "node:path";
|
|
181498
|
+
|
|
181499
|
+
// src/domains/runner/web/artifactPaths.ts
|
|
181500
|
+
import path14 from "node:path";
|
|
181501
|
+
function indexedArtifactPath(basePath, index2) {
|
|
181502
|
+
if (index2 === 0)
|
|
181503
|
+
return basePath;
|
|
181504
|
+
const ext = path14.extname(basePath);
|
|
181505
|
+
const base = path14.basename(basePath, ext);
|
|
181506
|
+
return path14.join(path14.dirname(basePath), `${base}-${index2 + 1}${ext}`);
|
|
181507
|
+
}
|
|
181508
|
+
function createContextArtifacts(contextSetup, tracePath) {
|
|
181509
|
+
let contextIndex = 0;
|
|
181510
|
+
const harPaths = [];
|
|
181511
|
+
const tracePaths = [];
|
|
181512
|
+
const nextSetup = (overrides = {}) => {
|
|
181513
|
+
const index2 = contextIndex;
|
|
181514
|
+
contextIndex += 1;
|
|
181515
|
+
const setup = { ...contextSetup, ...overrides };
|
|
181516
|
+
if (contextSetup.recordHar !== undefined && overrides.recordHar === undefined) {
|
|
181517
|
+
setup.recordHar = {
|
|
181518
|
+
...contextSetup.recordHar,
|
|
181519
|
+
path: indexedArtifactPath(contextSetup.recordHar.path, index2)
|
|
181520
|
+
};
|
|
181521
|
+
}
|
|
181522
|
+
if (setup.recordHar !== undefined)
|
|
181523
|
+
harPaths.push(setup.recordHar.path);
|
|
181524
|
+
return { setup, index: index2 };
|
|
181525
|
+
};
|
|
181526
|
+
const nextTracePath = (index2) => {
|
|
181527
|
+
if (tracePath === undefined)
|
|
181528
|
+
return;
|
|
181529
|
+
const contextTracePath = indexedArtifactPath(tracePath, index2);
|
|
181530
|
+
tracePaths.push(contextTracePath);
|
|
181531
|
+
return contextTracePath;
|
|
181532
|
+
};
|
|
181533
|
+
const artifactPaths = () => ({
|
|
181534
|
+
harPaths: [...harPaths],
|
|
181535
|
+
tracePaths: [...tracePaths]
|
|
181536
|
+
});
|
|
181537
|
+
return { nextSetup, nextTracePath, artifactPaths };
|
|
181538
|
+
}
|
|
181539
|
+
|
|
181540
|
+
// src/domains/runner/createLaunch.ts
|
|
181541
|
+
function createLaunch({
|
|
181542
|
+
browsers,
|
|
181543
|
+
contextSetup,
|
|
181544
|
+
launchBrowserOpts,
|
|
181545
|
+
signals,
|
|
181546
|
+
timeout,
|
|
181547
|
+
traceMode = "off",
|
|
181548
|
+
tracePath
|
|
181549
|
+
}) {
|
|
181550
|
+
const openBrowsers = [];
|
|
181551
|
+
const openContexts = [];
|
|
181552
|
+
const traceByContext = new Map;
|
|
181553
|
+
const tracingEnabled = traceMode !== "off";
|
|
181554
|
+
const { nextSetup, nextTracePath, artifactPaths } = createContextArtifacts(contextSetup, tracePath);
|
|
181555
|
+
let closePromise;
|
|
181556
|
+
const closeAll = () => {
|
|
181557
|
+
closePromise ??= (async () => {
|
|
181558
|
+
if (tracingEnabled) {
|
|
181559
|
+
await Promise.allSettled(openContexts.map((c2) => {
|
|
181560
|
+
const contextTracePath = traceByContext.get(c2);
|
|
181561
|
+
return contextTracePath === undefined ? Promise.resolve() : c2.tracing.stop({ path: contextTracePath });
|
|
181562
|
+
}));
|
|
181563
|
+
}
|
|
181564
|
+
await Promise.allSettled(openContexts.map((c2) => c2.close()));
|
|
181565
|
+
await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
|
|
181566
|
+
})();
|
|
181567
|
+
return closePromise;
|
|
181568
|
+
};
|
|
181569
|
+
const unregister = signals.register(closeAll);
|
|
181570
|
+
const trackContext = async (ctx, index2) => {
|
|
181571
|
+
openContexts.push(ctx);
|
|
181572
|
+
if (tracingEnabled) {
|
|
181573
|
+
const contextTracePath = nextTracePath(index2);
|
|
181574
|
+
if (contextTracePath !== undefined) {
|
|
181575
|
+
traceByContext.set(ctx, contextTracePath);
|
|
181576
|
+
}
|
|
181577
|
+
await ctx.tracing.start({ screenshots: true, snapshots: true });
|
|
181578
|
+
}
|
|
181579
|
+
};
|
|
181580
|
+
const instrumentBrowser = (browser) => {
|
|
181581
|
+
const originalNewContext = browser.newContext.bind(browser);
|
|
181582
|
+
browser.newContext = async (opts) => {
|
|
181583
|
+
const { setup, index: index2 } = nextSetup(opts);
|
|
181584
|
+
const context = await originalNewContext(setup);
|
|
181585
|
+
context.setDefaultTimeout(timeout);
|
|
181586
|
+
await trackContext(context, index2);
|
|
181587
|
+
return context;
|
|
181588
|
+
};
|
|
181589
|
+
};
|
|
181590
|
+
const launch = async (launchOpts) => {
|
|
181591
|
+
const browserName = normalizeBrowserName(launchOpts?.browser);
|
|
181592
|
+
const bt2 = browsers[browserName];
|
|
181593
|
+
if (launchOpts?.persistentContext === true) {
|
|
181594
|
+
const userDataDir = launchOpts.userDataDir ?? path15.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
|
|
181595
|
+
const { setup, index: index2 } = nextSetup();
|
|
181596
|
+
const context2 = await bt2.launchPersistentContext(userDataDir, {
|
|
181597
|
+
...launchBrowserOpts,
|
|
181598
|
+
...setup
|
|
181599
|
+
});
|
|
181600
|
+
context2.setDefaultTimeout(timeout);
|
|
181601
|
+
await trackContext(context2, index2);
|
|
181602
|
+
const page = await context2.newPage();
|
|
181603
|
+
return { browserType: browserName, context: context2, page };
|
|
181604
|
+
}
|
|
181605
|
+
const browser = await bt2.launch(launchBrowserOpts);
|
|
181606
|
+
openBrowsers.push(browser);
|
|
181607
|
+
instrumentBrowser(browser);
|
|
181608
|
+
const context = await browser.newContext({});
|
|
181609
|
+
return { browser, browserType: browserName, context };
|
|
181610
|
+
};
|
|
181611
|
+
const cleanup = async () => {
|
|
181612
|
+
unregister();
|
|
181613
|
+
await closeAll();
|
|
181614
|
+
};
|
|
181615
|
+
return { launch, cleanup, artifactPaths };
|
|
181616
|
+
}
|
|
181617
|
+
|
|
181618
|
+
// src/domains/runner/web/contextSetup.ts
|
|
181619
|
+
import path16 from "node:path";
|
|
181525
181620
|
function harFlowPath(outputDir, flowName) {
|
|
181526
|
-
return
|
|
181621
|
+
return path16.join(outputDir, "har", `${flowName}.har`);
|
|
181527
181622
|
}
|
|
181528
181623
|
function traceFlowPath(outputDir, flowName) {
|
|
181529
|
-
return
|
|
181624
|
+
return path16.join(outputDir, "trace", `${flowName}.zip`);
|
|
181530
181625
|
}
|
|
181531
181626
|
function buildHarContextOpts(harPath, harContent) {
|
|
181532
181627
|
return {
|
|
@@ -181542,7 +181637,7 @@ async function initHar(fs9, options, flowName) {
|
|
|
181542
181637
|
if (harMode === "off")
|
|
181543
181638
|
return { harMode, harPath: undefined };
|
|
181544
181639
|
const harPath = harFlowPath(options.outputDir, flowName);
|
|
181545
|
-
await fs9.mkdir(
|
|
181640
|
+
await fs9.mkdir(path16.dirname(harPath), { recursive: true });
|
|
181546
181641
|
return { harMode, harPath };
|
|
181547
181642
|
}
|
|
181548
181643
|
async function initTrace(fs9, options, flowName) {
|
|
@@ -181550,7 +181645,7 @@ async function initTrace(fs9, options, flowName) {
|
|
|
181550
181645
|
if (traceMode === "off")
|
|
181551
181646
|
return { traceMode, tracePath: undefined };
|
|
181552
181647
|
const tracePath = traceFlowPath(options.outputDir, flowName);
|
|
181553
|
-
await fs9.mkdir(
|
|
181648
|
+
await fs9.mkdir(path16.dirname(tracePath), { recursive: true });
|
|
181554
181649
|
return { traceMode, tracePath };
|
|
181555
181650
|
}
|
|
181556
181651
|
async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
@@ -181563,7 +181658,7 @@ async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
|
181563
181658
|
}
|
|
181564
181659
|
}
|
|
181565
181660
|
function buildContextSetup(videoSize, options, harPath) {
|
|
181566
|
-
const videosDir = options.artifactDir ??
|
|
181661
|
+
const videosDir = options.artifactDir ?? path16.join(options.outputDir, "videos");
|
|
181567
181662
|
const harOpts = harPath !== undefined ? buildHarContextOpts(harPath, options.harContent ?? "omit") : {};
|
|
181568
181663
|
return options.video !== "off" ? {
|
|
181569
181664
|
viewport: videoSize,
|
|
@@ -181599,7 +181694,7 @@ async function runWebFlow({
|
|
|
181599
181694
|
depsRoot: deps.depsRoot
|
|
181600
181695
|
});
|
|
181601
181696
|
} catch (err) {
|
|
181602
|
-
const flowName2 =
|
|
181697
|
+
const flowName2 = path17.basename(flowPath, path17.extname(flowPath));
|
|
181603
181698
|
return {
|
|
181604
181699
|
passed: false,
|
|
181605
181700
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181608,7 +181703,7 @@ async function runWebFlow({
|
|
|
181608
181703
|
};
|
|
181609
181704
|
}
|
|
181610
181705
|
const isLegacy = typeof exported === "function";
|
|
181611
|
-
const flowName = isLegacy ?
|
|
181706
|
+
const flowName = isLegacy ? path17.basename(flowPath, path17.extname(flowPath)) : exported.name;
|
|
181612
181707
|
const runFn = isLegacy ? exported : exported.run;
|
|
181613
181708
|
const { harMode, harPath } = await initHar(deps.fs, options, flowName);
|
|
181614
181709
|
const { traceMode, tracePath } = await initTrace(deps.fs, options, flowName);
|
|
@@ -181619,7 +181714,7 @@ async function runWebFlow({
|
|
|
181619
181714
|
slowMo: options.slowMo,
|
|
181620
181715
|
...options.executablePath !== undefined ? { executablePath: options.executablePath } : {}
|
|
181621
181716
|
};
|
|
181622
|
-
const { launch, cleanup } = createLaunch({
|
|
181717
|
+
const { launch, cleanup, artifactPaths } = createLaunch({
|
|
181623
181718
|
browsers: {
|
|
181624
181719
|
chromium: deps.chromium,
|
|
181625
181720
|
firefox: deps.firefox,
|
|
@@ -181661,25 +181756,26 @@ async function runWebFlow({
|
|
|
181661
181756
|
} finally {
|
|
181662
181757
|
await cleanup();
|
|
181663
181758
|
const warn = (message) => deps.logger?.warn(message);
|
|
181664
|
-
|
|
181665
|
-
|
|
181759
|
+
const assigned = artifactPaths();
|
|
181760
|
+
for (const contextHarPath of assigned.harPaths) {
|
|
181761
|
+
await maybeCleanupHar(deps.fs, contextHarPath, passed, harMode, warn);
|
|
181666
181762
|
}
|
|
181667
|
-
|
|
181668
|
-
await maybeCleanupTrace(deps.fs,
|
|
181763
|
+
for (const contextTracePath of assigned.tracePaths) {
|
|
181764
|
+
await maybeCleanupTrace(deps.fs, contextTracePath, passed, traceMode, warn);
|
|
181669
181765
|
}
|
|
181670
181766
|
}
|
|
181671
181767
|
}
|
|
181672
181768
|
|
|
181673
181769
|
// src/shell/embeddedWorkerCli.ts
|
|
181674
181770
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
181675
|
-
import { basename as basename6, join as
|
|
181771
|
+
import { basename as basename6, join as join33 } from "node:path";
|
|
181676
181772
|
var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
|
|
181677
181773
|
function extractEmbeddedWorkerCli() {
|
|
181678
181774
|
const assetPath = process.env[embeddedCliPathEnv];
|
|
181679
181775
|
if (assetPath === undefined || assetPath === "")
|
|
181680
181776
|
return;
|
|
181681
|
-
const dir =
|
|
181682
|
-
const dest =
|
|
181777
|
+
const dir = join33(getDataDir(), "worker");
|
|
181778
|
+
const dest = join33(dir, basename6(assetPath));
|
|
181683
181779
|
if (existsSync3(dest))
|
|
181684
181780
|
return dest;
|
|
181685
181781
|
mkdirSync3(dir, { recursive: true });
|
|
@@ -181982,7 +182078,7 @@ function createCompositeReporter(reporters) {
|
|
|
181982
182078
|
};
|
|
181983
182079
|
}
|
|
181984
182080
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181985
|
-
import { join as
|
|
182081
|
+
import { join as join34 } from "node:path";
|
|
181986
182082
|
|
|
181987
182083
|
// src/shell/reporter/junitXml.ts
|
|
181988
182084
|
function escapeXml(str) {
|
|
@@ -182021,7 +182117,7 @@ ${suites}`;
|
|
|
182021
182117
|
|
|
182022
182118
|
// src/shell/reporter/createJUnitReporter.ts
|
|
182023
182119
|
function resolveJUnitOutputPath(junit, outputDir) {
|
|
182024
|
-
return typeof junit === "string" && junit.trim() !== "" ? junit :
|
|
182120
|
+
return typeof junit === "string" && junit.trim() !== "" ? junit : join34(outputDir, "junit-report.xml");
|
|
182025
182121
|
}
|
|
182026
182122
|
function createJUnitReporter(deps) {
|
|
182027
182123
|
const flows = [];
|
|
@@ -182108,11 +182204,11 @@ function buildFlowsRunDeps(args) {
|
|
|
182108
182204
|
|
|
182109
182205
|
// src/commands/flows/loadEnvFile.ts
|
|
182110
182206
|
import { readFile } from "node:fs/promises";
|
|
182111
|
-
import { join as
|
|
182207
|
+
import { join as join35 } from "node:path";
|
|
182112
182208
|
async function loadEnvFile(envDir) {
|
|
182113
182209
|
let content;
|
|
182114
182210
|
try {
|
|
182115
|
-
content = await readFile(
|
|
182211
|
+
content = await readFile(join35(envDir, ".env"), "utf8");
|
|
182116
182212
|
} catch (err) {
|
|
182117
182213
|
if (isNoEntError(err))
|
|
182118
182214
|
return;
|
|
@@ -182212,7 +182308,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182212
182308
|
}
|
|
182213
182309
|
|
|
182214
182310
|
// src/commands/flows/hybridRunDefaults.ts
|
|
182215
|
-
import { join as
|
|
182311
|
+
import { join as join36, resolve as resolve9 } from "node:path";
|
|
182216
182312
|
function makeDefaultHybridDeps(fs9) {
|
|
182217
182313
|
return {
|
|
182218
182314
|
expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
|
|
@@ -182231,7 +182327,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182231
182327
|
if (validation !== "ok") {
|
|
182232
182328
|
return { error: validation.error, exitCode: 2 };
|
|
182233
182329
|
}
|
|
182234
|
-
const envDir = resolve9(
|
|
182330
|
+
const envDir = resolve9(join36(".qawolf", flags.env));
|
|
182235
182331
|
const patternArgs = buildPatternArgs(pattern);
|
|
182236
182332
|
const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
|
|
182237
182333
|
let files = await globFlows();
|
|
@@ -182305,7 +182401,7 @@ async function runWorker(signals) {
|
|
|
182305
182401
|
throw new Error("worker subprocess currently supports web flows only");
|
|
182306
182402
|
await configureTestkit(input.resolvedDir);
|
|
182307
182403
|
const fs9 = makeDefaultFs();
|
|
182308
|
-
const apiBaseUrl = process.env
|
|
182404
|
+
const apiBaseUrl = resolveHostUrl(process.env);
|
|
182309
182405
|
const flowRuntimeDeps = await createFlowRuntimeDeps({
|
|
182310
182406
|
envDir: input.resolvedDir,
|
|
182311
182407
|
ctx: { apiBaseUrl, configDir: getConfigDir(), fs: fs9 }
|
|
@@ -182385,7 +182481,7 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182385
182481
|
}
|
|
182386
182482
|
|
|
182387
182483
|
// src/domains/init/init.ts
|
|
182388
|
-
import { dirname as dirname13, join as
|
|
182484
|
+
import { dirname as dirname13, join as join37, relative as relative7 } from "node:path";
|
|
182389
182485
|
|
|
182390
182486
|
// src/domains/init/templates.ts
|
|
182391
182487
|
var qawolfConfigTs = `export default {
|
|
@@ -182423,9 +182519,9 @@ function makeDefaultInitDeps() {
|
|
|
182423
182519
|
async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
|
|
182424
182520
|
ctx.ui.gap();
|
|
182425
182521
|
ctx.ui.intro(initMessages.title);
|
|
182426
|
-
const configPath =
|
|
182427
|
-
const flowPath =
|
|
182428
|
-
const gitignorePath =
|
|
182522
|
+
const configPath = join37(deps.cwd, "qawolf.config.ts");
|
|
182523
|
+
const flowPath = join37(deps.cwd, "src", "flows", "example.flow.ts");
|
|
182524
|
+
const gitignorePath = join37(deps.cwd, ".qawolf", ".gitignore");
|
|
182429
182525
|
await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
|
|
182430
182526
|
await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
|
|
182431
182527
|
await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
|
|
@@ -182449,7 +182545,7 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
|
|
|
182449
182545
|
ctx.ui.step(initMessages.createdFile(relPath));
|
|
182450
182546
|
}
|
|
182451
182547
|
async function ensurePackageJson(ctx, yes, deps) {
|
|
182452
|
-
const pkgPath =
|
|
182548
|
+
const pkgPath = join37(deps.cwd, "package.json");
|
|
182453
182549
|
if (!await deps.fs.pathExists(pkgPath)) {
|
|
182454
182550
|
const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
|
|
182455
182551
|
if (!confirmed2.ok || !confirmed2.value) {
|
|
@@ -182508,19 +182604,19 @@ Examples:
|
|
|
182508
182604
|
}
|
|
182509
182605
|
|
|
182510
182606
|
// src/commands/install/android.ts
|
|
182511
|
-
import { join as
|
|
182607
|
+
import { join as join40 } from "node:path";
|
|
182512
182608
|
|
|
182513
182609
|
// src/domains/install/android/index.ts
|
|
182514
182610
|
import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
|
|
182515
182611
|
|
|
182516
182612
|
// src/domains/install/android/avd.ts
|
|
182517
|
-
import { join as
|
|
182613
|
+
import { join as join38 } from "node:path";
|
|
182518
182614
|
async function installAvds(ctx, specs, deps) {
|
|
182519
182615
|
const versionResult = await deps.spawn(deps.sdkManagerPath, ["--version"]);
|
|
182520
182616
|
if (versionResult.exitCode !== 0) {
|
|
182521
182617
|
throw new Error(installMessages.android.sdkmanagerNotFound(deps.sdkManagerPath));
|
|
182522
182618
|
}
|
|
182523
|
-
const licenseFile =
|
|
182619
|
+
const licenseFile = join38(deps.androidHome, "licenses", "android-sdk-license");
|
|
182524
182620
|
if (deps.checkExists(licenseFile)) {
|
|
182525
182621
|
ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
|
|
182526
182622
|
} else {
|
|
@@ -182534,7 +182630,7 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182534
182630
|
for (const spec of specs) {
|
|
182535
182631
|
if (installedImages.has(spec.systemImage))
|
|
182536
182632
|
continue;
|
|
182537
|
-
const systemImg =
|
|
182633
|
+
const systemImg = join38(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
|
|
182538
182634
|
if (deps.checkExists(systemImg)) {
|
|
182539
182635
|
ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
|
|
182540
182636
|
installedImages.add(spec.systemImage);
|
|
@@ -182582,9 +182678,9 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182582
182678
|
}
|
|
182583
182679
|
|
|
182584
182680
|
// src/domains/install/android/driver.ts
|
|
182585
|
-
import { join as
|
|
182681
|
+
import { join as join39 } from "node:path";
|
|
182586
182682
|
var appiumEnv = {
|
|
182587
|
-
APPIUM_HOME:
|
|
182683
|
+
APPIUM_HOME: join39(envPaths("qawolf").data, "appium")
|
|
182588
182684
|
};
|
|
182589
182685
|
async function installUiautomator2Driver(ctx, deps) {
|
|
182590
182686
|
const listResult = await deps.spawn(deps.appiumBinPath, ["driver", "list", "--installed"], { env: appiumEnv });
|
|
@@ -182671,8 +182767,8 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
|
|
|
182671
182767
|
arch: process.arch,
|
|
182672
182768
|
androidHome: androidHome2,
|
|
182673
182769
|
checkExists: (path9) => fs9.existsSync(path9),
|
|
182674
|
-
sdkManagerPath:
|
|
182675
|
-
avdManagerPath:
|
|
182770
|
+
sdkManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
|
|
182771
|
+
avdManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
|
|
182676
182772
|
expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
|
|
182677
182773
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182678
182774
|
resolveDepsRoot: async (files) => envDir ?? (await resolveDepsRoot({ files, fs: fs9 })).depsRoot,
|
|
@@ -183063,4 +183159,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
183063
183159
|
process.exitCode = 1;
|
|
183064
183160
|
}).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
183065
183161
|
|
|
183066
|
-
//# debugId=
|
|
183162
|
+
//# debugId=724E44C8F9B9A9E264756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -63,13 +63,14 @@
|
|
|
63
63
|
"@qawolf/api-contracts": "0.2.0",
|
|
64
64
|
"@qawolf/emails": "1.1.1",
|
|
65
65
|
"@qawolf/flow-targets": "1.0.0",
|
|
66
|
-
"@qawolf/flows": "0.1.
|
|
66
|
+
"@qawolf/flows": "0.1.4",
|
|
67
67
|
"@qawolf/testkit": "1.1.1",
|
|
68
68
|
"appium": "2.11.3",
|
|
69
69
|
"appium-uiautomator2-driver": "3.7.0",
|
|
70
70
|
"appium-xcuitest-driver": "9.10.5",
|
|
71
71
|
"commander": "14.0.3",
|
|
72
72
|
"env-paths": "4.0.0",
|
|
73
|
+
"expect-webdriverio": "5.6.5",
|
|
73
74
|
"picomatch": "4.0.4",
|
|
74
75
|
"pino": "10.3.1",
|
|
75
76
|
"playwright": "1.58.2",
|
|
@@ -19,6 +19,11 @@ environment variable (or stored credentials from `qawolf auth login`).
|
|
|
19
19
|
their table entry notes a flag that switches them to `read`.
|
|
20
20
|
Verify with `qawolf auth whoami`. Never print or log the key.
|
|
21
21
|
|
|
22
|
+
Commands use `https://app.qawolf.com` by default. Set `QAWOLF_HOST_URL` to
|
|
23
|
+
target another deployment host, for example
|
|
24
|
+
`https://app.staging.example.com`. `QAWOLF_API_URL` is a separate API endpoint
|
|
25
|
+
and does not select the deployment host used by CLI commands.
|
|
26
|
+
|
|
22
27
|
## Output
|
|
23
28
|
|
|
24
29
|
When consuming output programmatically, always pass `--json` (or `--agent`).
|