@validate.qa/runner 1.0.6 → 1.0.9
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 +90 -36
- package/dist/cli.mjs +90 -36
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1264,7 +1264,8 @@ var init_constants = __esm({
|
|
|
1264
1264
|
"GROK_API_KEY",
|
|
1265
1265
|
"MINIMAX_API_KEY",
|
|
1266
1266
|
"ANTHROPIC_API_KEY",
|
|
1267
|
-
"NVIDIA_API_KEY"
|
|
1267
|
+
"NVIDIA_API_KEY",
|
|
1268
|
+
"DEEPSEEK_API_KEY"
|
|
1268
1269
|
]);
|
|
1269
1270
|
SPAWNED_ENV_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
1270
1271
|
// Runtime essentials - Node.js and Playwright need these to function
|
|
@@ -1605,11 +1606,11 @@ function buildAppiumCode(testName, target, steps) {
|
|
|
1605
1606
|
const x = Math.round(bounds.x + bounds.width / 2);
|
|
1606
1607
|
const y = Math.round(bounds.y + bounds.height / 2);
|
|
1607
1608
|
lines.push(` try {`);
|
|
1608
|
-
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
1609
|
-
lines.push(` } catch {`);
|
|
1610
1609
|
lines.push(` await __tapAt(driver, ${x}, ${y});`);
|
|
1611
1610
|
lines.push(` await driver.pause(${MOBILE_FOCUS_SETTLE_MS});`);
|
|
1612
1611
|
lines.push(` await __typeIntoFocused(driver, ${toJsStringLiteral(step.value ?? "")});`);
|
|
1612
|
+
lines.push(` } catch {`);
|
|
1613
|
+
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
1613
1614
|
lines.push(` }`);
|
|
1614
1615
|
} else {
|
|
1615
1616
|
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
@@ -2133,6 +2134,7 @@ var init_audit_pricing = __esm({
|
|
|
2133
2134
|
"MiniMax-M2.7": { input: 0.3, output: 1.2 },
|
|
2134
2135
|
"MiniMax-M2.7-highspeed": { input: 0.6, output: 2.4 },
|
|
2135
2136
|
"MiniMax-M2": { input: 0.3, output: 1.2 },
|
|
2137
|
+
"deepseek-v4-flash": { input: 0.14, output: 0.28 },
|
|
2136
2138
|
// Whisper bills per-minute, not per-token
|
|
2137
2139
|
"whisper-1": null,
|
|
2138
2140
|
// Historical / deprecated (kept so old audit rows still show a cost).
|
|
@@ -12036,7 +12038,7 @@ var require_kill_tree = __commonJS({
|
|
|
12036
12038
|
var node_child_process_1 = require("child_process");
|
|
12037
12039
|
var node_util_1 = require("util");
|
|
12038
12040
|
var node_fs_1 = require("fs");
|
|
12039
|
-
var
|
|
12041
|
+
var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
12040
12042
|
var PS_TIMEOUT_MS = 2e3;
|
|
12041
12043
|
var PS_BUFFER = 1024 * 1024;
|
|
12042
12044
|
async function gracefulCloseWithTreeKill(options) {
|
|
@@ -12090,7 +12092,7 @@ var require_kill_tree = __commonJS({
|
|
|
12090
12092
|
timer.unref?.();
|
|
12091
12093
|
let stdout;
|
|
12092
12094
|
try {
|
|
12093
|
-
const result = await
|
|
12095
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
|
|
12094
12096
|
maxBuffer: PS_BUFFER,
|
|
12095
12097
|
signal: controller.signal
|
|
12096
12098
|
});
|
|
@@ -12128,7 +12130,7 @@ var require_kill_tree = __commonJS({
|
|
|
12128
12130
|
timer.unref?.();
|
|
12129
12131
|
let stdout;
|
|
12130
12132
|
try {
|
|
12131
|
-
const result = await
|
|
12133
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
|
|
12132
12134
|
maxBuffer: PS_BUFFER,
|
|
12133
12135
|
signal: controller.signal
|
|
12134
12136
|
});
|
|
@@ -12205,7 +12207,7 @@ var require_kill_tree = __commonJS({
|
|
|
12205
12207
|
timer.unref?.();
|
|
12206
12208
|
let stdout;
|
|
12207
12209
|
try {
|
|
12208
|
-
const result = await
|
|
12210
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid="], {
|
|
12209
12211
|
maxBuffer: PS_BUFFER,
|
|
12210
12212
|
signal: controller.signal
|
|
12211
12213
|
});
|
|
@@ -321093,7 +321095,7 @@ var require_update = __commonJS({
|
|
|
321093
321095
|
var require_opts_arg = __commonJS({
|
|
321094
321096
|
"../../node_modules/tar/node_modules/mkdirp/lib/opts-arg.js"(exports2, module2) {
|
|
321095
321097
|
"use strict";
|
|
321096
|
-
var { promisify:
|
|
321098
|
+
var { promisify: promisify3 } = require("util");
|
|
321097
321099
|
var fs = require("fs");
|
|
321098
321100
|
var optsArg = (opts) => {
|
|
321099
321101
|
if (!opts)
|
|
@@ -321107,9 +321109,9 @@ var require_opts_arg = __commonJS({
|
|
|
321107
321109
|
else
|
|
321108
321110
|
throw new TypeError("invalid options argument");
|
|
321109
321111
|
opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir;
|
|
321110
|
-
opts.mkdirAsync =
|
|
321112
|
+
opts.mkdirAsync = promisify3(opts.mkdir);
|
|
321111
321113
|
opts.stat = opts.stat || opts.fs.stat || fs.stat;
|
|
321112
|
-
opts.statAsync =
|
|
321114
|
+
opts.statAsync = promisify3(opts.stat);
|
|
321113
321115
|
opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync;
|
|
321114
321116
|
opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync;
|
|
321115
321117
|
return opts;
|
|
@@ -325202,7 +325204,7 @@ var require_process_inventory = __commonJS({
|
|
|
325202
325204
|
var node_child_process_1 = require("child_process");
|
|
325203
325205
|
var node_os_1 = __importDefault(require("os"));
|
|
325204
325206
|
var node_util_1 = require("util");
|
|
325205
|
-
var
|
|
325207
|
+
var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
325206
325208
|
var DEFAULT_LIMIT = 60;
|
|
325207
325209
|
var DEFAULT_TIMEOUT_MS = 800;
|
|
325208
325210
|
var MAX_COMMAND_LENGTH = 320;
|
|
@@ -325264,7 +325266,7 @@ var require_process_inventory = __commonJS({
|
|
|
325264
325266
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
325265
325267
|
timer.unref?.();
|
|
325266
325268
|
try {
|
|
325267
|
-
const { stdout } = await
|
|
325269
|
+
const { stdout } = await execFileAsync3(command, args, {
|
|
325268
325270
|
maxBuffer: 1024 * 1024,
|
|
325269
325271
|
signal: controller.signal
|
|
325270
325272
|
});
|
|
@@ -326286,7 +326288,7 @@ var require_package4 = __commonJS({
|
|
|
326286
326288
|
"package.json"(exports2, module2) {
|
|
326287
326289
|
module2.exports = {
|
|
326288
326290
|
name: "@validate.qa/runner",
|
|
326289
|
-
version: "1.0.
|
|
326291
|
+
version: "1.0.9",
|
|
326290
326292
|
description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
|
|
326291
326293
|
bin: {
|
|
326292
326294
|
"validate-runner": "dist/cli.js"
|
|
@@ -326419,6 +326421,8 @@ function shouldUseAuthState(tags, playwrightCode) {
|
|
|
326419
326421
|
|
|
326420
326422
|
// src/services/appium-executor.ts
|
|
326421
326423
|
init_dist();
|
|
326424
|
+
var import_node_child_process2 = require("child_process");
|
|
326425
|
+
var import_node_util2 = require("util");
|
|
326422
326426
|
var import_runner_core8 = __toESM(require_dist2());
|
|
326423
326427
|
|
|
326424
326428
|
// src/services/appium-lifecycle.ts
|
|
@@ -327599,6 +327603,7 @@ async function startMobileNetworkCapture(options) {
|
|
|
327599
327603
|
|
|
327600
327604
|
// src/services/appium-executor.ts
|
|
327601
327605
|
var DEFAULT_STEP_TIMEOUT_MS = 1e4;
|
|
327606
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
327602
327607
|
var WebDriverTransportError = class extends Error {
|
|
327603
327608
|
isTransport = true;
|
|
327604
327609
|
constructor(message) {
|
|
@@ -327920,6 +327925,30 @@ async function sendKeysToFocusedInput(sessionId, value) {
|
|
|
327920
327925
|
body: JSON.stringify({ text: value, value: Array.from(value) })
|
|
327921
327926
|
});
|
|
327922
327927
|
}
|
|
327928
|
+
function encodeAndroidInputText(value) {
|
|
327929
|
+
return value.replace(/%/g, "%25").replace(/\s/g, "%s");
|
|
327930
|
+
}
|
|
327931
|
+
async function sendAndroidTextToFocusedInput(deviceId, value) {
|
|
327932
|
+
if (!deviceId) {
|
|
327933
|
+
throw new Error("Android keyboard fallback requires a device id");
|
|
327934
|
+
}
|
|
327935
|
+
await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
|
|
327936
|
+
timeout: 1e4,
|
|
327937
|
+
windowsHide: true
|
|
327938
|
+
});
|
|
327939
|
+
}
|
|
327940
|
+
async function trySendElementValue(sessionId, elementId, value) {
|
|
327941
|
+
try {
|
|
327942
|
+
await wdRequest(`/session/${sessionId}/element/${elementId}/value`, {
|
|
327943
|
+
method: "POST",
|
|
327944
|
+
body: JSON.stringify({ text: value })
|
|
327945
|
+
});
|
|
327946
|
+
return null;
|
|
327947
|
+
} catch (error2) {
|
|
327948
|
+
if (isTransportError(error2)) throw error2;
|
|
327949
|
+
return error2;
|
|
327950
|
+
}
|
|
327951
|
+
}
|
|
327923
327952
|
function shouldAbortAfterStepFailure(action) {
|
|
327924
327953
|
switch (action) {
|
|
327925
327954
|
case "launchApp":
|
|
@@ -327956,17 +327985,33 @@ async function executeTap(sessionId, step) {
|
|
|
327956
327985
|
}
|
|
327957
327986
|
await tapCoordinates(sessionId, bounds);
|
|
327958
327987
|
}
|
|
327959
|
-
async function executeType(sessionId, step) {
|
|
327988
|
+
async function executeType(sessionId, step, context) {
|
|
327960
327989
|
if (step.value === void 0 || step.value === null || step.value === "") return;
|
|
327961
327990
|
const value = String(step.value);
|
|
327962
327991
|
const bounds = boundsFromStep(step);
|
|
327963
327992
|
let elementId = null;
|
|
327993
|
+
let lastInputError = null;
|
|
327964
327994
|
let tappedBounds = false;
|
|
327995
|
+
if (bounds) {
|
|
327996
|
+
await tapCoordinates(sessionId, bounds);
|
|
327997
|
+
tappedBounds = true;
|
|
327998
|
+
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
327999
|
+
elementId = await getActiveElementId(sessionId);
|
|
328000
|
+
if (elementId) {
|
|
328001
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328002
|
+
if (!lastInputError) return;
|
|
328003
|
+
elementId = null;
|
|
328004
|
+
}
|
|
328005
|
+
}
|
|
327965
328006
|
if (step.target) {
|
|
327966
328007
|
try {
|
|
327967
328008
|
elementId = await findElement(sessionId, step.target, readStepTimeout(step));
|
|
328009
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328010
|
+
if (!lastInputError) return;
|
|
328011
|
+
elementId = null;
|
|
327968
328012
|
} catch (error2) {
|
|
327969
328013
|
if (!bounds) throw error2;
|
|
328014
|
+
lastInputError = error2;
|
|
327970
328015
|
}
|
|
327971
328016
|
}
|
|
327972
328017
|
if (!elementId && bounds) {
|
|
@@ -327979,25 +328024,39 @@ async function executeType(sessionId, step) {
|
|
|
327979
328024
|
elementId = await getActiveElementId(sessionId);
|
|
327980
328025
|
}
|
|
327981
328026
|
if (elementId) {
|
|
327982
|
-
|
|
327983
|
-
|
|
327984
|
-
|
|
327985
|
-
|
|
327986
|
-
|
|
327987
|
-
return;
|
|
327988
|
-
} catch (error2) {
|
|
327989
|
-
if (!bounds) throw error2;
|
|
328027
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328028
|
+
if (!lastInputError) return;
|
|
328029
|
+
elementId = null;
|
|
328030
|
+
if (!bounds) {
|
|
328031
|
+
throw lastInputError instanceof Error ? lastInputError : new Error(String(lastInputError));
|
|
327990
328032
|
}
|
|
327991
328033
|
}
|
|
327992
328034
|
if (bounds) {
|
|
327993
328035
|
if (!tappedBounds) {
|
|
327994
328036
|
await tapCoordinates(sessionId, bounds);
|
|
328037
|
+
tappedBounds = true;
|
|
327995
328038
|
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
327996
328039
|
}
|
|
327997
|
-
|
|
327998
|
-
|
|
328040
|
+
try {
|
|
328041
|
+
await sendKeysToFocusedInput(sessionId, value);
|
|
328042
|
+
return;
|
|
328043
|
+
} catch (error2) {
|
|
328044
|
+
if (isTransportError(error2)) throw error2;
|
|
328045
|
+
lastInputError = error2;
|
|
328046
|
+
}
|
|
327999
328047
|
}
|
|
328000
|
-
|
|
328048
|
+
if (context.platform === "ANDROID" && tappedBounds) {
|
|
328049
|
+
try {
|
|
328050
|
+
context.log?.(" Appium active-element send failed; using Android focused-keyboard input fallback");
|
|
328051
|
+
await sendAndroidTextToFocusedInput(context.deviceId, value);
|
|
328052
|
+
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
328053
|
+
return;
|
|
328054
|
+
} catch (error2) {
|
|
328055
|
+
if (isTransportError(error2)) throw error2;
|
|
328056
|
+
lastInputError = error2;
|
|
328057
|
+
}
|
|
328058
|
+
}
|
|
328059
|
+
throw new Error(`TYPE step could not send text to the focused target${lastInputError instanceof Error ? `: ${lastInputError.message}` : ""}`);
|
|
328001
328060
|
}
|
|
328002
328061
|
async function executeClear(sessionId, step) {
|
|
328003
328062
|
const bounds = boundsFromStep(step);
|
|
@@ -328218,7 +328277,11 @@ async function executeMobileTest(options) {
|
|
|
328218
328277
|
await executeTap(sessionId, step);
|
|
328219
328278
|
break;
|
|
328220
328279
|
case "type":
|
|
328221
|
-
await executeType(sessionId, step
|
|
328280
|
+
await executeType(sessionId, step, {
|
|
328281
|
+
platform: options.target.platform,
|
|
328282
|
+
deviceId: selectedDevice.id,
|
|
328283
|
+
log: log2
|
|
328284
|
+
});
|
|
328222
328285
|
break;
|
|
328223
328286
|
case "clear":
|
|
328224
328287
|
await executeClear(sessionId, step);
|
|
@@ -330657,16 +330720,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
330657
330720
|
}
|
|
330658
330721
|
return;
|
|
330659
330722
|
}
|
|
330660
|
-
if (runMode === "heal") {
|
|
330661
|
-
if (run2.framework === "APPIUM") {
|
|
330662
|
-
await postResult(opts.serverUrl, headers, run2.runId, {
|
|
330663
|
-
status: "ERROR",
|
|
330664
|
-
error: "CONFIG_ISSUE: Appium (mobile) tests are not auto-healable; skipped heal.",
|
|
330665
|
-
duration: Date.now() - startTime
|
|
330666
|
-
});
|
|
330667
|
-
log.warn("Skipped heal for APPIUM test (mobile tests are not healable)");
|
|
330668
|
-
return;
|
|
330669
|
-
}
|
|
330723
|
+
if (runMode === "heal" && run2.framework !== "APPIUM") {
|
|
330670
330724
|
const isApiTest2 = Array.isArray(run2.tags) && run2.tags.includes("@api");
|
|
330671
330725
|
if (isApiTest2) {
|
|
330672
330726
|
const testCode = run2.playwrightCode ?? "";
|
package/dist/cli.mjs
CHANGED
|
@@ -1269,7 +1269,8 @@ var init_constants = __esm({
|
|
|
1269
1269
|
"GROK_API_KEY",
|
|
1270
1270
|
"MINIMAX_API_KEY",
|
|
1271
1271
|
"ANTHROPIC_API_KEY",
|
|
1272
|
-
"NVIDIA_API_KEY"
|
|
1272
|
+
"NVIDIA_API_KEY",
|
|
1273
|
+
"DEEPSEEK_API_KEY"
|
|
1273
1274
|
]);
|
|
1274
1275
|
SPAWNED_ENV_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
1275
1276
|
// Runtime essentials - Node.js and Playwright need these to function
|
|
@@ -1610,11 +1611,11 @@ function buildAppiumCode(testName, target, steps) {
|
|
|
1610
1611
|
const x = Math.round(bounds.x + bounds.width / 2);
|
|
1611
1612
|
const y = Math.round(bounds.y + bounds.height / 2);
|
|
1612
1613
|
lines.push(` try {`);
|
|
1613
|
-
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
1614
|
-
lines.push(` } catch {`);
|
|
1615
1614
|
lines.push(` await __tapAt(driver, ${x}, ${y});`);
|
|
1616
1615
|
lines.push(` await driver.pause(${MOBILE_FOCUS_SETTLE_MS});`);
|
|
1617
1616
|
lines.push(` await __typeIntoFocused(driver, ${toJsStringLiteral(step.value ?? "")});`);
|
|
1617
|
+
lines.push(` } catch {`);
|
|
1618
|
+
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
1618
1619
|
lines.push(` }`);
|
|
1619
1620
|
} else {
|
|
1620
1621
|
lines.push(` await (await findFirst(driver, ${candidatesLiteral})).addValue(${toJsStringLiteral(step.value ?? "")});`);
|
|
@@ -2138,6 +2139,7 @@ var init_audit_pricing = __esm({
|
|
|
2138
2139
|
"MiniMax-M2.7": { input: 0.3, output: 1.2 },
|
|
2139
2140
|
"MiniMax-M2.7-highspeed": { input: 0.6, output: 2.4 },
|
|
2140
2141
|
"MiniMax-M2": { input: 0.3, output: 1.2 },
|
|
2142
|
+
"deepseek-v4-flash": { input: 0.14, output: 0.28 },
|
|
2141
2143
|
// Whisper bills per-minute, not per-token
|
|
2142
2144
|
"whisper-1": null,
|
|
2143
2145
|
// Historical / deprecated (kept so old audit rows still show a cost).
|
|
@@ -12041,7 +12043,7 @@ var require_kill_tree = __commonJS({
|
|
|
12041
12043
|
var node_child_process_1 = __require("child_process");
|
|
12042
12044
|
var node_util_1 = __require("util");
|
|
12043
12045
|
var node_fs_1 = __require("fs");
|
|
12044
|
-
var
|
|
12046
|
+
var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
12045
12047
|
var PS_TIMEOUT_MS = 2e3;
|
|
12046
12048
|
var PS_BUFFER = 1024 * 1024;
|
|
12047
12049
|
async function gracefulCloseWithTreeKill(options) {
|
|
@@ -12095,7 +12097,7 @@ var require_kill_tree = __commonJS({
|
|
|
12095
12097
|
timer.unref?.();
|
|
12096
12098
|
let stdout;
|
|
12097
12099
|
try {
|
|
12098
|
-
const result = await
|
|
12100
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
|
|
12099
12101
|
maxBuffer: PS_BUFFER,
|
|
12100
12102
|
signal: controller.signal
|
|
12101
12103
|
});
|
|
@@ -12133,7 +12135,7 @@ var require_kill_tree = __commonJS({
|
|
|
12133
12135
|
timer.unref?.();
|
|
12134
12136
|
let stdout;
|
|
12135
12137
|
try {
|
|
12136
|
-
const result = await
|
|
12138
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
|
|
12137
12139
|
maxBuffer: PS_BUFFER,
|
|
12138
12140
|
signal: controller.signal
|
|
12139
12141
|
});
|
|
@@ -12210,7 +12212,7 @@ var require_kill_tree = __commonJS({
|
|
|
12210
12212
|
timer.unref?.();
|
|
12211
12213
|
let stdout;
|
|
12212
12214
|
try {
|
|
12213
|
-
const result = await
|
|
12215
|
+
const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid="], {
|
|
12214
12216
|
maxBuffer: PS_BUFFER,
|
|
12215
12217
|
signal: controller.signal
|
|
12216
12218
|
});
|
|
@@ -321098,7 +321100,7 @@ var require_update = __commonJS({
|
|
|
321098
321100
|
var require_opts_arg = __commonJS({
|
|
321099
321101
|
"../../node_modules/tar/node_modules/mkdirp/lib/opts-arg.js"(exports, module) {
|
|
321100
321102
|
"use strict";
|
|
321101
|
-
var { promisify:
|
|
321103
|
+
var { promisify: promisify3 } = __require("util");
|
|
321102
321104
|
var fs = __require("fs");
|
|
321103
321105
|
var optsArg = (opts) => {
|
|
321104
321106
|
if (!opts)
|
|
@@ -321112,9 +321114,9 @@ var require_opts_arg = __commonJS({
|
|
|
321112
321114
|
else
|
|
321113
321115
|
throw new TypeError("invalid options argument");
|
|
321114
321116
|
opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir;
|
|
321115
|
-
opts.mkdirAsync =
|
|
321117
|
+
opts.mkdirAsync = promisify3(opts.mkdir);
|
|
321116
321118
|
opts.stat = opts.stat || opts.fs.stat || fs.stat;
|
|
321117
|
-
opts.statAsync =
|
|
321119
|
+
opts.statAsync = promisify3(opts.stat);
|
|
321118
321120
|
opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync;
|
|
321119
321121
|
opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync;
|
|
321120
321122
|
return opts;
|
|
@@ -325207,7 +325209,7 @@ var require_process_inventory = __commonJS({
|
|
|
325207
325209
|
var node_child_process_1 = __require("child_process");
|
|
325208
325210
|
var node_os_1 = __importDefault(__require("os"));
|
|
325209
325211
|
var node_util_1 = __require("util");
|
|
325210
|
-
var
|
|
325212
|
+
var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
325211
325213
|
var DEFAULT_LIMIT = 60;
|
|
325212
325214
|
var DEFAULT_TIMEOUT_MS = 800;
|
|
325213
325215
|
var MAX_COMMAND_LENGTH = 320;
|
|
@@ -325269,7 +325271,7 @@ var require_process_inventory = __commonJS({
|
|
|
325269
325271
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
325270
325272
|
timer.unref?.();
|
|
325271
325273
|
try {
|
|
325272
|
-
const { stdout } = await
|
|
325274
|
+
const { stdout } = await execFileAsync3(command, args, {
|
|
325273
325275
|
maxBuffer: 1024 * 1024,
|
|
325274
325276
|
signal: controller.signal
|
|
325275
325277
|
});
|
|
@@ -326291,7 +326293,7 @@ var require_package4 = __commonJS({
|
|
|
326291
326293
|
"package.json"(exports, module) {
|
|
326292
326294
|
module.exports = {
|
|
326293
326295
|
name: "@validate.qa/runner",
|
|
326294
|
-
version: "1.0.
|
|
326296
|
+
version: "1.0.9",
|
|
326295
326297
|
description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
|
|
326296
326298
|
bin: {
|
|
326297
326299
|
"validate-runner": "dist/cli.js"
|
|
@@ -326425,6 +326427,8 @@ function shouldUseAuthState(tags, playwrightCode) {
|
|
|
326425
326427
|
// src/services/appium-executor.ts
|
|
326426
326428
|
init_dist();
|
|
326427
326429
|
var import_runner_core8 = __toESM(require_dist2());
|
|
326430
|
+
import { execFile as execFile2 } from "child_process";
|
|
326431
|
+
import { promisify as promisify2 } from "util";
|
|
326428
326432
|
|
|
326429
326433
|
// src/services/appium-lifecycle.ts
|
|
326430
326434
|
import { spawn, execFileSync } from "child_process";
|
|
@@ -327607,6 +327611,7 @@ async function startMobileNetworkCapture(options) {
|
|
|
327607
327611
|
|
|
327608
327612
|
// src/services/appium-executor.ts
|
|
327609
327613
|
var DEFAULT_STEP_TIMEOUT_MS = 1e4;
|
|
327614
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
327610
327615
|
var WebDriverTransportError = class extends Error {
|
|
327611
327616
|
isTransport = true;
|
|
327612
327617
|
constructor(message) {
|
|
@@ -327928,6 +327933,30 @@ async function sendKeysToFocusedInput(sessionId, value) {
|
|
|
327928
327933
|
body: JSON.stringify({ text: value, value: Array.from(value) })
|
|
327929
327934
|
});
|
|
327930
327935
|
}
|
|
327936
|
+
function encodeAndroidInputText(value) {
|
|
327937
|
+
return value.replace(/%/g, "%25").replace(/\s/g, "%s");
|
|
327938
|
+
}
|
|
327939
|
+
async function sendAndroidTextToFocusedInput(deviceId, value) {
|
|
327940
|
+
if (!deviceId) {
|
|
327941
|
+
throw new Error("Android keyboard fallback requires a device id");
|
|
327942
|
+
}
|
|
327943
|
+
await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
|
|
327944
|
+
timeout: 1e4,
|
|
327945
|
+
windowsHide: true
|
|
327946
|
+
});
|
|
327947
|
+
}
|
|
327948
|
+
async function trySendElementValue(sessionId, elementId, value) {
|
|
327949
|
+
try {
|
|
327950
|
+
await wdRequest(`/session/${sessionId}/element/${elementId}/value`, {
|
|
327951
|
+
method: "POST",
|
|
327952
|
+
body: JSON.stringify({ text: value })
|
|
327953
|
+
});
|
|
327954
|
+
return null;
|
|
327955
|
+
} catch (error2) {
|
|
327956
|
+
if (isTransportError(error2)) throw error2;
|
|
327957
|
+
return error2;
|
|
327958
|
+
}
|
|
327959
|
+
}
|
|
327931
327960
|
function shouldAbortAfterStepFailure(action) {
|
|
327932
327961
|
switch (action) {
|
|
327933
327962
|
case "launchApp":
|
|
@@ -327964,17 +327993,33 @@ async function executeTap(sessionId, step) {
|
|
|
327964
327993
|
}
|
|
327965
327994
|
await tapCoordinates(sessionId, bounds);
|
|
327966
327995
|
}
|
|
327967
|
-
async function executeType(sessionId, step) {
|
|
327996
|
+
async function executeType(sessionId, step, context) {
|
|
327968
327997
|
if (step.value === void 0 || step.value === null || step.value === "") return;
|
|
327969
327998
|
const value = String(step.value);
|
|
327970
327999
|
const bounds = boundsFromStep(step);
|
|
327971
328000
|
let elementId = null;
|
|
328001
|
+
let lastInputError = null;
|
|
327972
328002
|
let tappedBounds = false;
|
|
328003
|
+
if (bounds) {
|
|
328004
|
+
await tapCoordinates(sessionId, bounds);
|
|
328005
|
+
tappedBounds = true;
|
|
328006
|
+
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
328007
|
+
elementId = await getActiveElementId(sessionId);
|
|
328008
|
+
if (elementId) {
|
|
328009
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328010
|
+
if (!lastInputError) return;
|
|
328011
|
+
elementId = null;
|
|
328012
|
+
}
|
|
328013
|
+
}
|
|
327973
328014
|
if (step.target) {
|
|
327974
328015
|
try {
|
|
327975
328016
|
elementId = await findElement(sessionId, step.target, readStepTimeout(step));
|
|
328017
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328018
|
+
if (!lastInputError) return;
|
|
328019
|
+
elementId = null;
|
|
327976
328020
|
} catch (error2) {
|
|
327977
328021
|
if (!bounds) throw error2;
|
|
328022
|
+
lastInputError = error2;
|
|
327978
328023
|
}
|
|
327979
328024
|
}
|
|
327980
328025
|
if (!elementId && bounds) {
|
|
@@ -327987,25 +328032,39 @@ async function executeType(sessionId, step) {
|
|
|
327987
328032
|
elementId = await getActiveElementId(sessionId);
|
|
327988
328033
|
}
|
|
327989
328034
|
if (elementId) {
|
|
327990
|
-
|
|
327991
|
-
|
|
327992
|
-
|
|
327993
|
-
|
|
327994
|
-
|
|
327995
|
-
return;
|
|
327996
|
-
} catch (error2) {
|
|
327997
|
-
if (!bounds) throw error2;
|
|
328035
|
+
lastInputError = await trySendElementValue(sessionId, elementId, value);
|
|
328036
|
+
if (!lastInputError) return;
|
|
328037
|
+
elementId = null;
|
|
328038
|
+
if (!bounds) {
|
|
328039
|
+
throw lastInputError instanceof Error ? lastInputError : new Error(String(lastInputError));
|
|
327998
328040
|
}
|
|
327999
328041
|
}
|
|
328000
328042
|
if (bounds) {
|
|
328001
328043
|
if (!tappedBounds) {
|
|
328002
328044
|
await tapCoordinates(sessionId, bounds);
|
|
328045
|
+
tappedBounds = true;
|
|
328003
328046
|
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
328004
328047
|
}
|
|
328005
|
-
|
|
328006
|
-
|
|
328048
|
+
try {
|
|
328049
|
+
await sendKeysToFocusedInput(sessionId, value);
|
|
328050
|
+
return;
|
|
328051
|
+
} catch (error2) {
|
|
328052
|
+
if (isTransportError(error2)) throw error2;
|
|
328053
|
+
lastInputError = error2;
|
|
328054
|
+
}
|
|
328007
328055
|
}
|
|
328008
|
-
|
|
328056
|
+
if (context.platform === "ANDROID" && tappedBounds) {
|
|
328057
|
+
try {
|
|
328058
|
+
context.log?.(" Appium active-element send failed; using Android focused-keyboard input fallback");
|
|
328059
|
+
await sendAndroidTextToFocusedInput(context.deviceId, value);
|
|
328060
|
+
await new Promise((resolve) => setTimeout(resolve, MOBILE_FOCUS_SETTLE_MS));
|
|
328061
|
+
return;
|
|
328062
|
+
} catch (error2) {
|
|
328063
|
+
if (isTransportError(error2)) throw error2;
|
|
328064
|
+
lastInputError = error2;
|
|
328065
|
+
}
|
|
328066
|
+
}
|
|
328067
|
+
throw new Error(`TYPE step could not send text to the focused target${lastInputError instanceof Error ? `: ${lastInputError.message}` : ""}`);
|
|
328009
328068
|
}
|
|
328010
328069
|
async function executeClear(sessionId, step) {
|
|
328011
328070
|
const bounds = boundsFromStep(step);
|
|
@@ -328226,7 +328285,11 @@ async function executeMobileTest(options) {
|
|
|
328226
328285
|
await executeTap(sessionId, step);
|
|
328227
328286
|
break;
|
|
328228
328287
|
case "type":
|
|
328229
|
-
await executeType(sessionId, step
|
|
328288
|
+
await executeType(sessionId, step, {
|
|
328289
|
+
platform: options.target.platform,
|
|
328290
|
+
deviceId: selectedDevice.id,
|
|
328291
|
+
log: log2
|
|
328292
|
+
});
|
|
328230
328293
|
break;
|
|
328231
328294
|
case "clear":
|
|
328232
328295
|
await executeClear(sessionId, step);
|
|
@@ -330665,16 +330728,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
330665
330728
|
}
|
|
330666
330729
|
return;
|
|
330667
330730
|
}
|
|
330668
|
-
if (runMode === "heal") {
|
|
330669
|
-
if (run2.framework === "APPIUM") {
|
|
330670
|
-
await postResult(opts.serverUrl, headers, run2.runId, {
|
|
330671
|
-
status: "ERROR",
|
|
330672
|
-
error: "CONFIG_ISSUE: Appium (mobile) tests are not auto-healable; skipped heal.",
|
|
330673
|
-
duration: Date.now() - startTime
|
|
330674
|
-
});
|
|
330675
|
-
log.warn("Skipped heal for APPIUM test (mobile tests are not healable)");
|
|
330676
|
-
return;
|
|
330677
|
-
}
|
|
330731
|
+
if (runMode === "heal" && run2.framework !== "APPIUM") {
|
|
330678
330732
|
const isApiTest2 = Array.isArray(run2.tags) && run2.tags.includes("@api");
|
|
330679
330733
|
if (isApiTest2) {
|
|
330680
330734
|
const testCode = run2.playwrightCode ?? "";
|
package/package.json
CHANGED