doc-detective 4.23.0 → 4.25.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.
Files changed (92) hide show
  1. package/dist/common/src/schemas/schemas.json +39128 -4588
  2. package/dist/common/src/types/generated/record_v3.d.ts +38 -5
  3. package/dist/common/src/types/generated/record_v3.d.ts.map +1 -1
  4. package/dist/common/src/types/generated/step_v3.d.ts +529 -44
  5. package/dist/common/src/types/generated/step_v3.d.ts.map +1 -1
  6. package/dist/common/src/types/generated/swipe_v3.d.ts +288 -0
  7. package/dist/common/src/types/generated/swipe_v3.d.ts.map +1 -0
  8. package/dist/common/src/types/generated/swipe_v3.js +7 -0
  9. package/dist/common/src/types/generated/swipe_v3.js.map +1 -0
  10. package/dist/common/src/types/generated/test_v3.d.ts +2159 -1125
  11. package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
  12. package/dist/core/tests/appGestures.d.ts +27 -0
  13. package/dist/core/tests/appGestures.d.ts.map +1 -0
  14. package/dist/core/tests/appGestures.js +325 -0
  15. package/dist/core/tests/appGestures.js.map +1 -0
  16. package/dist/core/tests/appSurface.d.ts +2 -1
  17. package/dist/core/tests/appSurface.d.ts.map +1 -1
  18. package/dist/core/tests/appSurface.js +101 -29
  19. package/dist/core/tests/appSurface.js.map +1 -1
  20. package/dist/core/tests/click.d.ts.map +1 -1
  21. package/dist/core/tests/click.js +12 -8
  22. package/dist/core/tests/click.js.map +1 -1
  23. package/dist/core/tests/ffmpegRecorder.d.ts +29 -2
  24. package/dist/core/tests/ffmpegRecorder.d.ts.map +1 -1
  25. package/dist/core/tests/ffmpegRecorder.js +154 -8
  26. package/dist/core/tests/ffmpegRecorder.js.map +1 -1
  27. package/dist/core/tests/findElement.d.ts.map +1 -1
  28. package/dist/core/tests/findElement.js +82 -10
  29. package/dist/core/tests/findElement.js.map +1 -1
  30. package/dist/core/tests/mobileBrowser.d.ts.map +1 -1
  31. package/dist/core/tests/mobileBrowser.js +4 -3
  32. package/dist/core/tests/mobileBrowser.js.map +1 -1
  33. package/dist/core/tests/movement.d.ts +43 -0
  34. package/dist/core/tests/movement.d.ts.map +1 -0
  35. package/dist/core/tests/movement.js +92 -0
  36. package/dist/core/tests/movement.js.map +1 -0
  37. package/dist/core/tests/startRecording.d.ts +9 -1
  38. package/dist/core/tests/startRecording.d.ts.map +1 -1
  39. package/dist/core/tests/startRecording.js +187 -19
  40. package/dist/core/tests/startRecording.js.map +1 -1
  41. package/dist/core/tests/stopRecording.d.ts +8 -1
  42. package/dist/core/tests/stopRecording.d.ts.map +1 -1
  43. package/dist/core/tests/stopRecording.js +108 -3
  44. package/dist/core/tests/stopRecording.js.map +1 -1
  45. package/dist/core/tests/swipe.d.ts +8 -0
  46. package/dist/core/tests/swipe.d.ts.map +1 -0
  47. package/dist/core/tests/swipe.js +158 -0
  48. package/dist/core/tests/swipe.js.map +1 -0
  49. package/dist/core/tests/typeKeys.d.ts +8 -1
  50. package/dist/core/tests/typeKeys.d.ts.map +1 -1
  51. package/dist/core/tests/typeKeys.js +123 -23
  52. package/dist/core/tests/typeKeys.js.map +1 -1
  53. package/dist/core/tests.d.ts.map +1 -1
  54. package/dist/core/tests.js +55 -22
  55. package/dist/core/tests.js.map +1 -1
  56. package/dist/core/utils.d.ts +2 -1
  57. package/dist/core/utils.d.ts.map +1 -1
  58. package/dist/core/utils.js +36 -3
  59. package/dist/core/utils.js.map +1 -1
  60. package/dist/hints/context.d.ts.map +1 -1
  61. package/dist/hints/context.js +4 -0
  62. package/dist/hints/context.js.map +1 -1
  63. package/dist/hints/hints.d.ts.map +1 -1
  64. package/dist/hints/hints.js +7 -2
  65. package/dist/hints/hints.js.map +1 -1
  66. package/dist/hints/types.d.ts +3 -4
  67. package/dist/hints/types.d.ts.map +1 -1
  68. package/dist/index.cjs +39556 -3991
  69. package/dist/reporters/htmlReporter.js +3 -2
  70. package/dist/reporters/htmlReporter.js.map +1 -1
  71. package/dist/runtime/androidInstaller.d.ts +27 -0
  72. package/dist/runtime/androidInstaller.d.ts.map +1 -1
  73. package/dist/runtime/androidInstaller.js +138 -2
  74. package/dist/runtime/androidInstaller.js.map +1 -1
  75. package/dist/runtime/browserStepKeys.d.ts +17 -1
  76. package/dist/runtime/browserStepKeys.d.ts.map +1 -1
  77. package/dist/runtime/browserStepKeys.js +25 -0
  78. package/dist/runtime/browserStepKeys.js.map +1 -1
  79. package/dist/runtime/heavyDeps.d.ts +25 -0
  80. package/dist/runtime/heavyDeps.d.ts.map +1 -1
  81. package/dist/runtime/heavyDeps.js +34 -0
  82. package/dist/runtime/heavyDeps.js.map +1 -1
  83. package/dist/runtime/inferRuntimeNeeds.js +8 -2
  84. package/dist/runtime/inferRuntimeNeeds.js.map +1 -1
  85. package/dist/runtime/installer.d.ts +9 -0
  86. package/dist/runtime/installer.d.ts.map +1 -1
  87. package/dist/runtime/installer.js +17 -1
  88. package/dist/runtime/installer.js.map +1 -1
  89. package/dist/runtime/loader.d.ts.map +1 -1
  90. package/dist/runtime/loader.js +16 -8
  91. package/dist/runtime/loader.js.map +1 -1
  92. package/package.json +1 -1
@@ -13,7 +13,7 @@ import { requiredBrowserAssets, ensureBrowserInstalled, } from "../runtime/brows
13
13
  // Single source of truth for browser/driver-requiring step keys.
14
14
  import { BROWSER_STEP_KEYS as driverActions } from "../runtime/browserStepKeys.js";
15
15
  import os from "node:os";
16
- import { log, replaceEnvs, selectSpecsForRun, findFreePort, runConcurrent, runResourceAware, createResourceRegistry, rollUpResults, createAppiumPool, getRunOutputDir, runArchivesArtifacts, sanitizeFilesystemName, evaluateContextRequirements, } from "./utils.js";
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";
18
18
  import { instantiateCursor } from "./tests/moveTo.js";
19
19
  import { goTo } from "./tests/goTo.js";
@@ -21,11 +21,12 @@ import { findElement } from "./tests/findElement.js";
21
21
  import { runShell } from "./tests/runShell.js";
22
22
  import { checkLink } from "./tests/checkLink.js";
23
23
  import { typeKeys } from "./tests/typeKeys.js";
24
+ import { swipeSurface } from "./tests/swipe.js";
24
25
  import { wait } from "./tests/wait.js";
25
26
  import { saveScreenshot } from "./tests/saveScreenshot.js";
26
27
  import { startRecording } from "./tests/startRecording.js";
27
28
  import { stopRecording } from "./tests/stopRecording.js";
28
- import { browserCaptureTitle, browserDownloadDir, coerceRecordContextBrowser, jobIsFfmpegRecording, computeEffectiveConcurrency, jobExclusiveResources, checkSystemBinary, xvfbDisplay, startXvfb, XVFB_SCREEN_SIZE, isRecordingActive, recordStepName, detectRecordingNameConflict, } from "./tests/ffmpegRecorder.js";
29
+ import { browserCaptureTitle, browserDownloadDir, coerceRecordContextBrowser, jobIsFfmpegRecording, computeEffectiveConcurrency, jobExclusiveResources, checkSystemBinary, xvfbDisplay, startXvfb, XVFB_SCREEN_SIZE, isRecordingActive, recordStepName, detectRecordingNameConflict, getFfmpegPath, ffmpegPathEnv, } from "./tests/ffmpegRecorder.js";
29
30
  import { loadVariables } from "./tests/loadVariables.js";
30
31
  import { saveCookie } from "./tests/saveCookie.js";
31
32
  import { loadCookie } from "./tests/loadCookie.js";
@@ -2306,7 +2307,16 @@ function buildAutoRecordStep({ config, spec, test, context, }) {
2306
2307
  const contextSegment = capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context"));
2307
2308
  const recordPath = path.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", contextSegment, "recordings", `${contextSegment}.mp4`);
2308
2309
  return {
2309
- record: { path: recordPath, overwrite: "true", engine: "ffmpeg" },
2310
+ // Mobile-target contexts record the device screen through the app driver
2311
+ // (the internal device plan, resolved from the platform) — pinning ffmpeg
2312
+ // there would host-capture the emulator window, or nothing when headless.
2313
+ record: {
2314
+ path: recordPath,
2315
+ overwrite: "true",
2316
+ ...(isMobileTargetPlatform(context?.platform)
2317
+ ? {}
2318
+ : { engine: "ffmpeg" }),
2319
+ },
2310
2320
  description: "Automatic full-context recording",
2311
2321
  stepId: `${sanitizeFilesystemName(String(test.testId ?? ""), "test")}~autorecord`,
2312
2322
  // Internal marker — the runStep record dispatch flags the started handle as
@@ -3621,6 +3631,7 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
3621
3631
  step: step,
3622
3632
  driver: driver,
3623
3633
  recordingHost,
3634
+ appSession,
3624
3635
  });
3625
3636
  // Push the started recording onto the per-context stack so several can
3626
3637
  // overlap. Carry the step's `id`/`name` so a later stopRecord can target
@@ -3648,11 +3659,16 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
3648
3659
  handle.name = handle.name ?? recordStepName(step.record);
3649
3660
  if (step.__autoRecord) {
3650
3661
  handle.synthetic = true;
3651
- // App-only context: no window exists yet to crop to. Mark the handle
3652
- // so the first app surface to open late-binds its window rect as the
3653
- // crop (startAppSurface), scoping the capture to the app under test.
3654
- if (!driver && appSession)
3662
+ // Desktop app-only context: no window exists yet to crop to. Mark the
3663
+ // handle so the first app surface to open late-binds its window rect
3664
+ // as the crop (startAppSurface), scoping the capture to the app under
3665
+ // test. Mobile contexts don't crop — their pending handles late-START
3666
+ // the device recording instead (appium-pending).
3667
+ if (!driver &&
3668
+ appSession &&
3669
+ !isMobileTargetPlatform(context?.platform)) {
3655
3670
  handle.pendingAppWindowCrop = true;
3671
+ }
3656
3672
  }
3657
3673
  recordingHost.state.recordings.push(handle);
3658
3674
  }
@@ -3699,6 +3715,24 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
3699
3715
  env.ANDROID_HOME = appSession.androidSdkRoot;
3700
3716
  env.ANDROID_SDK_ROOT = appSession.androidSdkRoot;
3701
3717
  }
3718
+ // iOS device recording: the XCUITest driver's
3719
+ // startRecordingScreen shells out to a bare `ffmpeg` on the
3720
+ // server's PATH for encoding, and hosted runners don't reliably
3721
+ // ship one. Put the bundled @ffmpeg-installer binary's directory
3722
+ // first. Best-effort — the session must start even when the
3723
+ // ffmpeg install isn't available (recording then SKIPs with
3724
+ // guidance).
3725
+ if (isMobileTargetPlatform(context?.platform) === "ios") {
3726
+ try {
3727
+ const ffmpegPath = await getFfmpegPath({
3728
+ cacheDir: config?.cacheDir,
3729
+ });
3730
+ Object.assign(env, ffmpegPathEnv(ffmpegPath));
3731
+ }
3732
+ catch {
3733
+ /* best-effort */
3734
+ }
3735
+ }
3702
3736
  const server = await startAppiumServer(appiumEntry, config, undefined, env);
3703
3737
  return { port: server.port, process: server.process };
3704
3738
  },
@@ -3737,6 +3771,14 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
3737
3771
  appSession,
3738
3772
  });
3739
3773
  }
3774
+ else if (typeof step.swipe !== "undefined") {
3775
+ actionResult = await swipeSurface({
3776
+ config: config,
3777
+ step: step,
3778
+ driver: driver,
3779
+ appSession,
3780
+ });
3781
+ }
3740
3782
  else if (typeof step.type !== "undefined") {
3741
3783
  actionResult = await typeKeys({
3742
3784
  config: config,
@@ -3869,20 +3911,11 @@ async function appiumIsReady(port, timeoutMs = 120000) {
3869
3911
  }
3870
3912
  // Start the Appium driver specified in `capabilities`.
3871
3913
  async function driverStart(capabilities, port, maxAttempts = 4, ctx = {}) {
3872
- // Two families of transient, retryable session-creation failures, both worse
3873
- // under concurrency (the TRANSIENT regex below enumerates the specific
3874
- // patterns):
3875
- // 1. POST /session races a just-spawned-or-still-dying Appium (Windows):
3876
- // /status returns 200 from the outgoing process while /session no longer
3877
- // accepts, or Appium's proxy to chromedriver drops the socket ->
3878
- // ECONNREFUSED / ECONNRESET / "socket hang up" / "could not proxy command".
3879
- // 2. Several Chromes launching at once briefly starve resources and
3880
- // ChromeDriver "crashed during startup" / "cannot connect to" /
3881
- // "DevToolsActivePort" / "session not created". A staggered retry lets
3882
- // the contention clear; it recovers on the next attempt in practice.
3883
- // Retry these with linear backoff; any other error is a real session-
3884
- // creation failure and propagates immediately.
3885
- const TRANSIENT = /ECONNREFUSED|ECONNRESET|socket hang up|could not proxy command|crashed during startup|cannot connect to|DevToolsActivePort|session not created/i;
3914
+ // Retryable session-creation failures (transient races/contention, plus the
3915
+ // client-side timeout abort for slow-startup native sessions) are enumerated
3916
+ // by isRetryableSessionError in ./utils.js. Retry those with linear backoff;
3917
+ // any other error is a real session-creation failure and propagates
3918
+ // immediately.
3886
3919
  const wdio = await loadHeavyDep("webdriverio", { ctx });
3887
3920
  // The wdio client aborts the POST /session request after connectionRetryTimeout.
3888
3921
  // A cold native session can take far longer to create than the 2-minute
@@ -3917,7 +3950,7 @@ async function driverStart(capabilities, port, maxAttempts = 4, ctx = {}) {
3917
3950
  }
3918
3951
  catch (err) {
3919
3952
  lastError = err;
3920
- if (!TRANSIENT.test(String(err && err.message)))
3953
+ if (!isRetryableSessionError(String(err && err.message), startupCeiling))
3921
3954
  throw err;
3922
3955
  if (attempt < maxAttempts) {
3923
3956
  await new Promise((r) => setTimeout(r, 500 * attempt));