doc-detective 4.25.0 → 4.26.0
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/common/src/schemas/schemas.json +15966 -4090
- package/dist/common/src/types/generated/closeSurface_v3.d.ts +2 -2
- package/dist/common/src/types/generated/dragAndDrop_v3.d.ts +1 -1
- package/dist/common/src/types/generated/goTo_v3.d.ts +1 -1
- package/dist/common/src/types/generated/record_v3.d.ts +1 -1
- package/dist/common/src/types/generated/runBrowserScript_v3.d.ts +1 -1
- package/dist/common/src/types/generated/screenshot_v3.d.ts +1 -1
- package/dist/common/src/types/generated/startSurface_v3.d.ts +317 -3
- package/dist/common/src/types/generated/startSurface_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/step_v3.d.ts +329 -15
- package/dist/common/src/types/generated/step_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/surface_v3.d.ts +1 -1
- package/dist/common/src/types/generated/swipe_v3.d.ts +2 -2
- package/dist/common/src/types/generated/test_v3.d.ts +669 -41
- package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/type_v3.d.ts +1 -1
- package/dist/core/resolveTests.d.ts.map +1 -1
- package/dist/core/resolveTests.js +8 -1
- package/dist/core/resolveTests.js.map +1 -1
- package/dist/core/tests/appGestures.d.ts +7 -1
- package/dist/core/tests/appGestures.d.ts.map +1 -1
- package/dist/core/tests/appGestures.js +24 -4
- package/dist/core/tests/appGestures.js.map +1 -1
- package/dist/core/tests/appSurface.d.ts +13 -1
- package/dist/core/tests/appSurface.d.ts.map +1 -1
- package/dist/core/tests/appSurface.js +42 -10
- package/dist/core/tests/appSurface.js.map +1 -1
- package/dist/core/tests/appWindows.d.ts +45 -0
- package/dist/core/tests/appWindows.d.ts.map +1 -0
- package/dist/core/tests/appWindows.js +638 -0
- package/dist/core/tests/appWindows.js.map +1 -0
- package/dist/core/tests/browserSessions.d.ts +25 -3
- package/dist/core/tests/browserSessions.d.ts.map +1 -1
- package/dist/core/tests/browserSessions.js +63 -27
- package/dist/core/tests/browserSessions.js.map +1 -1
- package/dist/core/tests/browserSurface.d.ts.map +1 -1
- package/dist/core/tests/browserSurface.js +5 -2
- package/dist/core/tests/browserSurface.js.map +1 -1
- package/dist/core/tests/closeSurface.d.ts.map +1 -1
- package/dist/core/tests/closeSurface.js +25 -3
- package/dist/core/tests/closeSurface.js.map +1 -1
- package/dist/core/tests/ffmpegRecorder.d.ts +1 -7
- package/dist/core/tests/ffmpegRecorder.d.ts.map +1 -1
- package/dist/core/tests/ffmpegRecorder.js +5 -22
- package/dist/core/tests/ffmpegRecorder.js.map +1 -1
- package/dist/core/tests/findElement.d.ts.map +1 -1
- package/dist/core/tests/findElement.js +27 -5
- package/dist/core/tests/findElement.js.map +1 -1
- package/dist/core/tests/processSurface.d.ts +22 -0
- package/dist/core/tests/processSurface.d.ts.map +1 -0
- package/dist/core/tests/processSurface.js +105 -0
- package/dist/core/tests/processSurface.js.map +1 -0
- package/dist/core/tests/runShell.d.ts.map +1 -1
- package/dist/core/tests/runShell.js +23 -94
- package/dist/core/tests/runShell.js.map +1 -1
- package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
- package/dist/core/tests/saveScreenshot.js +27 -6
- package/dist/core/tests/saveScreenshot.js.map +1 -1
- package/dist/core/tests/startRecording.d.ts.map +1 -1
- package/dist/core/tests/startRecording.js +29 -14
- package/dist/core/tests/startRecording.js.map +1 -1
- package/dist/core/tests/startSurface.d.ts +17 -0
- package/dist/core/tests/startSurface.d.ts.map +1 -0
- package/dist/core/tests/startSurface.js +275 -0
- package/dist/core/tests/startSurface.js.map +1 -0
- package/dist/core/tests/swipe.d.ts.map +1 -1
- package/dist/core/tests/swipe.js +30 -5
- package/dist/core/tests/swipe.js.map +1 -1
- package/dist/core/tests/typeKeys.d.ts.map +1 -1
- package/dist/core/tests/typeKeys.js +22 -0
- package/dist/core/tests/typeKeys.js.map +1 -1
- package/dist/core/tests.d.ts +3 -1
- package/dist/core/tests.d.ts.map +1 -1
- package/dist/core/tests.js +174 -42
- package/dist/core/tests.js.map +1 -1
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +10 -5
- package/dist/core/utils.js.map +1 -1
- package/dist/index.cjs +17519 -4763
- package/dist/runtime/browserStepKeys.d.ts +22 -0
- package/dist/runtime/browserStepKeys.d.ts.map +1 -1
- package/dist/runtime/browserStepKeys.js +31 -0
- package/dist/runtime/browserStepKeys.js.map +1 -1
- package/dist/runtime/inferRuntimeNeeds.d.ts.map +1 -1
- package/dist/runtime/inferRuntimeNeeds.js +23 -3
- package/dist/runtime/inferRuntimeNeeds.js.map +1 -1
- package/package.json +1 -1
package/dist/core/tests.js
CHANGED
|
@@ -11,7 +11,7 @@ import fs from "node:fs";
|
|
|
11
11
|
import { loadHeavyDep, resolveHeavyDepPath } from "../runtime/loader.js";
|
|
12
12
|
import { requiredBrowserAssets, ensureBrowserInstalled, } from "../runtime/browsers.js";
|
|
13
13
|
// Single source of truth for browser/driver-requiring step keys.
|
|
14
|
-
import { BROWSER_STEP_KEYS as driverActions } from "../runtime/browserStepKeys.js";
|
|
14
|
+
import { BROWSER_STEP_KEYS as driverActions, startSurfaceDescriptors, stepOpensBrowserSurface, } from "../runtime/browserStepKeys.js";
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
import { log, replaceEnvs, selectSpecsForRun, findFreePort, runConcurrent, runResourceAware, createResourceRegistry, rollUpResults, createAppiumPool, getRunOutputDir, runArchivesArtifacts, sanitizeFilesystemName, evaluateContextRequirements, isRetryableSessionError, } from "./utils.js";
|
|
17
17
|
import axios from "axios";
|
|
@@ -34,7 +34,8 @@ import { httpRequest } from "./tests/httpRequest.js";
|
|
|
34
34
|
import { clickElement } from "./tests/click.js";
|
|
35
35
|
import { runCode } from "./tests/runCode.js";
|
|
36
36
|
import { closeSurface } from "./tests/closeSurface.js";
|
|
37
|
-
import { createAppSessionState, appSurfacePreflight, isAppDriverRequired, stepTargetsAppSurface,
|
|
37
|
+
import { createAppSessionState, appSurfacePreflight, isAppDriverRequired, stepTargetsAppSurface, teardownAppSession, } from "./tests/appSurface.js";
|
|
38
|
+
import { startSurfaceStep } from "./tests/startSurface.js";
|
|
38
39
|
import { isMobileTargetPlatform } from "./tests/mobilePlatform.js";
|
|
39
40
|
import { mobileBrowserGate, buildMobileBrowserCapabilities, } from "./tests/mobileBrowser.js";
|
|
40
41
|
import { getCacheDir } from "../runtime/cacheDir.js";
|
|
@@ -58,7 +59,7 @@ import http from "node:http";
|
|
|
58
59
|
import https from "node:https";
|
|
59
60
|
import { fileURLToPath } from "node:url";
|
|
60
61
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
61
|
-
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, getDefaultBrowser, buildFallbackCandidates, driverSkipDiagnostic, resolveBrowserFallbackPolicy, shouldRepairBeforeFallback, isSupportedContext, contextRequirementsSkipMessage, resolveAutoScreenshot, resolveAutoRecord, buildAutoRecordStep, specIsRouted, killTree, jobDisplayResources, };
|
|
62
|
+
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, withChromedriverPort, getDefaultBrowser, buildFallbackCandidates, driverSkipDiagnostic, resolveBrowserFallbackPolicy, shouldRepairBeforeFallback, isSupportedContext, contextRequirementsSkipMessage, resolveAutoScreenshot, resolveAutoRecord, buildAutoRecordStep, specIsRouted, killTree, jobDisplayResources, };
|
|
62
63
|
// exports.appiumStart = appiumStart;
|
|
63
64
|
// exports.appiumIsReady = appiumIsReady;
|
|
64
65
|
// exports.driverStart = driverStart;
|
|
@@ -270,6 +271,26 @@ function getDriverCapabilities({ runnerDetails, name, options }) {
|
|
|
270
271
|
}
|
|
271
272
|
return capabilities;
|
|
272
273
|
}
|
|
274
|
+
// Bind a Chromium (chromedriver-backed) session to a specific chromedriver
|
|
275
|
+
// port. appium-chromium-driver hands `chromedriverPort` straight to
|
|
276
|
+
// appium-chromedriver; when it is undefined, appium-chromedriver falls back to
|
|
277
|
+
// its fixed DEFAULT_PORT (9515). Two concurrent browser contexts (own Appium
|
|
278
|
+
// server each, from the pool) then both spawn chromedriver on 9515 — one binds,
|
|
279
|
+
// the other's connection is REFUSED, surfacing later as a mid-session
|
|
280
|
+
// `ECONNREFUSED 127.0.0.1:9515` when a command proxies to the wrong/dead
|
|
281
|
+
// chromedriver (ADR 01039). Assigning a unique free port per session removes the
|
|
282
|
+
// collision. Gecko/Safari are unaffected — geckodriver auto-selects a free
|
|
283
|
+
// `systemPort` from a range, and Safari has no such port — so this only touches
|
|
284
|
+
// Chromium caps, leaving every other engine's caps byte-identical. An explicit
|
|
285
|
+
// `appium:chromedriverPort` (a caller opting into a fixed port) is preserved.
|
|
286
|
+
function withChromedriverPort(capabilities, port) {
|
|
287
|
+
if (!capabilities ||
|
|
288
|
+
capabilities["appium:automationName"] !== "Chromium" ||
|
|
289
|
+
capabilities["appium:chromedriverPort"] !== undefined) {
|
|
290
|
+
return capabilities;
|
|
291
|
+
}
|
|
292
|
+
return { ...capabilities, "appium:chromedriverPort": port };
|
|
293
|
+
}
|
|
273
294
|
function isDriverRequired({ test }) {
|
|
274
295
|
let driverRequired = false;
|
|
275
296
|
// The resolved shape doesn't guarantee `steps` — treat a stepless test or
|
|
@@ -280,9 +301,21 @@ function isDriverRequired({ test }) {
|
|
|
280
301
|
if (typeof step[action] !== "undefined")
|
|
281
302
|
driverRequired = true;
|
|
282
303
|
});
|
|
304
|
+
// A startSurface browser descriptor opens a WebDriver session (Phase 6);
|
|
305
|
+
// app/process descriptors provision their own runtimes and don't count.
|
|
306
|
+
if (stepOpensBrowserSurface(step))
|
|
307
|
+
driverRequired = true;
|
|
283
308
|
});
|
|
284
309
|
return driverRequired;
|
|
285
310
|
}
|
|
311
|
+
// Non-android platforms whose native app surface is driven by a shared,
|
|
312
|
+
// per-host Appium driver server (macOS Mac2, Windows NovaWindows, iOS/xcuitest
|
|
313
|
+
// simulator). Two concurrent sessions against one of these clobber the shared
|
|
314
|
+
// server, so app-driver contexts on these platforms serialize on the
|
|
315
|
+
// "native-app-driver" resource. Mirrors the non-android keys of
|
|
316
|
+
// APP_DRIVER_PLATFORMS in tests/appSurface.ts; android is excluded because it
|
|
317
|
+
// already has its own "android-emulator" bound.
|
|
318
|
+
const NATIVE_APP_DRIVER_PLATFORMS = ["mac", "windows", "ios"];
|
|
286
319
|
// The exclusive resources a context job must hold to run safely under
|
|
287
320
|
// concurrency. A shared-display ffmpeg recording holds "display" exclusively
|
|
288
321
|
// (jobExclusiveResources). And once ANY such recording is in the run
|
|
@@ -310,14 +343,77 @@ function jobDisplayResources(job, ctx) {
|
|
|
310
343
|
hasBrowserStep: isBrowserRequired({ test: job.context }),
|
|
311
344
|
hasAppStep: isAppDriverRequired({ test: job.context }),
|
|
312
345
|
}).action === "proceed";
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
346
|
+
// Non-android native app-driver contexts share a per-host driver stack that
|
|
347
|
+
// two concurrent sessions clobber (a proxied step fails because the driver
|
|
348
|
+
// "process is not running (probably crashed)" / "Session does not exist" /
|
|
349
|
+
// ECONNREFUSED to WebDriverAgent on :8100). Serialize them against each other
|
|
350
|
+
// on one exclusive resource — the non-android sibling of the android-emulator
|
|
351
|
+
// bound (extending ADR 01001 the way ADR 01025 did for emulators). Android
|
|
352
|
+
// already has its own bound above, so it's excluded here (never double-tagged).
|
|
353
|
+
//
|
|
354
|
+
// Which contexts contend depends on the platform:
|
|
355
|
+
// - macOS Mac2 / Windows: only contexts that drive a NATIVE APP contend —
|
|
356
|
+
// the driver launches the app under test, but a plain desktop browser
|
|
357
|
+
// (firefox/chrome) uses a separate browser session and must STILL
|
|
358
|
+
// parallelize. So these require isAppDriverRequired.
|
|
359
|
+
// - iOS: the single per-host iOS simulator + WebDriverAgent is shared by
|
|
360
|
+
// BOTH native-app (xcuitest) AND mobile-web (Safari-on-sim) contexts, so
|
|
361
|
+
// two of EITHER kind clobber each other. So an ios context contends
|
|
362
|
+
// whether or not it has an app step (mobile-web-ios failed the same way
|
|
363
|
+
// apps-ios did under concurrency).
|
|
364
|
+
// As with attemptsEmulator, the context must also clear the mobile browser
|
|
365
|
+
// gate, so a context that deterministically SKIPs/FAILs (mixed app+web on
|
|
366
|
+
// mobile, unsupported browser, device-fixed config) boots nothing and never
|
|
367
|
+
// needlessly serializes other jobs.
|
|
368
|
+
const platform = job.context?.platform;
|
|
369
|
+
const contendsForNativeDriver = platform === "ios"
|
|
370
|
+
? true // any ios context boots the shared simulator (app OR web)
|
|
371
|
+
: isAppDriverRequired({ test: job.context }); // mac/windows: app only
|
|
372
|
+
// The mobileBrowserGate check only makes sense on MOBILE targets (ios/android):
|
|
373
|
+
// it SKIPs a context that deterministically won't boot the shared driver
|
|
374
|
+
// (mixed native-app + device-browser, unsupported/misconfigured mobile
|
|
375
|
+
// browser). On DESKTOP (mac/windows) there is no device browser to mix with, so
|
|
376
|
+
// the gate must NOT run: a desktop app context that also RECORDS carries
|
|
377
|
+
// `record`/`stopRecord` steps (BROWSER_STEP_KEYS) whose non-object payloads
|
|
378
|
+
// aren't app-targeting, so isBrowserRequired reports a "browser step" and the
|
|
379
|
+
// gate would wrongly SKIP — dropping the native-app-driver bound and letting a
|
|
380
|
+
// recording app context run concurrently with another native app context,
|
|
381
|
+
// clobbering the single per-host Mac2 / NovaWindows driver. Desktop app
|
|
382
|
+
// contexts always boot that driver, so they always contend. (ADR 01040.)
|
|
383
|
+
const isMobileTarget = platform === "ios" || platform === "android";
|
|
384
|
+
const gateProceeds = !isMobileTarget ||
|
|
385
|
+
mobileBrowserGate({
|
|
386
|
+
platform,
|
|
387
|
+
browser: job.context?.browser,
|
|
388
|
+
hasBrowserStep: isBrowserRequired({ test: job.context }),
|
|
389
|
+
hasAppStep: isAppDriverRequired({ test: job.context }),
|
|
390
|
+
}).action === "proceed";
|
|
391
|
+
const attemptsNativeAppDriver = platform !== "android" &&
|
|
392
|
+
NATIVE_APP_DRIVER_PLATFORMS.includes(platform) &&
|
|
393
|
+
contendsForNativeDriver &&
|
|
394
|
+
gateProceeds;
|
|
395
|
+
const extra = [
|
|
396
|
+
...(attemptsEmulator ? ["android-emulator"] : []),
|
|
397
|
+
...(attemptsNativeAppDriver ? ["native-app-driver"] : []),
|
|
398
|
+
];
|
|
399
|
+
// A run-wide shared-display recording promotes every driver context onto the
|
|
400
|
+
// "display" resource so its ffmpeg capture doesn't include their windows.
|
|
401
|
+
// This composes with the native-app-driver bound above — a native app
|
|
402
|
+
// context in such a run holds BOTH (e.g. ["native-app-driver","display"]).
|
|
403
|
+
// Android emulator contexts are exempt: an emulator renders off the host
|
|
404
|
+
// display, so it neither pollutes a screen capture nor needs the display
|
|
405
|
+
// mutex (it stays on "android-emulator" only, as before).
|
|
406
|
+
const displayPromotion = !attemptsEmulator &&
|
|
407
|
+
ctx.runHasDisplayRecording &&
|
|
408
|
+
isDriverRequired({ test: { steps: job.context?.steps } })
|
|
409
|
+
? ["display"]
|
|
410
|
+
: [];
|
|
411
|
+
// Order the union driver-bound first (native-app-driver / android-emulator),
|
|
412
|
+
// then the display resource — whether "display" arrived via `base` (this job's
|
|
413
|
+
// own ffmpeg recording) or `displayPromotion` (a recording elsewhere in the
|
|
414
|
+
// run). This keeps the canonical `["native-app-driver", "display"]` shape
|
|
415
|
+
// stable regardless of which path added the display bound.
|
|
416
|
+
return [...new Set([...extra, ...base, ...displayPromotion])];
|
|
321
417
|
}
|
|
322
418
|
// Check if context is supported by current platform and available apps
|
|
323
419
|
function isSupportedContext({ context, apps, platform }) {
|
|
@@ -355,8 +451,11 @@ function isBrowserRequired({ test }) {
|
|
|
355
451
|
if (!Array.isArray(test?.steps))
|
|
356
452
|
return false;
|
|
357
453
|
return test.steps.some((step) => !step?.__autoRecord &&
|
|
358
|
-
driverActions.some((action) => typeof step[action] !== "undefined") &&
|
|
359
|
-
|
|
454
|
+
((driverActions.some((action) => typeof step[action] !== "undefined") &&
|
|
455
|
+
!stepTargetsAppSurface(step)) ||
|
|
456
|
+
// Phase 6: `startSurface: { browser: … }` opens a browser session
|
|
457
|
+
// (the goTo-opener sibling); app/process descriptors don't.
|
|
458
|
+
stepOpensBrowserSurface(step)));
|
|
360
459
|
}
|
|
361
460
|
// Size the browser Appium server pool: the number of concurrent runner jobs
|
|
362
461
|
// that will actually create a BROWSER session. App-only jobs are excluded —
|
|
@@ -387,14 +486,19 @@ function contextRequirementsSkipMessage({ context, deps, }) {
|
|
|
387
486
|
return null;
|
|
388
487
|
return `Skipping context on '${context.platform}': unmet requirements — ${missing.join(", ")}.`;
|
|
389
488
|
}
|
|
390
|
-
// The device descriptors a test needs: each startSurface's
|
|
391
|
-
// when it omits one — the context default / auto device)
|
|
392
|
-
//
|
|
393
|
-
|
|
489
|
+
// The device descriptors a test needs: each APP startSurface descriptor's
|
|
490
|
+
// `device` (undefined when it omits one — the context default / auto device),
|
|
491
|
+
// across both the object form and the Phase 6 parallel array form. Browser /
|
|
492
|
+
// process descriptors boot no device and contribute nothing. At least one
|
|
493
|
+
// entry so a test with no explicit device still validates the default device.
|
|
494
|
+
// Exported for a focused unit test.
|
|
495
|
+
export function collectDeviceDescriptors(context) {
|
|
394
496
|
const out = [];
|
|
395
497
|
for (const step of context?.steps ?? []) {
|
|
396
|
-
|
|
397
|
-
|
|
498
|
+
for (const d of startSurfaceDescriptors(step)) {
|
|
499
|
+
if (d && typeof d === "object" && typeof d.app === "string")
|
|
500
|
+
out.push(d.device);
|
|
501
|
+
}
|
|
398
502
|
}
|
|
399
503
|
return out.length ? out : [undefined];
|
|
400
504
|
}
|
|
@@ -2865,18 +2969,34 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
2865
2969
|
downloadDir: browserDownloadDir(context.contextId),
|
|
2866
2970
|
};
|
|
2867
2971
|
// Start a session for one engine, headed first then (on failure) headless.
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2972
|
+
// `overrides` carries a startSurface browser descriptor's launch knobs
|
|
2973
|
+
// (Phase 6): explicit headless wins over the context setting, `size`
|
|
2974
|
+
// wins over the context window dimensions, and `driverOptions` merges
|
|
2975
|
+
// into the computed capabilities last (the app branch's escape-hatch
|
|
2976
|
+
// precedent).
|
|
2977
|
+
const startDriverForBrowser = async (browserName, overrides) => {
|
|
2978
|
+
const wantHeadless = overrides?.headless !== undefined
|
|
2979
|
+
? overrides.headless
|
|
2980
|
+
: context.browser?.headless !== false;
|
|
2981
|
+
const buildCaps = (headless) => {
|
|
2982
|
+
const caps = getDriverCapabilities({
|
|
2983
|
+
runnerDetails,
|
|
2984
|
+
name: browserName,
|
|
2985
|
+
options: {
|
|
2986
|
+
width: overrides?.size?.width ||
|
|
2987
|
+
context.browser?.window?.width ||
|
|
2988
|
+
1200,
|
|
2989
|
+
height: overrides?.size?.height ||
|
|
2990
|
+
context.browser?.window?.height ||
|
|
2991
|
+
800,
|
|
2992
|
+
headless,
|
|
2993
|
+
...recordOptions,
|
|
2994
|
+
},
|
|
2995
|
+
});
|
|
2996
|
+
if (overrides?.driverOptions)
|
|
2997
|
+
Object.assign(caps, overrides.driverOptions);
|
|
2998
|
+
return caps;
|
|
2999
|
+
};
|
|
2880
3000
|
const startFailure = () => {
|
|
2881
3001
|
let error = `Failed to start context '${browserName}' on '${platform}'.`;
|
|
2882
3002
|
if (browserName === "safari" || browserName === "webkit") {
|
|
@@ -3011,8 +3131,8 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
3011
3131
|
// stamps driver.state.engine and back-links driver.state.sessionRegistry.
|
|
3012
3132
|
if (driver) {
|
|
3013
3133
|
browserSessions = createSessionRegistry({
|
|
3014
|
-
open: async (engine) => {
|
|
3015
|
-
const res = await startDriverForBrowser(engine);
|
|
3134
|
+
open: async (engine, overrides) => {
|
|
3135
|
+
const res = await startDriverForBrowser(engine, overrides);
|
|
3016
3136
|
if (!res.ok)
|
|
3017
3137
|
throw new Error(res.error);
|
|
3018
3138
|
return res.driver;
|
|
@@ -3690,17 +3810,17 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
|
|
|
3690
3810
|
actionResult = await closeSurface({ config: config, step: step, driver, processRegistry, appSession });
|
|
3691
3811
|
}
|
|
3692
3812
|
else if (typeof step.startSurface !== "undefined") {
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
else {
|
|
3700
|
-
actionResult = await startAppSurface({
|
|
3813
|
+
{
|
|
3814
|
+
// Multi-surface Phase 6: startSurfaceStep dispatches app / browser /
|
|
3815
|
+
// process descriptors (and the parallel array form). The app lane
|
|
3816
|
+
// FAILs per descriptor when no app session was preflighted; browser
|
|
3817
|
+
// descriptors ride the context's session registry via `driver`.
|
|
3818
|
+
actionResult = await startSurfaceStep({
|
|
3701
3819
|
config: config,
|
|
3702
3820
|
step: step,
|
|
3703
3821
|
appSession,
|
|
3822
|
+
driver,
|
|
3823
|
+
processRegistry,
|
|
3704
3824
|
platform: context?.platform ?? "",
|
|
3705
3825
|
serverDeps: {
|
|
3706
3826
|
startServer: async (appiumEntry, appiumHome) => {
|
|
@@ -3930,13 +4050,25 @@ async function driverStart(capabilities, port, maxAttempts = 4, ctx = {}) {
|
|
|
3930
4050
|
let lastError;
|
|
3931
4051
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3932
4052
|
try {
|
|
4053
|
+
// Chromium sessions get a unique free chromedriver port so concurrent
|
|
4054
|
+
// browser contexts never collide on chromedriver's fixed default (9515);
|
|
4055
|
+
// see withChromedriverPort (ADR 01039). A FRESH port is allocated per
|
|
4056
|
+
// attempt so a retryable ECONNREFUSED — the very rebind-race the Appium
|
|
4057
|
+
// path already retries — moves to a new free port instead of re-racing
|
|
4058
|
+
// the same one. Only Chromium caps need a port; every other engine skips
|
|
4059
|
+
// the allocation and keeps a byte-identical wdio.remote payload.
|
|
4060
|
+
const needsChromedriverPort = capabilities?.["appium:automationName"] === "Chromium" &&
|
|
4061
|
+
capabilities?.["appium:chromedriverPort"] === undefined;
|
|
4062
|
+
const attemptCaps = needsChromedriverPort
|
|
4063
|
+
? withChromedriverPort(capabilities, await findFreePort())
|
|
4064
|
+
: capabilities;
|
|
3933
4065
|
const driver = await wdio.remote({
|
|
3934
4066
|
protocol: "http",
|
|
3935
4067
|
hostname: "127.0.0.1",
|
|
3936
4068
|
port,
|
|
3937
4069
|
path: "/",
|
|
3938
4070
|
logLevel: "error",
|
|
3939
|
-
capabilities,
|
|
4071
|
+
capabilities: attemptCaps,
|
|
3940
4072
|
connectionRetryTimeout: startupCeiling,
|
|
3941
4073
|
waitforTimeout: 120000, // 2 minutes
|
|
3942
4074
|
});
|