@validate.qa/runner 1.0.21 → 1.0.23

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 CHANGED
@@ -328483,7 +328483,7 @@ var require_package4 = __commonJS({
328483
328483
  "package.json"(exports2, module2) {
328484
328484
  module2.exports = {
328485
328485
  name: "@validate.qa/runner",
328486
- version: "1.0.21",
328486
+ version: "1.0.23",
328487
328487
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
328488
328488
  bin: {
328489
328489
  "validate-runner": "dist/cli.js"
@@ -330252,6 +330252,11 @@ async function startMobileNetworkCapture(options) {
330252
330252
 
330253
330253
  // src/services/appium-executor.ts
330254
330254
  var DEFAULT_STEP_TIMEOUT_MS = 1e4;
330255
+ var MOBILE_NEW_COMMAND_TIMEOUT_SECONDS = (() => {
330256
+ const raw = Number(process.env.VALIDATEQA_MOBILE_NEW_COMMAND_TIMEOUT);
330257
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 600;
330258
+ })();
330259
+ var COLD_BOOT_ENTRY_TIMEOUT_MS = 3e4;
330255
330260
  var VERIFICATION_ACTIONS = /* @__PURE__ */ new Set(["assertVisible", "assertText", "waitForElement"]);
330256
330261
  var WD_SESSION_CREATE_TIMEOUT_MS = 12e4;
330257
330262
  var WD_ACTION_TIMEOUT_MS = 3e4;
@@ -330351,6 +330356,14 @@ async function createSession(target) {
330351
330356
  ...target.deviceId ? { "appium:udid": target.deviceId } : {},
330352
330357
  ...target.platformVersion ? { "appium:platformVersion": target.platformVersion } : {},
330353
330358
  "appium:noReset": true,
330359
+ // Appium's default newCommandTimeout is 60s — it kills the session if no
330360
+ // WebDriver command arrives in that window. A discovery session is driven
330361
+ // by an AI loop where a single model turn (or a pause on a permission
330362
+ // dialog) can exceed 60s, so the default silently terminates the session
330363
+ // mid-run and every later command 404s ("invalid session id"), failing the
330364
+ // whole survey. The runner owns session teardown explicitly, so a long
330365
+ // idle window is safe. Env-overridable.
330366
+ "appium:newCommandTimeout": MOBILE_NEW_COMMAND_TIMEOUT_SECONDS,
330354
330367
  ...realCaps
330355
330368
  } : {
330356
330369
  platformName: "Android",
@@ -330359,6 +330372,8 @@ async function createSession(target) {
330359
330372
  ...target.deviceId ? { "appium:udid": target.deviceId } : {},
330360
330373
  ...target.platformVersion ? { "appium:platformVersion": target.platformVersion } : {},
330361
330374
  "appium:noReset": true,
330375
+ // See the iOS branch: the 60s default kills AI-driven discovery sessions.
330376
+ "appium:newCommandTimeout": MOBILE_NEW_COMMAND_TIMEOUT_SECONDS,
330362
330377
  ...realCaps
330363
330378
  };
330364
330379
  const result = await wdRequest("/session", {
@@ -330476,18 +330491,24 @@ async function freshLaunchTargetApp(sessionId, platform3, appId, log2) {
330476
330491
  }
330477
330492
  async function waitForColdBootContent(sessionId, log2, timeoutMs = 2e4) {
330478
330493
  const deadline = Date.now() + timeoutMs;
330479
- const MIN_ELEMENTS = 3;
330494
+ const MIN_ELEMENTS = 12;
330480
330495
  let lastCount = 0;
330496
+ let stableFrames = 0;
330481
330497
  while (Date.now() < deadline) {
330482
330498
  try {
330483
330499
  const res = await wdRequest(`/session/${sessionId}/source`);
330484
330500
  const xml = typeof res?.value === "string" ? res.value : "";
330485
330501
  const count = (xml.match(/<(android\.|XCUIElementType)/g) ?? []).length;
330486
- lastCount = count;
330487
- if (count >= MIN_ELEMENTS) {
330488
- log2(` Cold-boot ready: UI tree has ${count} node(s).`);
330489
- return;
330502
+ if (count >= MIN_ELEMENTS && count === lastCount) {
330503
+ stableFrames++;
330504
+ if (stableFrames >= 2) {
330505
+ log2(` Cold-boot ready: UI tree settled at ${count} node(s).`);
330506
+ return;
330507
+ }
330508
+ } else {
330509
+ stableFrames = 0;
330490
330510
  }
330511
+ lastCount = count;
330491
330512
  } catch {
330492
330513
  }
330493
330514
  await new Promise((r) => setTimeout(r, 750));
@@ -331172,7 +331193,8 @@ async function executeMobileTest(options) {
331172
331193
  );
331173
331194
  if (entryStep) {
331174
331195
  try {
331175
- await findElement(sessionId, entryStep.target, DEFAULT_STEP_TIMEOUT_MS, { identityOnly: true, platform: runPlatform });
331196
+ const entryTimeout = recordedFromClearedState ? Math.max(DEFAULT_STEP_TIMEOUT_MS, COLD_BOOT_ENTRY_TIMEOUT_MS) : DEFAULT_STEP_TIMEOUT_MS;
331197
+ await findElement(sessionId, entryStep.target, entryTimeout, { identityOnly: true, platform: runPlatform });
331176
331198
  } catch (err) {
331177
331199
  if (isTransportError(err)) throw err;
331178
331200
  entryDrift = true;
package/dist/cli.mjs CHANGED
@@ -328488,7 +328488,7 @@ var require_package4 = __commonJS({
328488
328488
  "package.json"(exports, module) {
328489
328489
  module.exports = {
328490
328490
  name: "@validate.qa/runner",
328491
- version: "1.0.21",
328491
+ version: "1.0.23",
328492
328492
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
328493
328493
  bin: {
328494
328494
  "validate-runner": "dist/cli.js"
@@ -330260,6 +330260,11 @@ async function startMobileNetworkCapture(options) {
330260
330260
 
330261
330261
  // src/services/appium-executor.ts
330262
330262
  var DEFAULT_STEP_TIMEOUT_MS = 1e4;
330263
+ var MOBILE_NEW_COMMAND_TIMEOUT_SECONDS = (() => {
330264
+ const raw = Number(process.env.VALIDATEQA_MOBILE_NEW_COMMAND_TIMEOUT);
330265
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 600;
330266
+ })();
330267
+ var COLD_BOOT_ENTRY_TIMEOUT_MS = 3e4;
330263
330268
  var VERIFICATION_ACTIONS = /* @__PURE__ */ new Set(["assertVisible", "assertText", "waitForElement"]);
330264
330269
  var WD_SESSION_CREATE_TIMEOUT_MS = 12e4;
330265
330270
  var WD_ACTION_TIMEOUT_MS = 3e4;
@@ -330359,6 +330364,14 @@ async function createSession(target) {
330359
330364
  ...target.deviceId ? { "appium:udid": target.deviceId } : {},
330360
330365
  ...target.platformVersion ? { "appium:platformVersion": target.platformVersion } : {},
330361
330366
  "appium:noReset": true,
330367
+ // Appium's default newCommandTimeout is 60s — it kills the session if no
330368
+ // WebDriver command arrives in that window. A discovery session is driven
330369
+ // by an AI loop where a single model turn (or a pause on a permission
330370
+ // dialog) can exceed 60s, so the default silently terminates the session
330371
+ // mid-run and every later command 404s ("invalid session id"), failing the
330372
+ // whole survey. The runner owns session teardown explicitly, so a long
330373
+ // idle window is safe. Env-overridable.
330374
+ "appium:newCommandTimeout": MOBILE_NEW_COMMAND_TIMEOUT_SECONDS,
330362
330375
  ...realCaps
330363
330376
  } : {
330364
330377
  platformName: "Android",
@@ -330367,6 +330380,8 @@ async function createSession(target) {
330367
330380
  ...target.deviceId ? { "appium:udid": target.deviceId } : {},
330368
330381
  ...target.platformVersion ? { "appium:platformVersion": target.platformVersion } : {},
330369
330382
  "appium:noReset": true,
330383
+ // See the iOS branch: the 60s default kills AI-driven discovery sessions.
330384
+ "appium:newCommandTimeout": MOBILE_NEW_COMMAND_TIMEOUT_SECONDS,
330370
330385
  ...realCaps
330371
330386
  };
330372
330387
  const result = await wdRequest("/session", {
@@ -330484,18 +330499,24 @@ async function freshLaunchTargetApp(sessionId, platform3, appId, log2) {
330484
330499
  }
330485
330500
  async function waitForColdBootContent(sessionId, log2, timeoutMs = 2e4) {
330486
330501
  const deadline = Date.now() + timeoutMs;
330487
- const MIN_ELEMENTS = 3;
330502
+ const MIN_ELEMENTS = 12;
330488
330503
  let lastCount = 0;
330504
+ let stableFrames = 0;
330489
330505
  while (Date.now() < deadline) {
330490
330506
  try {
330491
330507
  const res = await wdRequest(`/session/${sessionId}/source`);
330492
330508
  const xml = typeof res?.value === "string" ? res.value : "";
330493
330509
  const count = (xml.match(/<(android\.|XCUIElementType)/g) ?? []).length;
330494
- lastCount = count;
330495
- if (count >= MIN_ELEMENTS) {
330496
- log2(` Cold-boot ready: UI tree has ${count} node(s).`);
330497
- return;
330510
+ if (count >= MIN_ELEMENTS && count === lastCount) {
330511
+ stableFrames++;
330512
+ if (stableFrames >= 2) {
330513
+ log2(` Cold-boot ready: UI tree settled at ${count} node(s).`);
330514
+ return;
330515
+ }
330516
+ } else {
330517
+ stableFrames = 0;
330498
330518
  }
330519
+ lastCount = count;
330499
330520
  } catch {
330500
330521
  }
330501
330522
  await new Promise((r) => setTimeout(r, 750));
@@ -331180,7 +331201,8 @@ async function executeMobileTest(options) {
331180
331201
  );
331181
331202
  if (entryStep) {
331182
331203
  try {
331183
- await findElement(sessionId, entryStep.target, DEFAULT_STEP_TIMEOUT_MS, { identityOnly: true, platform: runPlatform });
331204
+ const entryTimeout = recordedFromClearedState ? Math.max(DEFAULT_STEP_TIMEOUT_MS, COLD_BOOT_ENTRY_TIMEOUT_MS) : DEFAULT_STEP_TIMEOUT_MS;
331205
+ await findElement(sessionId, entryStep.target, entryTimeout, { identityOnly: true, platform: runPlatform });
331184
331206
  } catch (err) {
331185
331207
  if (isTransportError(err)) throw err;
331186
331208
  entryDrift = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
5
5
  "bin": {
6
6
  "validate-runner": "dist/cli.js"