@qawolf/cli 1.2.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 +378 -265
- package/package.json +4 -3
- package/skills/qawolf-cli/SKILL.md +6 -1
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;
|
|
@@ -134044,7 +134044,7 @@ var require_snapshot_utils = __commonJS((exports, module) => {
|
|
|
134044
134044
|
// node_modules/undici/lib/mock/snapshot-recorder.js
|
|
134045
134045
|
var require_snapshot_recorder = __commonJS((exports, module) => {
|
|
134046
134046
|
var { writeFile, readFile, mkdir } = __require("node:fs/promises");
|
|
134047
|
-
var { dirname:
|
|
134047
|
+
var { dirname: dirname11, resolve: resolve9 } = __require("node:path");
|
|
134048
134048
|
var { setTimeout: setTimeout3, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
134049
134049
|
var { InvalidArgumentError: InvalidArgumentError3, UndiciError } = require_errors6();
|
|
134050
134050
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -134239,7 +134239,7 @@ var require_snapshot_recorder = __commonJS((exports, module) => {
|
|
|
134239
134239
|
throw new InvalidArgumentError3("Snapshot path is required");
|
|
134240
134240
|
}
|
|
134241
134241
|
const resolvedPath = resolve9(path4);
|
|
134242
|
-
await mkdir(
|
|
134242
|
+
await mkdir(dirname11(resolvedPath), { recursive: true });
|
|
134243
134243
|
const data2 = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
|
|
134244
134244
|
hash: hash2,
|
|
134245
134245
|
snapshot
|
|
@@ -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: {
|
|
@@ -155273,6 +155273,10 @@ var flowsMessages = {
|
|
|
155273
155273
|
title: "Flows",
|
|
155274
155274
|
remoteTitle: "Remote Flows",
|
|
155275
155275
|
flowCount: (count) => pluralize(count, "flow"),
|
|
155276
|
+
list: {
|
|
155277
|
+
remoteRequiresEnv: "--remote requires --env <env> to pick the environment to list",
|
|
155278
|
+
flagsRequireRemote: "--env and --include-drafts require --remote"
|
|
155279
|
+
},
|
|
155276
155280
|
pull: {
|
|
155277
155281
|
downloadingBundle: "Downloading flows bundle",
|
|
155278
155282
|
fetchingEnvVars: "Fetching environment variables",
|
|
@@ -155622,6 +155626,15 @@ function makeDefaultFs() {
|
|
|
155622
155626
|
};
|
|
155623
155627
|
}
|
|
155624
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
|
+
|
|
155625
155638
|
// src/domains/auth/resolve.ts
|
|
155626
155639
|
import { Entry as Entry3 } from "@napi-rs/keyring";
|
|
155627
155640
|
|
|
@@ -170022,7 +170035,8 @@ async function validateApiKey(deps) {
|
|
|
170022
170035
|
}
|
|
170023
170036
|
// src/shell/logger.ts
|
|
170024
170037
|
var import_pino = __toESM(require_pino(), 1);
|
|
170025
|
-
import {
|
|
170038
|
+
import { mkdirSync as mkdirSync2, openSync } from "node:fs";
|
|
170039
|
+
import { dirname, join as join4 } from "node:path";
|
|
170026
170040
|
function defaultLogPath() {
|
|
170027
170041
|
return join4(envPaths("qawolf", { suffix: "" }).log, "cli.log");
|
|
170028
170042
|
}
|
|
@@ -170036,7 +170050,8 @@ function resolveStderrLevel(env2, verbose) {
|
|
|
170036
170050
|
}
|
|
170037
170051
|
function createLoggingSystem(opts) {
|
|
170038
170052
|
const { stderrLevel, logPath } = opts;
|
|
170039
|
-
|
|
170053
|
+
mkdirSync2(dirname(logPath), { recursive: true });
|
|
170054
|
+
const dest = import_pino.default.destination({ fd: openSync(logPath, "a"), sync: false });
|
|
170040
170055
|
const fileLogger = import_pino.default({ level: "debug" }, dest);
|
|
170041
170056
|
const levelNums = {
|
|
170042
170057
|
trace: 10,
|
|
@@ -171158,45 +171173,6 @@ async function getIdentity(apiKey, deps) {
|
|
|
171158
171173
|
return { ok: true, data: parsed.data };
|
|
171159
171174
|
}
|
|
171160
171175
|
|
|
171161
|
-
// src/shell/platform/getRemoteFlows.ts
|
|
171162
|
-
var remoteFlowSchema = exports_external.object({
|
|
171163
|
-
executionTarget: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]),
|
|
171164
|
-
id: exports_external.string(),
|
|
171165
|
-
name: exports_external.string(),
|
|
171166
|
-
path: exports_external.string(),
|
|
171167
|
-
tags: exports_external.array(exports_external.string())
|
|
171168
|
-
});
|
|
171169
|
-
var remoteFlowsResponseSchema = exports_external.object({
|
|
171170
|
-
flows: exports_external.array(remoteFlowSchema)
|
|
171171
|
-
});
|
|
171172
|
-
async function getRemoteFlows(apiKey, deps) {
|
|
171173
|
-
const url2 = `${deps.baseUrl}/api/v0/flows?includeDrafts=false`;
|
|
171174
|
-
let response;
|
|
171175
|
-
try {
|
|
171176
|
-
response = await deps.fetch(url2, {
|
|
171177
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
171178
|
-
signal: AbortSignal.timeout(15000)
|
|
171179
|
-
});
|
|
171180
|
-
} catch (error51) {
|
|
171181
|
-
return { ok: false, error: { kind: "network", cause: toError(error51) } };
|
|
171182
|
-
}
|
|
171183
|
-
if (!response.ok) {
|
|
171184
|
-
const body = await response.text().catch(() => "");
|
|
171185
|
-
return {
|
|
171186
|
-
ok: false,
|
|
171187
|
-
error: { kind: "http", status: response.status, body }
|
|
171188
|
-
};
|
|
171189
|
-
}
|
|
171190
|
-
const json2 = await response.json().catch(() => {
|
|
171191
|
-
return;
|
|
171192
|
-
});
|
|
171193
|
-
const parsed = remoteFlowsResponseSchema.safeParse(json2);
|
|
171194
|
-
if (!parsed.success) {
|
|
171195
|
-
return { ok: false, error: { kind: "parse", cause: parsed.error } };
|
|
171196
|
-
}
|
|
171197
|
-
return { ok: true, data: parsed.data };
|
|
171198
|
-
}
|
|
171199
|
-
|
|
171200
171176
|
// src/shell/platform/types.ts
|
|
171201
171177
|
var environmentListItemSchema = exports_external.object({
|
|
171202
171178
|
envId: exports_external.string(),
|
|
@@ -171250,10 +171226,10 @@ async function listTeamStorageFiles(trpc, args, deps) {
|
|
|
171250
171226
|
}
|
|
171251
171227
|
|
|
171252
171228
|
// src/shell/platform/teamStorageAssets.ts
|
|
171253
|
-
import { dirname as
|
|
171229
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
171254
171230
|
|
|
171255
171231
|
// src/shell/platform/teamStorageAssetManifest.ts
|
|
171256
|
-
import { dirname } from "node:path";
|
|
171232
|
+
import { dirname as dirname2 } from "node:path";
|
|
171257
171233
|
var assetManifestSchema = exports_external.object({
|
|
171258
171234
|
version: exports_external.literal(1),
|
|
171259
171235
|
files: exports_external.array(exports_external.object({
|
|
@@ -171297,7 +171273,7 @@ async function writeAssetManifest(assetsAbs, files, fs2) {
|
|
|
171297
171273
|
}))
|
|
171298
171274
|
};
|
|
171299
171275
|
const path2 = assetManifestPath(assetsAbs);
|
|
171300
|
-
await fs2.mkdir(
|
|
171276
|
+
await fs2.mkdir(dirname2(path2), { recursive: true });
|
|
171301
171277
|
await fs2.writeFile(path2, `${JSON.stringify(manifest, undefined, 2)}
|
|
171302
171278
|
`);
|
|
171303
171279
|
}
|
|
@@ -171463,7 +171439,7 @@ async function writeAssetSnapshot(args) {
|
|
|
171463
171439
|
await args.deps.fs.mkdir(args.tmpAssets, { recursive: true });
|
|
171464
171440
|
for (const { file: file2, relativePath } of args.safeFiles) {
|
|
171465
171441
|
const dest = join8(args.tmpAssets, relativePath);
|
|
171466
|
-
await args.deps.fs.mkdir(
|
|
171442
|
+
await args.deps.fs.mkdir(dirname3(dest), { recursive: true });
|
|
171467
171443
|
if (args.reusable.has(relativePath)) {
|
|
171468
171444
|
await args.deps.fs.copyFile(join8(args.assetsAbs, relativePath), dest);
|
|
171469
171445
|
continue;
|
|
@@ -171502,14 +171478,6 @@ function createPlatformClient(apiKey, deps) {
|
|
|
171502
171478
|
sleep: deps.sleep
|
|
171503
171479
|
});
|
|
171504
171480
|
},
|
|
171505
|
-
async getRemoteFlows() {
|
|
171506
|
-
return requestWithRetry({
|
|
171507
|
-
call: () => getRemoteFlows(apiKey, deps),
|
|
171508
|
-
backoffMs: requestBackoffMs2,
|
|
171509
|
-
describe: (err) => describeRequestError(err, deps.baseUrl, "flows"),
|
|
171510
|
-
sleep: deps.sleep
|
|
171511
|
-
});
|
|
171512
|
-
},
|
|
171513
171481
|
getFlowsBundleUrl: getFlowsBundleUrlImpl,
|
|
171514
171482
|
callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
|
|
171515
171483
|
async getEnvVars(envId) {
|
|
@@ -173418,7 +173386,7 @@ function buildBaseContext(command, signals) {
|
|
|
173418
173386
|
logPath: defaultLogPath(),
|
|
173419
173387
|
...verboseWrite ? { verboseWrite } : {}
|
|
173420
173388
|
});
|
|
173421
|
-
const apiBaseUrl = env2
|
|
173389
|
+
const apiBaseUrl = resolveHostUrl(env2);
|
|
173422
173390
|
return {
|
|
173423
173391
|
ctx: {
|
|
173424
173392
|
ui: createUI(outputMode, {
|
|
@@ -173630,7 +173598,7 @@ function registerAuthCommand(program2, signals) {
|
|
|
173630
173598
|
// package.json
|
|
173631
173599
|
var package_default = {
|
|
173632
173600
|
name: "@qawolf/cli",
|
|
173633
|
-
version: "1.2
|
|
173601
|
+
version: "1.3.2",
|
|
173634
173602
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
173635
173603
|
keywords: [
|
|
173636
173604
|
"automation",
|
|
@@ -173690,16 +173658,17 @@ var package_default = {
|
|
|
173690
173658
|
"@napi-rs/keyring": "1.3.0",
|
|
173691
173659
|
"@oxc-node/core": "0.1.0",
|
|
173692
173660
|
"@playwright/test": "1.60.0",
|
|
173693
|
-
"@qawolf/api-contracts": "0.
|
|
173661
|
+
"@qawolf/api-contracts": "0.2.0",
|
|
173694
173662
|
"@qawolf/emails": "1.1.1",
|
|
173695
173663
|
"@qawolf/flow-targets": "1.0.0",
|
|
173696
|
-
"@qawolf/flows": "0.1.
|
|
173664
|
+
"@qawolf/flows": "0.1.4",
|
|
173697
173665
|
"@qawolf/testkit": "1.1.1",
|
|
173698
173666
|
appium: "2.11.3",
|
|
173699
173667
|
"appium-uiautomator2-driver": "3.7.0",
|
|
173700
173668
|
"appium-xcuitest-driver": "9.10.5",
|
|
173701
173669
|
commander: "14.0.3",
|
|
173702
173670
|
"env-paths": "4.0.0",
|
|
173671
|
+
"expect-webdriverio": "5.6.5",
|
|
173703
173672
|
picomatch: "4.0.4",
|
|
173704
173673
|
pino: "10.3.1",
|
|
173705
173674
|
playwright: "1.58.2",
|
|
@@ -174217,13 +174186,13 @@ function renderResults(ui, results) {
|
|
|
174217
174186
|
}
|
|
174218
174187
|
|
|
174219
174188
|
// src/domains/flows/ensureDeps.ts
|
|
174220
|
-
import { dirname as
|
|
174189
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
174221
174190
|
function findEnvDir(flowPath, fs2) {
|
|
174222
|
-
let dir =
|
|
174191
|
+
let dir = dirname4(flowPath);
|
|
174223
174192
|
while (true) {
|
|
174224
174193
|
if (fs2.existsSync(join10(dir, "package.json")))
|
|
174225
174194
|
return dir;
|
|
174226
|
-
const parent =
|
|
174195
|
+
const parent = dirname4(dir);
|
|
174227
174196
|
if (parent === dir)
|
|
174228
174197
|
return;
|
|
174229
174198
|
dir = parent;
|
|
@@ -174256,7 +174225,7 @@ import { fileURLToPath } from "url";
|
|
|
174256
174225
|
|
|
174257
174226
|
// node_modules/fdir/dist/index.mjs
|
|
174258
174227
|
import { createRequire as createRequire2 } from "module";
|
|
174259
|
-
import { basename, dirname as
|
|
174228
|
+
import { basename, dirname as dirname5, normalize as normalize2, relative as relative2, resolve, sep as sep3 } from "path";
|
|
174260
174229
|
import * as nativeFs from "fs";
|
|
174261
174230
|
var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
|
|
174262
174231
|
function cleanPath(path2) {
|
|
@@ -174420,7 +174389,7 @@ function build$2(options, isSynchronous) {
|
|
|
174420
174389
|
function isRecursive(path2, resolved, state) {
|
|
174421
174390
|
if (state.options.useRealPaths)
|
|
174422
174391
|
return isRecursiveUsingRealPaths(resolved, state);
|
|
174423
|
-
let parent =
|
|
174392
|
+
let parent = dirname5(path2);
|
|
174424
174393
|
let depth = 1;
|
|
174425
174394
|
while (parent !== state.root && depth < 2) {
|
|
174426
174395
|
const resolvedPath = state.symlinks.get(parent);
|
|
@@ -174428,7 +174397,7 @@ function isRecursive(path2, resolved, state) {
|
|
|
174428
174397
|
if (isSameRoot)
|
|
174429
174398
|
depth++;
|
|
174430
174399
|
else
|
|
174431
|
-
parent =
|
|
174400
|
+
parent = dirname5(parent);
|
|
174432
174401
|
}
|
|
174433
174402
|
state.symlinks.set(path2, resolved);
|
|
174434
174403
|
return depth > 1;
|
|
@@ -174624,7 +174593,7 @@ var Walker = class {
|
|
|
174624
174593
|
} else {
|
|
174625
174594
|
resolvedPath = useRealPaths ? resolvedPath : path2;
|
|
174626
174595
|
const filename = basename(resolvedPath);
|
|
174627
|
-
const directoryPath$1 = normalizePath(
|
|
174596
|
+
const directoryPath$1 = normalizePath(dirname5(resolvedPath), this.state.options);
|
|
174628
174597
|
resolvedPath = this.joinPath(filename, directoryPath$1);
|
|
174629
174598
|
this.pushFile(resolvedPath, files, this.state.counts, filters);
|
|
174630
174599
|
}
|
|
@@ -175173,13 +175142,14 @@ import { createHash } from "node:crypto";
|
|
|
175173
175142
|
import { join as join12, resolve as resolve3 } from "node:path";
|
|
175174
175143
|
|
|
175175
175144
|
// src/generated/dependencyVersions.ts
|
|
175176
|
-
var flowsVersion = "0.1.
|
|
175145
|
+
var flowsVersion = "0.1.4";
|
|
175177
175146
|
var playwrightVersion = "1.58.2";
|
|
175178
175147
|
var emailsVersion = "1.1.1";
|
|
175179
175148
|
var testkitVersion = "1.1.1";
|
|
175180
175149
|
var appiumVersion = "2.11.3";
|
|
175181
175150
|
var appiumXcuitestDriverVersion = "9.10.5";
|
|
175182
175151
|
var appiumUiautomator2DriverVersion = "3.7.0";
|
|
175152
|
+
var expectWebdriverioVersion = "5.6.5";
|
|
175183
175153
|
|
|
175184
175154
|
// src/domains/runtimeEnv/pinnedPackages.ts
|
|
175185
175155
|
var pinnedPackages = [
|
|
@@ -175192,7 +175162,8 @@ var pinnedPackages = [
|
|
|
175192
175162
|
{
|
|
175193
175163
|
name: "appium-uiautomator2-driver",
|
|
175194
175164
|
version: appiumUiautomator2DriverVersion
|
|
175195
|
-
}
|
|
175165
|
+
},
|
|
175166
|
+
{ name: "expect-webdriverio", version: expectWebdriverioVersion }
|
|
175196
175167
|
];
|
|
175197
175168
|
|
|
175198
175169
|
// src/domains/runtimeEnv/managedEnvDir.ts
|
|
@@ -175702,14 +175673,80 @@ function handleFlowsList(ctx, pattern) {
|
|
|
175702
175673
|
|
|
175703
175674
|
// src/domains/flows/listRemote.ts
|
|
175704
175675
|
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
175705
|
-
|
|
175706
|
-
|
|
175676
|
+
|
|
175677
|
+
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
175678
|
+
var defaultIdSchemas = {
|
|
175679
|
+
environment: exports_external.string().min(1).describe("The id of the environment."),
|
|
175680
|
+
flow: exports_external.string().min(1).describe("The id of the flow."),
|
|
175681
|
+
run: exports_external.string().min(1).describe("The id of the run.")
|
|
175682
|
+
};
|
|
175683
|
+
|
|
175684
|
+
// node_modules/@qawolf/api-contracts/dist/v1/flow/index.js
|
|
175685
|
+
var makeListFlowsContract = (ids) => {
|
|
175686
|
+
const input = exports_external.object({
|
|
175687
|
+
environmentId: ids.environment,
|
|
175688
|
+
includeDrafts: exports_external.boolean().default(false).describe("Include draft flows in the listing.")
|
|
175689
|
+
});
|
|
175690
|
+
const executionTarget = exports_external.union([exports_external.string().min(1), exports_external.record(exports_external.string(), exports_external.unknown())]).describe("The target the flow executes against, as a preset or object.");
|
|
175691
|
+
const output = exports_external.object({
|
|
175692
|
+
flows: exports_external.array(exports_external.object({
|
|
175693
|
+
executionTarget,
|
|
175694
|
+
flowId: ids.flow,
|
|
175695
|
+
name: exports_external.string(),
|
|
175696
|
+
path: exports_external.string().describe("The flow file path, relative to the repo."),
|
|
175697
|
+
tags: exports_external.array(exports_external.string())
|
|
175698
|
+
}))
|
|
175699
|
+
});
|
|
175700
|
+
return {
|
|
175701
|
+
description: "List the flows of an environment at its latest reconciled commit.",
|
|
175702
|
+
input,
|
|
175703
|
+
kind: "read",
|
|
175704
|
+
name: "flow.list",
|
|
175705
|
+
output
|
|
175706
|
+
};
|
|
175707
|
+
};
|
|
175708
|
+
|
|
175709
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/index.js
|
|
175710
|
+
var makeCreateRunContract = (ids) => {
|
|
175711
|
+
const input = exports_external.object({
|
|
175712
|
+
environmentId: ids.environment,
|
|
175713
|
+
environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
175714
|
+
flowIds: exports_external.array(ids.flow).min(1),
|
|
175715
|
+
ignoreRules: exports_external.boolean().default(false)
|
|
175716
|
+
});
|
|
175717
|
+
const output = exports_external.object({ runId: ids.run });
|
|
175718
|
+
return {
|
|
175719
|
+
description: "Create a run for selected flows in an environment.",
|
|
175720
|
+
input,
|
|
175721
|
+
kind: "write",
|
|
175722
|
+
name: "run.create",
|
|
175723
|
+
output
|
|
175724
|
+
};
|
|
175725
|
+
};
|
|
175726
|
+
|
|
175727
|
+
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
175728
|
+
var makeContractsV1 = (ids) => ({
|
|
175729
|
+
flow: {
|
|
175730
|
+
list: makeListFlowsContract(ids)
|
|
175731
|
+
},
|
|
175732
|
+
run: {
|
|
175733
|
+
create: makeCreateRunContract(ids)
|
|
175734
|
+
}
|
|
175735
|
+
});
|
|
175736
|
+
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
175737
|
+
|
|
175738
|
+
// src/domains/flows/listRemote.ts
|
|
175739
|
+
async function flowsListRemote(ctx, pattern, options) {
|
|
175740
|
+
const result = await ctx.platform.callPublicApi(publicContractsV1.flow.list, {
|
|
175741
|
+
environmentId: options.env,
|
|
175742
|
+
includeDrafts: options.includeDrafts
|
|
175743
|
+
});
|
|
175707
175744
|
if (!result.ok) {
|
|
175708
175745
|
return { error: result.error };
|
|
175709
175746
|
}
|
|
175710
175747
|
const matches = pattern ? import_picomatch2.default(pattern) : undefined;
|
|
175711
175748
|
const items = result.value.flows.filter((f2) => !matches || matches(f2.path)).map((f2) => ({
|
|
175712
|
-
|
|
175749
|
+
flowId: f2.flowId,
|
|
175713
175750
|
file: f2.path,
|
|
175714
175751
|
name: f2.name,
|
|
175715
175752
|
tags: f2.tags,
|
|
@@ -175739,7 +175776,7 @@ async function flowsListRemote(ctx, pattern) {
|
|
|
175739
175776
|
}
|
|
175740
175777
|
|
|
175741
175778
|
// src/domains/flows/pull/handler.ts
|
|
175742
|
-
import { dirname as
|
|
175779
|
+
import { dirname as dirname7, join as join23, resolve as resolve7 } from "node:path";
|
|
175743
175780
|
|
|
175744
175781
|
// src/shell/manifest/io.ts
|
|
175745
175782
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -176130,7 +176167,7 @@ async function writeEnvFile(envDir, vars, fs2 = makeDefaultFs()) {
|
|
|
176130
176167
|
}
|
|
176131
176168
|
|
|
176132
176169
|
// src/domains/flows/pull/extract.ts
|
|
176133
|
-
import { dirname as
|
|
176170
|
+
import { dirname as dirname6, resolve as resolve6 } from "node:path";
|
|
176134
176171
|
import { createGunzip } from "node:zlib";
|
|
176135
176172
|
|
|
176136
176173
|
// node_modules/tar/dist/esm/index.min.js
|
|
@@ -179394,7 +179431,7 @@ async function handleEntry(args) {
|
|
|
179394
179431
|
total: args.getTotal()
|
|
179395
179432
|
});
|
|
179396
179433
|
args.addTotal(size);
|
|
179397
|
-
args.fs.mkdirSync(
|
|
179434
|
+
args.fs.mkdirSync(dirname6(target), { recursive: true });
|
|
179398
179435
|
const chunks = [];
|
|
179399
179436
|
await new Promise((res, rej) => {
|
|
179400
179437
|
entry.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -179498,7 +179535,7 @@ async function handleFlowsPull(ctx, opts, deps) {
|
|
|
179498
179535
|
return { error: validation.error };
|
|
179499
179536
|
}
|
|
179500
179537
|
const destAbs = resolve7(opts.out ?? join23(".qawolf", opts.env));
|
|
179501
|
-
const assetsAbs = join23(
|
|
179538
|
+
const assetsAbs = join23(dirname7(destAbs), "assets");
|
|
179502
179539
|
await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
|
|
179503
179540
|
const yes = opts.yes ?? false;
|
|
179504
179541
|
let archive;
|
|
@@ -180034,7 +180071,7 @@ async function defaultRunWebFlowDeps(cwd = process.cwd(), signals) {
|
|
|
180034
180071
|
|
|
180035
180072
|
// src/domains/runtimeEnv/prepareRunDir.ts
|
|
180036
180073
|
import { createHash as createHash3 } from "node:crypto";
|
|
180037
|
-
import { basename as basename4, dirname as
|
|
180074
|
+
import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve8, sep as sep4 } from "node:path";
|
|
180038
180075
|
|
|
180039
180076
|
// src/shell/copyDir.ts
|
|
180040
180077
|
import { cp, readdir as readdir2 } from "node:fs/promises";
|
|
@@ -180075,7 +180112,10 @@ async function readPackageJson(pkgPath, fs3) {
|
|
|
180075
180112
|
}
|
|
180076
180113
|
|
|
180077
180114
|
// src/domains/runtimeEnv/innerHop.ts
|
|
180078
|
-
import {
|
|
180115
|
+
import { join as join27 } from "node:path";
|
|
180116
|
+
|
|
180117
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180118
|
+
import { dirname as dirname8, join as join26 } from "node:path";
|
|
180079
180119
|
|
|
180080
180120
|
// src/domains/runtimeEnv/symlinkDir.ts
|
|
180081
180121
|
import { symlink } from "node:fs/promises";
|
|
@@ -180084,25 +180124,35 @@ async function createDirSymlink(source, target) {
|
|
|
180084
180124
|
await symlink(source, target, linkType);
|
|
180085
180125
|
}
|
|
180086
180126
|
|
|
180087
|
-
// src/domains/runtimeEnv/
|
|
180088
|
-
async function
|
|
180089
|
-
const { depsRoot,
|
|
180127
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180128
|
+
async function linkPinnedPackages(args) {
|
|
180129
|
+
const { depsRoot, nodeModulesDir, fs: fs3 } = args;
|
|
180090
180130
|
const managedModules = join26(depsRoot, "node_modules");
|
|
180091
|
-
|
|
180092
|
-
await fs3.mkdir(innerModules, { recursive: true });
|
|
180131
|
+
await fs3.mkdir(nodeModulesDir, { recursive: true });
|
|
180093
180132
|
for (const { name } of pinnedPackages) {
|
|
180094
180133
|
const segments = name.split("/");
|
|
180095
|
-
const target = join26(
|
|
180134
|
+
const target = join26(nodeModulesDir, ...segments);
|
|
180096
180135
|
if (segments.length > 1) {
|
|
180097
|
-
await fs3.mkdir(
|
|
180136
|
+
await fs3.mkdir(dirname8(target), { recursive: true });
|
|
180098
180137
|
}
|
|
180138
|
+
await fs3.rm(target, { recursive: true, force: true });
|
|
180099
180139
|
await createDirSymlink(join26(managedModules, ...segments), target);
|
|
180100
180140
|
}
|
|
180101
180141
|
}
|
|
180102
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
|
+
|
|
180103
180153
|
// src/domains/runtimeEnv/outerHop.ts
|
|
180104
180154
|
import { lstat } from "node:fs/promises";
|
|
180105
|
-
import { dirname as
|
|
180155
|
+
import { dirname as dirname9, join as join28 } from "node:path";
|
|
180106
180156
|
var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
|
|
180107
180157
|
async function populateOuterHop(args) {
|
|
180108
180158
|
const { projectDir, runDir, fs: fs3 } = args;
|
|
@@ -180112,28 +180162,33 @@ async function populateOuterHop(args) {
|
|
|
180112
180162
|
const depNames = Object.keys(installableDeps);
|
|
180113
180163
|
const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
|
|
180114
180164
|
if (satisfying !== undefined) {
|
|
180115
|
-
await createDirSymlink(satisfying,
|
|
180165
|
+
await createDirSymlink(satisfying, join28(runDir, "node_modules"));
|
|
180116
180166
|
return { mode: "symlink", nodeModulesDir: satisfying };
|
|
180117
180167
|
}
|
|
180118
180168
|
if (depNames.length === 0)
|
|
180119
180169
|
return { mode: "none" };
|
|
180120
180170
|
args.onInstallStart?.(depNames.length);
|
|
180121
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
|
+
});
|
|
180122
180177
|
return { mode: "install", depCount: depNames.length, rejected };
|
|
180123
180178
|
}
|
|
180124
180179
|
async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
|
|
180125
180180
|
const rejected = [];
|
|
180126
180181
|
let dir = startDir;
|
|
180127
180182
|
while (true) {
|
|
180128
|
-
const candidate =
|
|
180183
|
+
const candidate = join28(dir, "node_modules");
|
|
180129
180184
|
const stats = await lstatSafe(candidate);
|
|
180130
180185
|
if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
180131
|
-
const missing = depNames.filter((name) => !fs3.existsSync(
|
|
180186
|
+
const missing = depNames.filter((name) => !fs3.existsSync(join28(candidate, name, "package.json")));
|
|
180132
180187
|
if (missing.length === 0)
|
|
180133
180188
|
return { satisfying: candidate, rejected };
|
|
180134
180189
|
rejected.push({ dir: candidate, missing });
|
|
180135
180190
|
}
|
|
180136
|
-
const parent =
|
|
180191
|
+
const parent = dirname9(dir);
|
|
180137
180192
|
if (parent === dir)
|
|
180138
180193
|
return { satisfying: undefined, rejected };
|
|
180139
180194
|
dir = parent;
|
|
@@ -180149,7 +180204,7 @@ async function lstatSafe(path3) {
|
|
|
180149
180204
|
async function readInstallableDeps(projectDir, fs3) {
|
|
180150
180205
|
let content;
|
|
180151
180206
|
try {
|
|
180152
|
-
content = await fs3.readFile(
|
|
180207
|
+
content = await fs3.readFile(join28(projectDir, "package.json"));
|
|
180153
180208
|
} catch {
|
|
180154
180209
|
return {};
|
|
180155
180210
|
}
|
|
@@ -180167,8 +180222,8 @@ async function readInstallableDeps(projectDir, fs3) {
|
|
|
180167
180222
|
return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
|
|
180168
180223
|
}
|
|
180169
180224
|
async function installOuterDeps(runDir, deps, fs3, install) {
|
|
180170
|
-
await fs3.writeFile(
|
|
180171
|
-
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/
|
|
180172
180227
|
`);
|
|
180173
180228
|
const result = await install(runDir);
|
|
180174
180229
|
if (result.exitCode !== 0) {
|
|
@@ -180182,10 +180237,10 @@ async function prepareRunDir(args) {
|
|
|
180182
180237
|
const { files, projectDir, depsRoot, runRoot } = args;
|
|
180183
180238
|
const fs3 = args.fs ?? makeDefaultFs();
|
|
180184
180239
|
const runId = buildRunId(projectDir, files);
|
|
180185
|
-
const runDir =
|
|
180240
|
+
const runDir = join29(runRoot, runId);
|
|
180186
180241
|
await fs3.rm(runDir, { recursive: true, force: true });
|
|
180187
180242
|
await fs3.mkdir(runDir, { recursive: true });
|
|
180188
|
-
const execDir =
|
|
180243
|
+
const execDir = join29(runDir, "exec");
|
|
180189
180244
|
await fs3.mkdir(execDir, { recursive: true });
|
|
180190
180245
|
await populateInnerHop({ depsRoot, execDir, fs: fs3 });
|
|
180191
180246
|
const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs3 });
|
|
@@ -180195,6 +180250,7 @@ async function prepareRunDir(args) {
|
|
|
180195
180250
|
const outerHop = await populateOuterHop({
|
|
180196
180251
|
projectDir,
|
|
180197
180252
|
runDir,
|
|
180253
|
+
depsRoot,
|
|
180198
180254
|
fs: fs3,
|
|
180199
180255
|
...args.onInstallStart !== undefined ? { onInstallStart: args.onInstallStart } : {}
|
|
180200
180256
|
});
|
|
@@ -180221,38 +180277,38 @@ async function stageFlowFiles(args) {
|
|
|
180221
180277
|
}
|
|
180222
180278
|
if (files.length > 1) {
|
|
180223
180279
|
return Promise.all(files.map(async (f3) => {
|
|
180224
|
-
const dirHash = createHash3("sha256").update(
|
|
180225
|
-
const subDir =
|
|
180280
|
+
const dirHash = createHash3("sha256").update(dirname10(f3)).digest("hex").slice(0, 8);
|
|
180281
|
+
const subDir = join29(execDir, dirHash);
|
|
180226
180282
|
await fs3.mkdir(subDir, { recursive: true });
|
|
180227
|
-
const dest =
|
|
180283
|
+
const dest = join29(subDir, basename4(f3));
|
|
180228
180284
|
await fs3.copyFile(f3, dest);
|
|
180229
180285
|
return dest;
|
|
180230
180286
|
}));
|
|
180231
180287
|
}
|
|
180232
|
-
await Promise.all(files.map((f3) => fs3.copyFile(f3,
|
|
180233
|
-
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)));
|
|
180234
180290
|
}
|
|
180235
180291
|
function remapPath(file2, projectDir, execDir) {
|
|
180236
180292
|
if (file2 === projectDir)
|
|
180237
180293
|
return execDir;
|
|
180238
180294
|
if (file2.startsWith(projectDir + sep4)) {
|
|
180239
|
-
return
|
|
180295
|
+
return join29(execDir, file2.slice(projectDir.length + 1));
|
|
180240
180296
|
}
|
|
180241
180297
|
return file2;
|
|
180242
180298
|
}
|
|
180243
180299
|
|
|
180244
180300
|
// src/shell/testkit.ts
|
|
180245
|
-
import { join as
|
|
180301
|
+
import { join as join30 } from "node:path";
|
|
180246
180302
|
function notAvailableLocally(name) {
|
|
180247
180303
|
throw new Error(runnerMessages.notAvailableLocally(name));
|
|
180248
180304
|
}
|
|
180249
180305
|
async function loadSdkDeps(cwd) {
|
|
180250
|
-
const testkitDir =
|
|
180306
|
+
const testkitDir = join30(cwd, "node_modules", "@qawolf", "testkit");
|
|
180251
180307
|
try {
|
|
180252
180308
|
const clientMod = await import(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
|
|
180253
180309
|
if (typeof clientMod.createTestkitClient !== "function")
|
|
180254
180310
|
throw new Error("createTestkitClient is not a function");
|
|
180255
|
-
const scopeMod = await import(
|
|
180311
|
+
const scopeMod = await import(join30(testkitDir, "dist", "clientScope.js"));
|
|
180256
180312
|
if (typeof scopeMod.configureTestkitClient !== "function")
|
|
180257
180313
|
throw new Error("configureTestkitClient is not a function");
|
|
180258
180314
|
return {
|
|
@@ -180309,9 +180365,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
|
|
|
180309
180365
|
}
|
|
180310
180366
|
|
|
180311
180367
|
// src/commands/flows/envVarDeps.ts
|
|
180312
|
-
import { join as
|
|
180368
|
+
import { join as join31 } from "node:path";
|
|
180313
180369
|
function envFilePath(envDir) {
|
|
180314
|
-
return
|
|
180370
|
+
return join31(envDir, ".env");
|
|
180315
180371
|
}
|
|
180316
180372
|
async function readEnv(envDir, fs3) {
|
|
180317
180373
|
try {
|
|
@@ -180420,7 +180476,7 @@ async function createFlowRuntimeDeps({
|
|
|
180420
180476
|
}
|
|
180421
180477
|
|
|
180422
180478
|
// src/shell/appium/createAppiumServer.ts
|
|
180423
|
-
import { join as
|
|
180479
|
+
import { join as join32 } from "node:path";
|
|
180424
180480
|
|
|
180425
180481
|
// src/shell/appium/spawnAppium.ts
|
|
180426
180482
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -180495,7 +180551,7 @@ async function createAppiumServer(envDir, signals, params) {
|
|
|
180495
180551
|
const spawnFn = params?.deps?.spawn ?? defaultSpawnAppium;
|
|
180496
180552
|
const findFreePortFn = params?.deps?.findFreePort ?? findFreePort;
|
|
180497
180553
|
const resolveAppiumBinFn = params?.deps?.resolveAppiumBin ?? resolveAppiumBin;
|
|
180498
|
-
const appiumHome2 = params?.options?.appiumHome ??
|
|
180554
|
+
const appiumHome2 = params?.options?.appiumHome ?? join32(envPaths("qawolf").data, "appium");
|
|
180499
180555
|
const timeoutMs3 = params?.options?.startTimeoutMs ?? defaultStartTimeoutMs;
|
|
180500
180556
|
const bin = resolveAppiumBinFn(envDir);
|
|
180501
180557
|
const port = await findFreePortFn();
|
|
@@ -180834,12 +180890,12 @@ Install Android Studio and open Tools > SDK Manager.`);
|
|
|
180834
180890
|
}
|
|
180835
180891
|
|
|
180836
180892
|
// src/shell/manifest/lookup.ts
|
|
180837
|
-
import { basename as basename5, dirname as
|
|
180893
|
+
import { basename as basename5, dirname as dirname11, relative as relative6 } from "node:path";
|
|
180838
180894
|
async function findFlowStamp(flowAbsPath) {
|
|
180839
180895
|
let envDir;
|
|
180840
|
-
let current =
|
|
180841
|
-
while (current !==
|
|
180842
|
-
const parent2 =
|
|
180896
|
+
let current = dirname11(flowAbsPath);
|
|
180897
|
+
while (current !== dirname11(current)) {
|
|
180898
|
+
const parent2 = dirname11(current);
|
|
180843
180899
|
if (basename5(parent2) === ".qawolf") {
|
|
180844
180900
|
envDir = current;
|
|
180845
180901
|
break;
|
|
@@ -180914,7 +180970,7 @@ function formatError2(browser, result) {
|
|
|
180914
180970
|
}
|
|
180915
180971
|
|
|
180916
180972
|
// src/domains/runner/runAndroidFlow.ts
|
|
180917
|
-
import
|
|
180973
|
+
import path12 from "node:path";
|
|
180918
180974
|
|
|
180919
180975
|
// src/shell/appium/createAndroidLaunchContext.ts
|
|
180920
180976
|
import { writeFile } from "node:fs/promises";
|
|
@@ -181260,66 +181316,6 @@ function createRunner({
|
|
|
181260
181316
|
}
|
|
181261
181317
|
|
|
181262
181318
|
// src/domains/runner/runWebFlowUtils.ts
|
|
181263
|
-
import crypto from "node:crypto";
|
|
181264
|
-
import os6 from "node:os";
|
|
181265
|
-
import path12 from "node:path";
|
|
181266
|
-
function createLaunch({
|
|
181267
|
-
browsers,
|
|
181268
|
-
contextSetup,
|
|
181269
|
-
launchBrowserOpts,
|
|
181270
|
-
signals,
|
|
181271
|
-
timeout,
|
|
181272
|
-
traceMode = "off",
|
|
181273
|
-
tracePath
|
|
181274
|
-
}) {
|
|
181275
|
-
const openBrowsers = [];
|
|
181276
|
-
const openContexts = [];
|
|
181277
|
-
const tracingEnabled = traceMode !== "off";
|
|
181278
|
-
let closed = false;
|
|
181279
|
-
const closeAll = async () => {
|
|
181280
|
-
if (closed)
|
|
181281
|
-
return;
|
|
181282
|
-
closed = true;
|
|
181283
|
-
if (tracingEnabled && tracePath !== undefined) {
|
|
181284
|
-
await Promise.allSettled(openContexts.map((c2) => c2.tracing.stop({ path: tracePath })));
|
|
181285
|
-
}
|
|
181286
|
-
await Promise.allSettled(openContexts.map((c2) => c2.close()));
|
|
181287
|
-
await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
|
|
181288
|
-
};
|
|
181289
|
-
const unregister = signals.register(closeAll);
|
|
181290
|
-
const trackContext = async (ctx) => {
|
|
181291
|
-
openContexts.push(ctx);
|
|
181292
|
-
if (tracingEnabled) {
|
|
181293
|
-
await ctx.tracing.start({ screenshots: true, snapshots: true });
|
|
181294
|
-
}
|
|
181295
|
-
};
|
|
181296
|
-
const launch = async (launchOpts) => {
|
|
181297
|
-
const browserName = normalizeBrowserName(launchOpts?.browser);
|
|
181298
|
-
const bt2 = browsers[browserName];
|
|
181299
|
-
if (launchOpts?.persistentContext === true) {
|
|
181300
|
-
const userDataDir = launchOpts.userDataDir ?? path12.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
|
|
181301
|
-
const context2 = await bt2.launchPersistentContext(userDataDir, {
|
|
181302
|
-
...launchBrowserOpts,
|
|
181303
|
-
...contextSetup
|
|
181304
|
-
});
|
|
181305
|
-
context2.setDefaultTimeout(timeout);
|
|
181306
|
-
await trackContext(context2);
|
|
181307
|
-
const page = await context2.newPage();
|
|
181308
|
-
return { browserType: browserName, context: context2, page };
|
|
181309
|
-
}
|
|
181310
|
-
const browser = await bt2.launch(launchBrowserOpts);
|
|
181311
|
-
openBrowsers.push(browser);
|
|
181312
|
-
const context = await browser.newContext(contextSetup);
|
|
181313
|
-
context.setDefaultTimeout(timeout);
|
|
181314
|
-
await trackContext(context);
|
|
181315
|
-
return { browser, browserType: browserName, context };
|
|
181316
|
-
};
|
|
181317
|
-
const cleanup = async () => {
|
|
181318
|
-
unregister();
|
|
181319
|
-
await closeAll();
|
|
181320
|
-
};
|
|
181321
|
-
return { launch, cleanup };
|
|
181322
|
-
}
|
|
181323
181319
|
function normalizeBrowserName(browser) {
|
|
181324
181320
|
if (browser === "chrome" || browser === "msedge")
|
|
181325
181321
|
return "chromium";
|
|
@@ -181359,7 +181355,7 @@ async function runAndroidFlow({
|
|
|
181359
181355
|
throw new Error("runAndroidFlow: legacy flow functions are not supported; use flow() from @qawolf/flows/android");
|
|
181360
181356
|
}
|
|
181361
181357
|
} catch (err) {
|
|
181362
|
-
const flowName2 =
|
|
181358
|
+
const flowName2 = path12.basename(flowPath, path12.extname(flowPath));
|
|
181363
181359
|
return {
|
|
181364
181360
|
passed: false,
|
|
181365
181361
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181427,13 +181423,13 @@ async function runAndroidFlow({
|
|
|
181427
181423
|
}
|
|
181428
181424
|
|
|
181429
181425
|
// src/domains/runner/runWebFlow.ts
|
|
181430
|
-
import
|
|
181426
|
+
import path17 from "node:path";
|
|
181431
181427
|
|
|
181432
181428
|
// src/domains/runner/initFlowRuntime.ts
|
|
181433
|
-
import
|
|
181429
|
+
import path13 from "node:path";
|
|
181434
181430
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
181435
181431
|
async function runnerPathInDir(dir, fs9) {
|
|
181436
|
-
const pkgPath =
|
|
181432
|
+
const pkgPath = path13.join(dir, "node_modules", "@qawolf", "flows", "package.json");
|
|
181437
181433
|
try {
|
|
181438
181434
|
const pkg = JSON.parse(await fs9.readFile(pkgPath));
|
|
181439
181435
|
const entry = pkg.exports?.["./_runner"];
|
|
@@ -181441,7 +181437,7 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181441
181437
|
if (typeof importPath !== "string") {
|
|
181442
181438
|
throw new Error(`@qawolf/flows at ${pkgPath} does not export "./_runner" with an "import" condition`);
|
|
181443
181439
|
}
|
|
181444
|
-
return
|
|
181440
|
+
return path13.resolve(path13.dirname(pkgPath), importPath);
|
|
181445
181441
|
} catch (err) {
|
|
181446
181442
|
if (!isNoEntError(err))
|
|
181447
181443
|
throw err;
|
|
@@ -181449,12 +181445,12 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181449
181445
|
}
|
|
181450
181446
|
}
|
|
181451
181447
|
async function findFlowsRunnerPath(flowPath, fs9) {
|
|
181452
|
-
let dir =
|
|
181448
|
+
let dir = path13.dirname(flowPath);
|
|
181453
181449
|
while (true) {
|
|
181454
181450
|
const result = await runnerPathInDir(dir, fs9);
|
|
181455
181451
|
if (result !== undefined)
|
|
181456
181452
|
return result;
|
|
181457
|
-
const parent2 =
|
|
181453
|
+
const parent2 = path13.dirname(dir);
|
|
181458
181454
|
if (parent2 === dir)
|
|
181459
181455
|
throw new Error(`@qawolf/flows not found in node_modules above: ${flowPath}`);
|
|
181460
181456
|
dir = parent2;
|
|
@@ -181486,7 +181482,7 @@ async function doInit(flowPath, timeout, fs9, depsRoot) {
|
|
|
181486
181482
|
});
|
|
181487
181483
|
}
|
|
181488
181484
|
function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
181489
|
-
const startDir =
|
|
181485
|
+
const startDir = path13.dirname(flowPath);
|
|
181490
181486
|
let p3 = initCache.get(startDir);
|
|
181491
181487
|
if (!p3) {
|
|
181492
181488
|
p3 = doInit(flowPath, options.timeout, fs9, options.depsRoot);
|
|
@@ -181495,13 +181491,137 @@ function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
|
181495
181491
|
return p3;
|
|
181496
181492
|
}
|
|
181497
181493
|
|
|
181498
|
-
// src/domains/runner/
|
|
181494
|
+
// src/domains/runner/createLaunch.ts
|
|
181495
|
+
import crypto from "node:crypto";
|
|
181496
|
+
import os6 from "node:os";
|
|
181499
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";
|
|
181500
181620
|
function harFlowPath(outputDir, flowName) {
|
|
181501
|
-
return
|
|
181621
|
+
return path16.join(outputDir, "har", `${flowName}.har`);
|
|
181502
181622
|
}
|
|
181503
181623
|
function traceFlowPath(outputDir, flowName) {
|
|
181504
|
-
return
|
|
181624
|
+
return path16.join(outputDir, "trace", `${flowName}.zip`);
|
|
181505
181625
|
}
|
|
181506
181626
|
function buildHarContextOpts(harPath, harContent) {
|
|
181507
181627
|
return {
|
|
@@ -181517,7 +181637,7 @@ async function initHar(fs9, options, flowName) {
|
|
|
181517
181637
|
if (harMode === "off")
|
|
181518
181638
|
return { harMode, harPath: undefined };
|
|
181519
181639
|
const harPath = harFlowPath(options.outputDir, flowName);
|
|
181520
|
-
await fs9.mkdir(
|
|
181640
|
+
await fs9.mkdir(path16.dirname(harPath), { recursive: true });
|
|
181521
181641
|
return { harMode, harPath };
|
|
181522
181642
|
}
|
|
181523
181643
|
async function initTrace(fs9, options, flowName) {
|
|
@@ -181525,7 +181645,7 @@ async function initTrace(fs9, options, flowName) {
|
|
|
181525
181645
|
if (traceMode === "off")
|
|
181526
181646
|
return { traceMode, tracePath: undefined };
|
|
181527
181647
|
const tracePath = traceFlowPath(options.outputDir, flowName);
|
|
181528
|
-
await fs9.mkdir(
|
|
181648
|
+
await fs9.mkdir(path16.dirname(tracePath), { recursive: true });
|
|
181529
181649
|
return { traceMode, tracePath };
|
|
181530
181650
|
}
|
|
181531
181651
|
async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
@@ -181538,7 +181658,7 @@ async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
|
181538
181658
|
}
|
|
181539
181659
|
}
|
|
181540
181660
|
function buildContextSetup(videoSize, options, harPath) {
|
|
181541
|
-
const videosDir = options.artifactDir ??
|
|
181661
|
+
const videosDir = options.artifactDir ?? path16.join(options.outputDir, "videos");
|
|
181542
181662
|
const harOpts = harPath !== undefined ? buildHarContextOpts(harPath, options.harContent ?? "omit") : {};
|
|
181543
181663
|
return options.video !== "off" ? {
|
|
181544
181664
|
viewport: videoSize,
|
|
@@ -181574,7 +181694,7 @@ async function runWebFlow({
|
|
|
181574
181694
|
depsRoot: deps.depsRoot
|
|
181575
181695
|
});
|
|
181576
181696
|
} catch (err) {
|
|
181577
|
-
const flowName2 =
|
|
181697
|
+
const flowName2 = path17.basename(flowPath, path17.extname(flowPath));
|
|
181578
181698
|
return {
|
|
181579
181699
|
passed: false,
|
|
181580
181700
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181583,7 +181703,7 @@ async function runWebFlow({
|
|
|
181583
181703
|
};
|
|
181584
181704
|
}
|
|
181585
181705
|
const isLegacy = typeof exported === "function";
|
|
181586
|
-
const flowName = isLegacy ?
|
|
181706
|
+
const flowName = isLegacy ? path17.basename(flowPath, path17.extname(flowPath)) : exported.name;
|
|
181587
181707
|
const runFn = isLegacy ? exported : exported.run;
|
|
181588
181708
|
const { harMode, harPath } = await initHar(deps.fs, options, flowName);
|
|
181589
181709
|
const { traceMode, tracePath } = await initTrace(deps.fs, options, flowName);
|
|
@@ -181594,7 +181714,7 @@ async function runWebFlow({
|
|
|
181594
181714
|
slowMo: options.slowMo,
|
|
181595
181715
|
...options.executablePath !== undefined ? { executablePath: options.executablePath } : {}
|
|
181596
181716
|
};
|
|
181597
|
-
const { launch, cleanup } = createLaunch({
|
|
181717
|
+
const { launch, cleanup, artifactPaths } = createLaunch({
|
|
181598
181718
|
browsers: {
|
|
181599
181719
|
chromium: deps.chromium,
|
|
181600
181720
|
firefox: deps.firefox,
|
|
@@ -181636,28 +181756,29 @@ async function runWebFlow({
|
|
|
181636
181756
|
} finally {
|
|
181637
181757
|
await cleanup();
|
|
181638
181758
|
const warn = (message) => deps.logger?.warn(message);
|
|
181639
|
-
|
|
181640
|
-
|
|
181759
|
+
const assigned = artifactPaths();
|
|
181760
|
+
for (const contextHarPath of assigned.harPaths) {
|
|
181761
|
+
await maybeCleanupHar(deps.fs, contextHarPath, passed, harMode, warn);
|
|
181641
181762
|
}
|
|
181642
|
-
|
|
181643
|
-
await maybeCleanupTrace(deps.fs,
|
|
181763
|
+
for (const contextTracePath of assigned.tracePaths) {
|
|
181764
|
+
await maybeCleanupTrace(deps.fs, contextTracePath, passed, traceMode, warn);
|
|
181644
181765
|
}
|
|
181645
181766
|
}
|
|
181646
181767
|
}
|
|
181647
181768
|
|
|
181648
181769
|
// src/shell/embeddedWorkerCli.ts
|
|
181649
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
181650
|
-
import { basename as basename6, join as
|
|
181770
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
181771
|
+
import { basename as basename6, join as join33 } from "node:path";
|
|
181651
181772
|
var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
|
|
181652
181773
|
function extractEmbeddedWorkerCli() {
|
|
181653
181774
|
const assetPath = process.env[embeddedCliPathEnv];
|
|
181654
181775
|
if (assetPath === undefined || assetPath === "")
|
|
181655
181776
|
return;
|
|
181656
|
-
const dir =
|
|
181657
|
-
const dest =
|
|
181777
|
+
const dir = join33(getDataDir(), "worker");
|
|
181778
|
+
const dest = join33(dir, basename6(assetPath));
|
|
181658
181779
|
if (existsSync3(dest))
|
|
181659
181780
|
return dest;
|
|
181660
|
-
|
|
181781
|
+
mkdirSync3(dir, { recursive: true });
|
|
181661
181782
|
writeFileSync2(dest, readFileSync2(assetPath));
|
|
181662
181783
|
return dest;
|
|
181663
181784
|
}
|
|
@@ -181807,7 +181928,7 @@ function makePooledDispatch(resolvedDir) {
|
|
|
181807
181928
|
}
|
|
181808
181929
|
|
|
181809
181930
|
// src/commands/flows/buildRunReporter.ts
|
|
181810
|
-
import { dirname as
|
|
181931
|
+
import { dirname as dirname12 } from "node:path";
|
|
181811
181932
|
|
|
181812
181933
|
// src/shell/reporter/createConsoleReporter.ts
|
|
181813
181934
|
import { styleText as styleText3 } from "node:util";
|
|
@@ -181957,7 +182078,7 @@ function createCompositeReporter(reporters) {
|
|
|
181957
182078
|
};
|
|
181958
182079
|
}
|
|
181959
182080
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181960
|
-
import { join as
|
|
182081
|
+
import { join as join34 } from "node:path";
|
|
181961
182082
|
|
|
181962
182083
|
// src/shell/reporter/junitXml.ts
|
|
181963
182084
|
function escapeXml(str) {
|
|
@@ -181996,7 +182117,7 @@ ${suites}`;
|
|
|
181996
182117
|
|
|
181997
182118
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181998
182119
|
function resolveJUnitOutputPath(junit, outputDir) {
|
|
181999
|
-
return typeof junit === "string" && junit.trim() !== "" ? junit :
|
|
182120
|
+
return typeof junit === "string" && junit.trim() !== "" ? junit : join34(outputDir, "junit-report.xml");
|
|
182000
182121
|
}
|
|
182001
182122
|
function createJUnitReporter(deps) {
|
|
182002
182123
|
const flows = [];
|
|
@@ -182035,7 +182156,7 @@ function buildRunReporter(flags, deps) {
|
|
|
182035
182156
|
const junit = createJUnitReporter({
|
|
182036
182157
|
outputPath: resolveJUnitOutputPath(flags.junit, flags.outputDir),
|
|
182037
182158
|
writeFile: (path9, content) => {
|
|
182038
|
-
deps.fs.mkdirSync(
|
|
182159
|
+
deps.fs.mkdirSync(dirname12(path9), { recursive: true });
|
|
182039
182160
|
deps.fs.writeFileSync(path9, content);
|
|
182040
182161
|
},
|
|
182041
182162
|
stderr
|
|
@@ -182083,11 +182204,11 @@ function buildFlowsRunDeps(args) {
|
|
|
182083
182204
|
|
|
182084
182205
|
// src/commands/flows/loadEnvFile.ts
|
|
182085
182206
|
import { readFile } from "node:fs/promises";
|
|
182086
|
-
import { join as
|
|
182207
|
+
import { join as join35 } from "node:path";
|
|
182087
182208
|
async function loadEnvFile(envDir) {
|
|
182088
182209
|
let content;
|
|
182089
182210
|
try {
|
|
182090
|
-
content = await readFile(
|
|
182211
|
+
content = await readFile(join35(envDir, ".env"), "utf8");
|
|
182091
182212
|
} catch (err) {
|
|
182092
182213
|
if (isNoEntError(err))
|
|
182093
182214
|
return;
|
|
@@ -182187,7 +182308,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182187
182308
|
}
|
|
182188
182309
|
|
|
182189
182310
|
// src/commands/flows/hybridRunDefaults.ts
|
|
182190
|
-
import { join as
|
|
182311
|
+
import { join as join36, resolve as resolve9 } from "node:path";
|
|
182191
182312
|
function makeDefaultHybridDeps(fs9) {
|
|
182192
182313
|
return {
|
|
182193
182314
|
expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
|
|
@@ -182206,7 +182327,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182206
182327
|
if (validation !== "ok") {
|
|
182207
182328
|
return { error: validation.error, exitCode: 2 };
|
|
182208
182329
|
}
|
|
182209
|
-
const envDir = resolve9(
|
|
182330
|
+
const envDir = resolve9(join36(".qawolf", flags.env));
|
|
182210
182331
|
const patternArgs = buildPatternArgs(pattern);
|
|
182211
182332
|
const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
|
|
182212
182333
|
let files = await globFlows();
|
|
@@ -182280,7 +182401,7 @@ async function runWorker(signals) {
|
|
|
182280
182401
|
throw new Error("worker subprocess currently supports web flows only");
|
|
182281
182402
|
await configureTestkit(input.resolvedDir);
|
|
182282
182403
|
const fs9 = makeDefaultFs();
|
|
182283
|
-
const apiBaseUrl = process.env
|
|
182404
|
+
const apiBaseUrl = resolveHostUrl(process.env);
|
|
182284
182405
|
const flowRuntimeDeps = await createFlowRuntimeDeps({
|
|
182285
182406
|
envDir: input.resolvedDir,
|
|
182286
182407
|
ctx: { apiBaseUrl, configDir: getConfigDir(), fs: fs9 }
|
|
@@ -182321,8 +182442,8 @@ var listExamples = `
|
|
|
182321
182442
|
Examples:
|
|
182322
182443
|
$ qawolf flows list
|
|
182323
182444
|
$ qawolf flows list "flows/checkout/**"
|
|
182324
|
-
$ qawolf flows list --remote
|
|
182325
|
-
$ qawolf flows list "**/checkout/**" --remote`;
|
|
182445
|
+
$ qawolf flows list --remote --env staging
|
|
182446
|
+
$ qawolf flows list "**/checkout/**" --remote --env staging --include-drafts`;
|
|
182326
182447
|
var pullExamples = `
|
|
182327
182448
|
Examples:
|
|
182328
182449
|
$ qawolf flows pull --env staging
|
|
@@ -182334,9 +182455,23 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182334
182455
|
registerRunWorkerCommand(flows, signals);
|
|
182335
182456
|
declareCommandKind(flows.command("list [pattern]"), "local", {
|
|
182336
182457
|
kindNote: "read with --remote"
|
|
182337
|
-
}).description("List flows matching [pattern] from the local project, or from QA Wolf with --remote").option("--remote", "List flows from the QA Wolf platform instead of the local project", false).addHelpText("after", listExamples).action((pattern, opts, command) => {
|
|
182458
|
+
}).description("List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote").option("--remote", "List flows from the QA Wolf platform instead of the local project", false).option("--env <env>", "Environment to list flows from (required with --remote)").option("--include-drafts", "Include draft flows in the listing (requires --remote)", false).addHelpText("after", listExamples).action((pattern, opts, command) => {
|
|
182338
182459
|
if (opts.remote) {
|
|
182339
|
-
|
|
182460
|
+
const env3 = opts.env;
|
|
182461
|
+
if (env3 === undefined) {
|
|
182462
|
+
return withContext(signals, async () => ({
|
|
182463
|
+
error: flowsMessages.list.remoteRequiresEnv
|
|
182464
|
+
}))(opts, command);
|
|
182465
|
+
}
|
|
182466
|
+
return withAuthContext(signals, (ctx) => flowsListRemote(ctx, pattern, {
|
|
182467
|
+
env: env3,
|
|
182468
|
+
includeDrafts: opts.includeDrafts
|
|
182469
|
+
}))(opts, command);
|
|
182470
|
+
}
|
|
182471
|
+
if (opts.env !== undefined || opts.includeDrafts) {
|
|
182472
|
+
return withContext(signals, async () => ({
|
|
182473
|
+
error: flowsMessages.list.flagsRequireRemote
|
|
182474
|
+
}))(opts, command);
|
|
182340
182475
|
}
|
|
182341
182476
|
return withContext(signals, (ctx) => handleFlowsList(ctx, pattern))(opts, command);
|
|
182342
182477
|
});
|
|
@@ -182346,7 +182481,7 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182346
182481
|
}
|
|
182347
182482
|
|
|
182348
182483
|
// src/domains/init/init.ts
|
|
182349
|
-
import { dirname as
|
|
182484
|
+
import { dirname as dirname13, join as join37, relative as relative7 } from "node:path";
|
|
182350
182485
|
|
|
182351
182486
|
// src/domains/init/templates.ts
|
|
182352
182487
|
var qawolfConfigTs = `export default {
|
|
@@ -182384,9 +182519,9 @@ function makeDefaultInitDeps() {
|
|
|
182384
182519
|
async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
|
|
182385
182520
|
ctx.ui.gap();
|
|
182386
182521
|
ctx.ui.intro(initMessages.title);
|
|
182387
|
-
const configPath =
|
|
182388
|
-
const flowPath =
|
|
182389
|
-
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");
|
|
182390
182525
|
await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
|
|
182391
182526
|
await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
|
|
182392
182527
|
await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
|
|
@@ -182405,12 +182540,12 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
|
|
|
182405
182540
|
return;
|
|
182406
182541
|
}
|
|
182407
182542
|
}
|
|
182408
|
-
await deps.fs.mkdir(
|
|
182543
|
+
await deps.fs.mkdir(dirname13(filePath), { recursive: true });
|
|
182409
182544
|
await deps.fs.writeFile(filePath, content);
|
|
182410
182545
|
ctx.ui.step(initMessages.createdFile(relPath));
|
|
182411
182546
|
}
|
|
182412
182547
|
async function ensurePackageJson(ctx, yes, deps) {
|
|
182413
|
-
const pkgPath =
|
|
182548
|
+
const pkgPath = join37(deps.cwd, "package.json");
|
|
182414
182549
|
if (!await deps.fs.pathExists(pkgPath)) {
|
|
182415
182550
|
const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
|
|
182416
182551
|
if (!confirmed2.ok || !confirmed2.value) {
|
|
@@ -182469,19 +182604,19 @@ Examples:
|
|
|
182469
182604
|
}
|
|
182470
182605
|
|
|
182471
182606
|
// src/commands/install/android.ts
|
|
182472
|
-
import { join as
|
|
182607
|
+
import { join as join40 } from "node:path";
|
|
182473
182608
|
|
|
182474
182609
|
// src/domains/install/android/index.ts
|
|
182475
182610
|
import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
|
|
182476
182611
|
|
|
182477
182612
|
// src/domains/install/android/avd.ts
|
|
182478
|
-
import { join as
|
|
182613
|
+
import { join as join38 } from "node:path";
|
|
182479
182614
|
async function installAvds(ctx, specs, deps) {
|
|
182480
182615
|
const versionResult = await deps.spawn(deps.sdkManagerPath, ["--version"]);
|
|
182481
182616
|
if (versionResult.exitCode !== 0) {
|
|
182482
182617
|
throw new Error(installMessages.android.sdkmanagerNotFound(deps.sdkManagerPath));
|
|
182483
182618
|
}
|
|
182484
|
-
const licenseFile =
|
|
182619
|
+
const licenseFile = join38(deps.androidHome, "licenses", "android-sdk-license");
|
|
182485
182620
|
if (deps.checkExists(licenseFile)) {
|
|
182486
182621
|
ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
|
|
182487
182622
|
} else {
|
|
@@ -182495,7 +182630,7 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182495
182630
|
for (const spec of specs) {
|
|
182496
182631
|
if (installedImages.has(spec.systemImage))
|
|
182497
182632
|
continue;
|
|
182498
|
-
const systemImg =
|
|
182633
|
+
const systemImg = join38(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
|
|
182499
182634
|
if (deps.checkExists(systemImg)) {
|
|
182500
182635
|
ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
|
|
182501
182636
|
installedImages.add(spec.systemImage);
|
|
@@ -182543,9 +182678,9 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182543
182678
|
}
|
|
182544
182679
|
|
|
182545
182680
|
// src/domains/install/android/driver.ts
|
|
182546
|
-
import { join as
|
|
182681
|
+
import { join as join39 } from "node:path";
|
|
182547
182682
|
var appiumEnv = {
|
|
182548
|
-
APPIUM_HOME:
|
|
182683
|
+
APPIUM_HOME: join39(envPaths("qawolf").data, "appium")
|
|
182549
182684
|
};
|
|
182550
182685
|
async function installUiautomator2Driver(ctx, deps) {
|
|
182551
182686
|
const listResult = await deps.spawn(deps.appiumBinPath, ["driver", "list", "--installed"], { env: appiumEnv });
|
|
@@ -182632,8 +182767,8 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
|
|
|
182632
182767
|
arch: process.arch,
|
|
182633
182768
|
androidHome: androidHome2,
|
|
182634
182769
|
checkExists: (path9) => fs9.existsSync(path9),
|
|
182635
|
-
sdkManagerPath:
|
|
182636
|
-
avdManagerPath:
|
|
182770
|
+
sdkManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
|
|
182771
|
+
avdManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
|
|
182637
182772
|
expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
|
|
182638
182773
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182639
182774
|
resolveDepsRoot: async (files) => envDir ?? (await resolveDepsRoot({ files, fs: fs9 })).depsRoot,
|
|
@@ -182779,39 +182914,6 @@ Examples:
|
|
|
182779
182914
|
});
|
|
182780
182915
|
}
|
|
182781
182916
|
|
|
182782
|
-
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
182783
|
-
var defaultIdSchemas = {
|
|
182784
|
-
environment: exports_external.string().min(1).describe("The id of the environment."),
|
|
182785
|
-
flow: exports_external.string().min(1).describe("The id of the flow."),
|
|
182786
|
-
run: exports_external.string().min(1).describe("The id of the run.")
|
|
182787
|
-
};
|
|
182788
|
-
|
|
182789
|
-
// node_modules/@qawolf/api-contracts/dist/v1/run/index.js
|
|
182790
|
-
var makeCreateRunContract = (ids) => {
|
|
182791
|
-
const input = exports_external.object({
|
|
182792
|
-
environmentId: ids.environment,
|
|
182793
|
-
environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
182794
|
-
flowIds: exports_external.array(ids.flow).min(1),
|
|
182795
|
-
ignoreRules: exports_external.boolean().default(false)
|
|
182796
|
-
});
|
|
182797
|
-
const output = exports_external.object({ runId: ids.run });
|
|
182798
|
-
return {
|
|
182799
|
-
description: "Create a run for selected flows in an environment.",
|
|
182800
|
-
input,
|
|
182801
|
-
kind: "write",
|
|
182802
|
-
name: "run.create",
|
|
182803
|
-
output
|
|
182804
|
-
};
|
|
182805
|
-
};
|
|
182806
|
-
|
|
182807
|
-
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
182808
|
-
var makeContractsV1 = (ids) => ({
|
|
182809
|
-
run: {
|
|
182810
|
-
create: makeCreateRunContract(ids)
|
|
182811
|
-
}
|
|
182812
|
-
});
|
|
182813
|
-
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
182814
|
-
|
|
182815
182917
|
// src/core/publicApi/flagSpecs.ts
|
|
182816
182918
|
var kebabCase = (field) => field.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
182817
182919
|
function flagKind(schema) {
|
|
@@ -182908,8 +183010,16 @@ function buildSpec(contract, path9) {
|
|
|
182908
183010
|
contract
|
|
182909
183011
|
};
|
|
182910
183012
|
}
|
|
182911
|
-
function buildCommandSpecs(tree) {
|
|
182912
|
-
const
|
|
183013
|
+
function buildCommandSpecs(tree, options = {}) {
|
|
183014
|
+
const skip = options.skipContractNames;
|
|
183015
|
+
const walk3 = (node2, path9) => Object.entries(node2).flatMap(([key, value]) => {
|
|
183016
|
+
const childPath = [...path9, key];
|
|
183017
|
+
if (!isContract(value))
|
|
183018
|
+
return walk3(value, childPath);
|
|
183019
|
+
if (skip?.has(childPath.join(".")))
|
|
183020
|
+
return [];
|
|
183021
|
+
return [buildSpec(value, childPath)];
|
|
183022
|
+
});
|
|
182913
183023
|
return walk3(tree, []);
|
|
182914
183024
|
}
|
|
182915
183025
|
|
|
@@ -182980,6 +183090,7 @@ async function handlePublicApiCommand(ctx, spec, options) {
|
|
|
182980
183090
|
var groupDescriptions = {
|
|
182981
183091
|
run: "Trigger and manage QA Wolf runs on the platform"
|
|
182982
183092
|
};
|
|
183093
|
+
var handWrittenContractNames = new Set(["flow.list"]);
|
|
182983
183094
|
function resolveGroup(parent2, segment) {
|
|
182984
183095
|
const existing = parent2.commands.find((command) => command.name() === segment);
|
|
182985
183096
|
return existing ?? parent2.command(segment).description(groupDescriptions[segment] ?? `QA Wolf public API ${segment} commands`);
|
|
@@ -183005,7 +183116,9 @@ function registerSpec(program2, signals, spec, authDeps) {
|
|
|
183005
183116
|
command.action((options, leaf) => withAuthContext(signals, (ctx) => handlePublicApiCommand(ctx, spec, options), authDeps ?? {})(options, leaf));
|
|
183006
183117
|
}
|
|
183007
183118
|
function registerPublicApiCommands(program2, signals, options = {}) {
|
|
183008
|
-
const specs = buildCommandSpecs(options.contracts ?? publicContractsV1
|
|
183119
|
+
const specs = buildCommandSpecs(options.contracts ?? publicContractsV1, {
|
|
183120
|
+
skipContractNames: handWrittenContractNames
|
|
183121
|
+
});
|
|
183009
183122
|
for (const spec of specs) {
|
|
183010
183123
|
registerSpec(program2, signals, spec, options.authDeps);
|
|
183011
183124
|
}
|
|
@@ -183046,4 +183159,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
183046
183159
|
process.exitCode = 1;
|
|
183047
183160
|
}).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
183048
183161
|
|
|
183049
|
-
//# debugId=
|
|
183162
|
+
//# debugId=724E44C8F9B9A9E264756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.2
|
|
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",
|
|
@@ -60,16 +60,17 @@
|
|
|
60
60
|
"@napi-rs/keyring": "1.3.0",
|
|
61
61
|
"@oxc-node/core": "0.1.0",
|
|
62
62
|
"@playwright/test": "1.60.0",
|
|
63
|
-
"@qawolf/api-contracts": "0.
|
|
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`).
|
|
@@ -41,7 +46,7 @@ write on timeout: it may have reached the server the first time.
|
|
|
41
46
|
| `qawolf auth logout` | local | Remove stored credentials |
|
|
42
47
|
| `qawolf auth whoami` | read | Show authentication status |
|
|
43
48
|
| `qawolf doctor` | local | Diagnose problems running flows locally |
|
|
44
|
-
| `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from QA Wolf with --remote
|
|
49
|
+
| `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote |
|
|
45
50
|
| `qawolf flows pull` | read | Download an environment's flows into the local .qawolf/<env>/ cache |
|
|
46
51
|
| `qawolf flows run` | local (read with --env) | Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment |
|
|
47
52
|
| `qawolf init` | local | Scaffold a QA Wolf project in the current directory |
|