@agentrhq/webcmd 0.1.1 → 0.1.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/README.md +317 -24
- package/dist/src/adapter-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +72 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-shadow.test.js +49 -0
- package/dist/src/adapter-source.d.ts +11 -0
- package/dist/src/adapter-source.js +24 -0
- package/dist/src/adapter-source.test.d.ts +1 -0
- package/dist/src/adapter-source.test.js +29 -0
- package/dist/src/brand.d.ts +9 -0
- package/dist/src/brand.js +9 -0
- package/dist/src/brand.test.d.ts +1 -0
- package/dist/src/brand.test.js +15 -0
- package/dist/src/browser/analyze.d.ts +118 -0
- package/dist/src/browser/analyze.js +405 -0
- package/dist/src/browser/analyze.test.d.ts +1 -0
- package/dist/src/browser/analyze.test.js +209 -0
- package/dist/src/browser/article-extract.d.ts +57 -0
- package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
- package/dist/src/browser/article-extract.e2e.test.js +105 -0
- package/dist/src/browser/article-extract.js +169 -0
- package/dist/src/browser/article-extract.test.d.ts +1 -0
- package/dist/src/browser/article-extract.test.js +94 -0
- package/dist/src/browser/ax-snapshot.d.ts +37 -0
- package/dist/src/browser/ax-snapshot.js +217 -0
- package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
- package/dist/src/browser/ax-snapshot.test.js +91 -0
- package/dist/src/browser/base-page.d.ts +151 -0
- package/dist/src/browser/base-page.js +1060 -0
- package/dist/src/browser/base-page.test.d.ts +1 -0
- package/dist/src/browser/base-page.test.js +803 -0
- package/dist/src/browser/bridge-readiness.d.ts +14 -0
- package/dist/src/browser/bridge-readiness.js +24 -0
- package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
- package/dist/src/browser/bridge-readiness.test.js +58 -0
- package/dist/src/browser/bridge.d.ts +26 -0
- package/dist/src/browser/bridge.js +58 -0
- package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
- package/dist/src/browser/cdp-click-fixture.test.js +87 -0
- package/dist/src/browser/cdp.d.ts +46 -0
- package/dist/src/browser/cdp.js +536 -0
- package/dist/src/browser/cdp.test.d.ts +1 -0
- package/dist/src/browser/cdp.test.js +79 -0
- package/dist/src/browser/compound.d.ts +59 -0
- package/dist/src/browser/compound.js +112 -0
- package/dist/src/browser/compound.test.d.ts +1 -0
- package/dist/src/browser/compound.test.js +175 -0
- package/dist/src/browser/config.d.ts +2 -0
- package/dist/src/browser/config.js +14 -0
- package/dist/src/browser/daemon-client.d.ts +32 -0
- package/dist/src/browser/daemon-client.js +135 -0
- package/dist/src/browser/daemon-client.test.d.ts +1 -0
- package/dist/src/browser/daemon-client.test.js +326 -0
- package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
- package/dist/src/browser/daemon-lifecycle.js +147 -0
- package/dist/src/browser/daemon-transport.d.ts +54 -0
- package/dist/src/browser/daemon-transport.js +68 -0
- package/dist/src/browser/daemon-version.d.ts +4 -0
- package/dist/src/browser/daemon-version.js +12 -0
- package/dist/src/browser/dom-helpers.d.ts +41 -0
- package/dist/src/browser/dom-helpers.js +239 -0
- package/dist/src/browser/dom-helpers.test.d.ts +1 -0
- package/dist/src/browser/dom-helpers.test.js +106 -0
- package/dist/src/browser/dom-snapshot.d.ts +93 -0
- package/dist/src/browser/dom-snapshot.js +894 -0
- package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/src/browser/dom-snapshot.test.js +313 -0
- package/dist/src/browser/errors.d.ts +38 -0
- package/dist/src/browser/errors.js +79 -0
- package/dist/src/browser/errors.test.d.ts +1 -0
- package/dist/src/browser/errors.test.js +60 -0
- package/dist/src/browser/extract.d.ts +69 -0
- package/dist/src/browser/extract.js +132 -0
- package/dist/src/browser/extract.test.d.ts +1 -0
- package/dist/src/browser/extract.test.js +129 -0
- package/dist/src/browser/find.d.ts +84 -0
- package/dist/src/browser/find.js +398 -0
- package/dist/src/browser/find.test.d.ts +1 -0
- package/dist/src/browser/find.test.js +180 -0
- package/dist/src/browser/html-tree.d.ts +75 -0
- package/dist/src/browser/html-tree.js +112 -0
- package/dist/src/browser/html-tree.test.d.ts +1 -0
- package/dist/src/browser/html-tree.test.js +181 -0
- package/dist/src/browser/index.d.ts +14 -0
- package/dist/src/browser/index.js +12 -0
- package/dist/src/browser/network-cache.d.ts +49 -0
- package/dist/src/browser/network-cache.js +78 -0
- package/dist/src/browser/network-cache.test.d.ts +1 -0
- package/dist/src/browser/network-cache.test.js +75 -0
- package/dist/src/browser/network-interceptor.d.ts +11 -0
- package/dist/src/browser/network-interceptor.js +11 -0
- package/dist/src/browser/network-key.d.ts +22 -0
- package/dist/src/browser/network-key.js +66 -0
- package/dist/src/browser/network-key.test.d.ts +1 -0
- package/dist/src/browser/network-key.test.js +49 -0
- package/dist/src/browser/page.d.ts +83 -0
- package/dist/src/browser/page.js +453 -0
- package/dist/src/browser/page.test.d.ts +1 -0
- package/dist/src/browser/page.test.js +406 -0
- package/dist/src/browser/profile.d.ts +14 -0
- package/dist/src/browser/profile.js +86 -0
- package/dist/src/browser/protocol.d.ts +63 -0
- package/dist/src/browser/protocol.js +1 -0
- package/dist/src/browser/runtime/local-cloak/actions.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/actions.js +240 -0
- package/dist/src/browser/runtime/local-cloak/downloads.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/downloads.js +28 -0
- package/dist/src/browser/runtime/local-cloak/network.d.ts +28 -0
- package/dist/src/browser/runtime/local-cloak/network.js +120 -0
- package/dist/src/browser/runtime/local-cloak/network.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/network.test.js +124 -0
- package/dist/src/browser/runtime/local-cloak/profiles.d.ts +6 -0
- package/dist/src/browser/runtime/local-cloak/profiles.js +17 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.js +19 -0
- package/dist/src/browser/runtime/local-cloak/provider.d.ts +15 -0
- package/dist/src/browser/runtime/local-cloak/provider.js +27 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.js +244 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +77 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.js +280 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.js +92 -0
- package/dist/src/browser/runtime/provider.d.ts +9 -0
- package/dist/src/browser/runtime/provider.js +1 -0
- package/dist/src/browser/runtime/provider.test.d.ts +2 -0
- package/dist/src/browser/runtime/provider.test.js +49 -0
- package/dist/src/browser/shape-filter.d.ts +52 -0
- package/dist/src/browser/shape-filter.js +101 -0
- package/dist/src/browser/shape-filter.test.d.ts +1 -0
- package/dist/src/browser/shape-filter.test.js +101 -0
- package/dist/src/browser/shape.d.ts +23 -0
- package/dist/src/browser/shape.js +95 -0
- package/dist/src/browser/shape.test.d.ts +1 -0
- package/dist/src/browser/shape.test.js +82 -0
- package/dist/src/browser/stealth.d.ts +11 -0
- package/dist/src/browser/stealth.js +359 -0
- package/dist/src/browser/stealth.test.d.ts +1 -0
- package/dist/src/browser/stealth.test.js +134 -0
- package/dist/src/browser/tabs.d.ts +13 -0
- package/dist/src/browser/tabs.js +70 -0
- package/dist/src/browser/target-errors.d.ts +39 -0
- package/dist/src/browser/target-errors.js +45 -0
- package/dist/src/browser/target-errors.test.d.ts +1 -0
- package/dist/src/browser/target-errors.test.js +94 -0
- package/dist/src/browser/target-resolver.d.ts +158 -0
- package/dist/src/browser/target-resolver.js +570 -0
- package/dist/src/browser/target-resolver.test.d.ts +1 -0
- package/dist/src/browser/target-resolver.test.js +118 -0
- package/dist/src/browser/utils.d.ts +20 -0
- package/dist/src/browser/utils.js +64 -0
- package/dist/src/browser/utils.test.d.ts +1 -0
- package/dist/src/browser/utils.test.js +29 -0
- package/dist/src/browser/verify-fixture.d.ts +65 -0
- package/dist/src/browser/verify-fixture.js +318 -0
- package/dist/src/browser/verify-fixture.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.test.js +219 -0
- package/dist/src/browser/visual-refs.d.ts +11 -0
- package/dist/src/browser/visual-refs.js +108 -0
- package/dist/src/browser.test.d.ts +1 -0
- package/dist/src/browser.test.js +297 -0
- package/dist/src/build-manifest.d.ts +103 -0
- package/dist/src/build-manifest.js +363 -0
- package/dist/src/build-manifest.test.d.ts +1 -0
- package/dist/src/build-manifest.test.js +341 -0
- package/dist/src/capabilityRouting.d.ts +19 -0
- package/dist/src/capabilityRouting.js +56 -0
- package/dist/src/capabilityRouting.test.d.ts +1 -0
- package/dist/src/capabilityRouting.test.js +63 -0
- package/dist/src/cli-argv-preprocess.d.ts +63 -0
- package/dist/src/cli-argv-preprocess.js +303 -0
- package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
- package/dist/src/cli-argv-preprocess.test.js +304 -0
- package/dist/src/cli.d.ts +79 -0
- package/dist/src/cli.js +3350 -0
- package/dist/src/cli.test.d.ts +1 -0
- package/dist/src/cli.test.js +3041 -0
- package/dist/src/commanderAdapter.d.ts +21 -0
- package/dist/src/commanderAdapter.js +208 -0
- package/dist/src/commanderAdapter.test.d.ts +1 -0
- package/dist/src/commanderAdapter.test.js +352 -0
- package/dist/src/commands/auth.d.ts +40 -0
- package/dist/src/commands/auth.js +434 -0
- package/dist/src/commands/auth.test.d.ts +1 -0
- package/dist/src/commands/auth.test.js +252 -0
- package/dist/src/commands/daemon.d.ts +9 -0
- package/dist/src/commands/daemon.js +91 -0
- package/dist/src/commands/daemon.test.d.ts +1 -0
- package/dist/src/commands/daemon.test.js +293 -0
- package/dist/src/completion-fast.d.ts +22 -0
- package/dist/src/completion-fast.js +88 -0
- package/dist/src/completion-shared.d.ts +13 -0
- package/dist/src/completion-shared.js +61 -0
- package/dist/src/completion.d.ts +21 -0
- package/dist/src/completion.js +68 -0
- package/dist/src/completion.test.d.ts +1 -0
- package/dist/src/completion.test.js +24 -0
- package/dist/src/constants.d.ts +18 -0
- package/dist/src/constants.js +41 -0
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +553 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/convention-audit.test.js +255 -0
- package/dist/src/daemon/server.d.ts +13 -0
- package/dist/src/daemon/server.js +185 -0
- package/dist/src/daemon/server.test.d.ts +1 -0
- package/dist/src/daemon/server.test.js +132 -0
- package/dist/src/daemon-utils.d.ts +18 -0
- package/dist/src/daemon-utils.js +37 -0
- package/dist/src/daemon.d.ts +1 -0
- package/dist/src/daemon.js +27 -0
- package/dist/src/daemon.test.d.ts +1 -0
- package/dist/src/daemon.test.js +60 -0
- package/dist/src/discovery.d.ts +46 -0
- package/dist/src/discovery.js +341 -0
- package/dist/src/doctor.d.ts +39 -0
- package/dist/src/doctor.js +168 -0
- package/dist/src/doctor.test.d.ts +1 -0
- package/dist/src/doctor.test.js +302 -0
- package/dist/src/download/article-download.d.ts +72 -0
- package/dist/src/download/article-download.js +305 -0
- package/dist/src/download/article-download.test.d.ts +1 -0
- package/dist/src/download/article-download.test.js +235 -0
- package/dist/src/download/index.d.ts +71 -0
- package/dist/src/download/index.js +363 -0
- package/dist/src/download/index.test.d.ts +1 -0
- package/dist/src/download/index.test.js +174 -0
- package/dist/src/download/media-download.d.ts +49 -0
- package/dist/src/download/media-download.js +127 -0
- package/dist/src/download/media-download.test.d.ts +1 -0
- package/dist/src/download/media-download.test.js +112 -0
- package/dist/src/download/progress.d.ts +36 -0
- package/dist/src/download/progress.js +120 -0
- package/dist/src/download/progress.test.d.ts +1 -0
- package/dist/src/download/progress.test.js +36 -0
- package/dist/src/electron-apps.d.ts +31 -0
- package/dist/src/electron-apps.js +91 -0
- package/dist/src/electron-apps.test.d.ts +1 -0
- package/dist/src/electron-apps.test.js +76 -0
- package/dist/src/engine.test.d.ts +1 -0
- package/dist/src/engine.test.js +312 -0
- package/dist/src/errors.d.ts +114 -0
- package/dist/src/errors.js +174 -0
- package/dist/src/errors.test.d.ts +1 -0
- package/dist/src/errors.test.js +109 -0
- package/dist/src/execution.d.ts +24 -0
- package/dist/src/execution.js +509 -0
- package/dist/src/execution.test.d.ts +1 -0
- package/dist/src/execution.test.js +645 -0
- package/dist/src/external.d.ts +49 -0
- package/dist/src/external.js +211 -0
- package/dist/src/external.test.d.ts +1 -0
- package/dist/src/external.test.js +110 -0
- package/dist/src/help.d.ts +88 -0
- package/dist/src/help.js +556 -0
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +58 -0
- package/dist/src/hooks.d.ts +46 -0
- package/dist/src/hooks.js +58 -0
- package/dist/src/hooks.test.d.ts +4 -0
- package/dist/src/hooks.test.js +92 -0
- package/dist/src/interceptor.d.ts +44 -0
- package/dist/src/interceptor.js +183 -0
- package/dist/src/interceptor.test.d.ts +4 -0
- package/dist/src/interceptor.test.js +81 -0
- package/dist/src/launcher.d.ts +41 -0
- package/dist/src/launcher.js +226 -0
- package/dist/src/launcher.test.d.ts +1 -0
- package/dist/src/launcher.test.js +153 -0
- package/dist/src/logger.d.ts +26 -0
- package/dist/src/logger.js +49 -0
- package/dist/src/main.d.ts +5 -0
- package/dist/src/main.js +177 -0
- package/dist/src/manifest-types.d.ts +43 -0
- package/dist/src/manifest-types.js +9 -0
- package/dist/src/node-network.d.ts +10 -0
- package/dist/src/node-network.js +174 -0
- package/dist/src/node-network.test.d.ts +1 -0
- package/dist/src/node-network.test.js +55 -0
- package/dist/src/observation/artifact.d.ts +16 -0
- package/dist/src/observation/artifact.js +260 -0
- package/dist/src/observation/artifact.test.d.ts +1 -0
- package/dist/src/observation/artifact.test.js +121 -0
- package/dist/src/observation/events.d.ts +89 -0
- package/dist/src/observation/events.js +1 -0
- package/dist/src/observation/index.d.ts +7 -0
- package/dist/src/observation/index.js +7 -0
- package/dist/src/observation/manager.d.ts +9 -0
- package/dist/src/observation/manager.js +27 -0
- package/dist/src/observation/manager.test.d.ts +1 -0
- package/dist/src/observation/manager.test.js +13 -0
- package/dist/src/observation/redaction.d.ts +11 -0
- package/dist/src/observation/redaction.js +81 -0
- package/dist/src/observation/redaction.test.d.ts +1 -0
- package/dist/src/observation/redaction.test.js +32 -0
- package/dist/src/observation/retention.d.ts +32 -0
- package/dist/src/observation/retention.js +160 -0
- package/dist/src/observation/retention.test.d.ts +1 -0
- package/dist/src/observation/retention.test.js +118 -0
- package/dist/src/observation/ring-buffer.d.ts +22 -0
- package/dist/src/observation/ring-buffer.js +45 -0
- package/dist/src/observation/ring-buffer.test.d.ts +1 -0
- package/dist/src/observation/ring-buffer.test.js +22 -0
- package/dist/src/observation/session.d.ts +25 -0
- package/dist/src/observation/session.js +50 -0
- package/dist/src/output.d.ts +14 -0
- package/dist/src/output.js +152 -0
- package/dist/src/output.test.d.ts +1 -0
- package/dist/src/output.test.js +53 -0
- package/dist/src/package-exports.test.d.ts +1 -0
- package/dist/src/package-exports.test.js +112 -0
- package/dist/src/package-paths.d.ts +8 -0
- package/dist/src/package-paths.js +41 -0
- package/dist/src/pipeline/executor.d.ts +11 -0
- package/dist/src/pipeline/executor.js +93 -0
- package/dist/src/pipeline/executor.test.d.ts +4 -0
- package/dist/src/pipeline/executor.test.js +182 -0
- package/dist/src/pipeline/index.d.ts +5 -0
- package/dist/src/pipeline/index.js +5 -0
- package/dist/src/pipeline/registry.d.ts +28 -0
- package/dist/src/pipeline/registry.js +55 -0
- package/dist/src/pipeline/steps/browser.d.ts +13 -0
- package/dist/src/pipeline/steps/browser.js +86 -0
- package/dist/src/pipeline/steps/download.d.ts +18 -0
- package/dist/src/pipeline/steps/download.js +252 -0
- package/dist/src/pipeline/steps/download.test.d.ts +1 -0
- package/dist/src/pipeline/steps/download.test.js +102 -0
- package/dist/src/pipeline/steps/fetch.d.ts +5 -0
- package/dist/src/pipeline/steps/fetch.js +122 -0
- package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/src/pipeline/steps/fetch.test.js +117 -0
- package/dist/src/pipeline/steps/intercept.d.ts +5 -0
- package/dist/src/pipeline/steps/intercept.js +50 -0
- package/dist/src/pipeline/steps/tap.d.ts +12 -0
- package/dist/src/pipeline/steps/tap.js +93 -0
- package/dist/src/pipeline/steps/transform.d.ts +9 -0
- package/dist/src/pipeline/steps/transform.js +70 -0
- package/dist/src/pipeline/template.d.ts +17 -0
- package/dist/src/pipeline/template.js +344 -0
- package/dist/src/pipeline/template.test.d.ts +4 -0
- package/dist/src/pipeline/template.test.js +197 -0
- package/dist/src/pipeline/transform.test.d.ts +4 -0
- package/dist/src/pipeline/transform.test.js +133 -0
- package/dist/src/plugin-manifest.d.ts +70 -0
- package/dist/src/plugin-manifest.js +160 -0
- package/dist/src/plugin-manifest.test.d.ts +4 -0
- package/dist/src/plugin-manifest.test.js +179 -0
- package/dist/src/plugin-scaffold.d.ts +28 -0
- package/dist/src/plugin-scaffold.js +143 -0
- package/dist/src/plugin-scaffold.test.d.ts +4 -0
- package/dist/src/plugin-scaffold.test.js +83 -0
- package/dist/src/plugin.d.ts +146 -0
- package/dist/src/plugin.js +1303 -0
- package/dist/src/plugin.test.d.ts +4 -0
- package/dist/src/plugin.test.js +1363 -0
- package/dist/src/registry-api.d.ts +13 -0
- package/dist/src/registry-api.js +10 -0
- package/dist/src/registry.d.ts +123 -0
- package/dist/src/registry.js +125 -0
- package/dist/src/registry.test.d.ts +4 -0
- package/dist/src/registry.test.js +209 -0
- package/dist/src/runtime-copy.test.d.ts +1 -0
- package/dist/src/runtime-copy.test.js +29 -0
- package/dist/src/runtime-detect.d.ts +31 -0
- package/dist/src/runtime-detect.js +47 -0
- package/dist/src/runtime-detect.test.d.ts +1 -0
- package/dist/src/runtime-detect.test.js +39 -0
- package/dist/src/runtime-identity.test.d.ts +1 -0
- package/dist/src/runtime-identity.test.js +18 -0
- package/dist/src/runtime.d.ts +47 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/scripts/framework.d.ts +7 -0
- package/dist/src/scripts/framework.js +25 -0
- package/dist/src/scripts/interact.d.ts +4 -0
- package/dist/src/scripts/interact.js +20 -0
- package/dist/src/scripts/store.d.ts +13 -0
- package/dist/src/scripts/store.js +48 -0
- package/dist/src/serialization.d.ts +42 -0
- package/dist/src/serialization.js +103 -0
- package/dist/src/serialization.test.d.ts +1 -0
- package/dist/src/serialization.test.js +78 -0
- package/dist/src/skills.d.ts +14 -0
- package/dist/src/skills.js +127 -0
- package/dist/src/skills.test.d.ts +1 -0
- package/dist/src/skills.test.js +71 -0
- package/dist/src/snapshotFormatter.d.ts +11 -0
- package/dist/src/snapshotFormatter.js +338 -0
- package/dist/src/snapshotFormatter.test.d.ts +7 -0
- package/dist/src/snapshotFormatter.test.js +521 -0
- package/dist/src/tui.d.ts +28 -0
- package/dist/src/tui.js +179 -0
- package/dist/src/tui.test.d.ts +1 -0
- package/dist/src/tui.test.js +19 -0
- package/dist/src/types.d.ts +236 -0
- package/dist/src/types.js +7 -0
- package/dist/src/update-check.d.ts +66 -0
- package/dist/src/update-check.js +206 -0
- package/dist/src/update-check.test.d.ts +1 -0
- package/dist/src/update-check.test.js +117 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +152 -0
- package/dist/src/utils.test.d.ts +1 -0
- package/dist/src/utils.test.js +155 -0
- package/dist/src/validate.d.ts +21 -0
- package/dist/src/validate.js +130 -0
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/dist/src/verify.d.ts +26 -0
- package/dist/src/verify.js +67 -0
- package/dist/src/version.d.ts +4 -0
- package/dist/src/version.js +22 -0
- package/dist/src/weixin-download.test.d.ts +1 -0
- package/dist/src/weixin-download.test.js +45 -0
- package/package.json +4 -3
- package/scripts/check-package-bin.mjs +95 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { executeCommand, prepareCommandArgs } from './execution.js';
|
|
6
|
+
import { ArgumentError, TimeoutError, toEnvelope } from './errors.js';
|
|
7
|
+
import { cli, Strategy } from './registry.js';
|
|
8
|
+
import * as runtime from './runtime.js';
|
|
9
|
+
import * as capRouting from './capabilityRouting.js';
|
|
10
|
+
describe('executeCommand — non-browser timeout', () => {
|
|
11
|
+
it('applies the user --timeout arg as the ceiling for non-browser commands', async () => {
|
|
12
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
13
|
+
const cmd = cli({
|
|
14
|
+
site: 'test-execution',
|
|
15
|
+
name: 'non-browser-timeout', access: 'read',
|
|
16
|
+
description: 'test non-browser --timeout enforcement',
|
|
17
|
+
browser: false,
|
|
18
|
+
strategy: Strategy.PUBLIC,
|
|
19
|
+
args: [
|
|
20
|
+
{ name: 'timeout', type: 'int', required: false, default: 5, help: 'Max seconds' },
|
|
21
|
+
],
|
|
22
|
+
func: async () => [{ ok: true }],
|
|
23
|
+
});
|
|
24
|
+
await executeCommand(cmd, {});
|
|
25
|
+
expect(runWithTimeoutSpy).toHaveBeenCalledTimes(1);
|
|
26
|
+
// Ceiling = user-supplied/default timeout + 30s padding (adapter return room).
|
|
27
|
+
expect(runWithTimeoutSpy.mock.calls[0]?.[1]).toMatchObject({
|
|
28
|
+
timeout: 35,
|
|
29
|
+
label: 'test-execution/non-browser-timeout',
|
|
30
|
+
});
|
|
31
|
+
vi.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
it('fires a TimeoutError when the inner adapter exceeds the --timeout ceiling', async () => {
|
|
34
|
+
const cmd = cli({
|
|
35
|
+
site: 'test-execution',
|
|
36
|
+
name: 'non-browser-timeout-fires', access: 'read',
|
|
37
|
+
description: 'test that the ceiling actually cancels the adapter',
|
|
38
|
+
browser: false,
|
|
39
|
+
strategy: Strategy.PUBLIC,
|
|
40
|
+
args: [
|
|
41
|
+
{ name: 'timeout', type: 'int', required: false, default: 1, help: 'Max seconds' },
|
|
42
|
+
],
|
|
43
|
+
func: () => new Promise(() => { }),
|
|
44
|
+
});
|
|
45
|
+
// Spy on runWithTimeout to intercept and pass a tiny ceiling so the test
|
|
46
|
+
// doesn't have to wait the real (1+30)s. We still verify the TimeoutError
|
|
47
|
+
// surface — code, label, hint — that users see.
|
|
48
|
+
vi.spyOn(runtime, 'runWithTimeout').mockImplementation(async (promise, opts) => {
|
|
49
|
+
return runtime.withTimeoutMs(promise, 50, () => new TimeoutError(opts.label ?? 'op', opts.timeout, opts.hint));
|
|
50
|
+
});
|
|
51
|
+
const error = await executeCommand(cmd, {}).catch((err) => err);
|
|
52
|
+
expect(error).toBeInstanceOf(TimeoutError);
|
|
53
|
+
expect(error).toMatchObject({
|
|
54
|
+
code: 'TIMEOUT',
|
|
55
|
+
hint: 'Pass a higher --timeout value (currently 1s)',
|
|
56
|
+
});
|
|
57
|
+
vi.restoreAllMocks();
|
|
58
|
+
});
|
|
59
|
+
it('runs non-browser commands without a ceiling when no --timeout arg is declared', async () => {
|
|
60
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
61
|
+
const cmd = cli({
|
|
62
|
+
site: 'test-execution',
|
|
63
|
+
name: 'non-browser-no-timeout', access: 'read',
|
|
64
|
+
description: 'test that omitting --timeout means no ceiling',
|
|
65
|
+
browser: false,
|
|
66
|
+
strategy: Strategy.PUBLIC,
|
|
67
|
+
func: async () => [{ ok: true }],
|
|
68
|
+
});
|
|
69
|
+
await executeCommand(cmd, {});
|
|
70
|
+
expect(runWithTimeoutSpy).not.toHaveBeenCalled();
|
|
71
|
+
vi.restoreAllMocks();
|
|
72
|
+
});
|
|
73
|
+
it('rejects invalid --timeout values instead of silently disabling the non-browser ceiling', async () => {
|
|
74
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
75
|
+
const cmd = cli({
|
|
76
|
+
site: 'test-execution',
|
|
77
|
+
name: 'non-browser-invalid-timeout', access: 'read',
|
|
78
|
+
description: 'test invalid --timeout fails upfront',
|
|
79
|
+
browser: false,
|
|
80
|
+
strategy: Strategy.PUBLIC,
|
|
81
|
+
args: [
|
|
82
|
+
{ name: 'timeout', type: 'int', required: false, default: 5, help: 'Max seconds' },
|
|
83
|
+
],
|
|
84
|
+
func: async () => [{ ok: true }],
|
|
85
|
+
});
|
|
86
|
+
await expect(executeCommand(cmd, { timeout: 0 })).rejects.toBeInstanceOf(ArgumentError);
|
|
87
|
+
await expect(executeCommand(cmd, { timeout: -1 })).rejects.toBeInstanceOf(ArgumentError);
|
|
88
|
+
await expect(executeCommand(cmd, { timeout: 1.5 })).rejects.toBeInstanceOf(ArgumentError);
|
|
89
|
+
expect(runWithTimeoutSpy).not.toHaveBeenCalled();
|
|
90
|
+
vi.restoreAllMocks();
|
|
91
|
+
});
|
|
92
|
+
it('applies the user --timeout arg as the ceiling for browser commands (with +30s padding)', async () => {
|
|
93
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
94
|
+
const mockPage = { closeWindow };
|
|
95
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
96
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
97
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
98
|
+
const cmd = cli({
|
|
99
|
+
site: 'test-execution',
|
|
100
|
+
name: 'browser-with-timeout', access: 'read',
|
|
101
|
+
description: 'test browser --timeout enforcement',
|
|
102
|
+
browser: true,
|
|
103
|
+
strategy: Strategy.PUBLIC,
|
|
104
|
+
args: [
|
|
105
|
+
{ name: 'timeout', type: 'int', required: false, default: 5, help: 'Max seconds' },
|
|
106
|
+
],
|
|
107
|
+
func: async () => [{ ok: true }],
|
|
108
|
+
});
|
|
109
|
+
await executeCommand(cmd, {});
|
|
110
|
+
expect(runWithTimeoutSpy).toHaveBeenCalledTimes(1);
|
|
111
|
+
expect(runWithTimeoutSpy.mock.calls[0]?.[1]).toMatchObject({
|
|
112
|
+
timeout: 35,
|
|
113
|
+
label: 'test-execution/browser-with-timeout',
|
|
114
|
+
});
|
|
115
|
+
vi.restoreAllMocks();
|
|
116
|
+
});
|
|
117
|
+
it('falls back to DEFAULT_BROWSER_COMMAND_TIMEOUT for browser commands without a --timeout arg', async () => {
|
|
118
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
119
|
+
const mockPage = { closeWindow };
|
|
120
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
121
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
122
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
123
|
+
const cmd = cli({
|
|
124
|
+
site: 'test-execution',
|
|
125
|
+
name: 'browser-no-timeout', access: 'read',
|
|
126
|
+
description: 'test browser fallback to global default',
|
|
127
|
+
browser: true,
|
|
128
|
+
strategy: Strategy.PUBLIC,
|
|
129
|
+
func: async () => [{ ok: true }],
|
|
130
|
+
});
|
|
131
|
+
await executeCommand(cmd, {});
|
|
132
|
+
expect(runWithTimeoutSpy).toHaveBeenCalledTimes(1);
|
|
133
|
+
expect(runWithTimeoutSpy.mock.calls[0]?.[1]).toMatchObject({
|
|
134
|
+
timeout: runtime.DEFAULT_BROWSER_COMMAND_TIMEOUT,
|
|
135
|
+
label: 'test-execution/browser-no-timeout',
|
|
136
|
+
});
|
|
137
|
+
vi.restoreAllMocks();
|
|
138
|
+
});
|
|
139
|
+
it('reuses a persistent site browser session and keeps the tab lease open', async () => {
|
|
140
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
141
|
+
const mockPage = { closeWindow };
|
|
142
|
+
const sessionOpts = [];
|
|
143
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
144
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
145
|
+
sessionOpts.push(opts ?? {});
|
|
146
|
+
return fn(mockPage);
|
|
147
|
+
});
|
|
148
|
+
const cmd = cli({
|
|
149
|
+
site: 'test-execution',
|
|
150
|
+
name: 'site-session-persistent', access: 'read',
|
|
151
|
+
description: 'test persistent site session',
|
|
152
|
+
browser: true,
|
|
153
|
+
strategy: Strategy.PUBLIC,
|
|
154
|
+
siteSession: 'persistent',
|
|
155
|
+
func: async () => [{ ok: true }],
|
|
156
|
+
});
|
|
157
|
+
await executeCommand(cmd, {});
|
|
158
|
+
await executeCommand(cmd, {}, false, { keepTab: 'false' });
|
|
159
|
+
expect(sessionOpts).toHaveLength(2);
|
|
160
|
+
expect(sessionOpts[0]).toMatchObject({ session: 'site:test-execution', windowMode: 'background', siteSession: 'persistent' });
|
|
161
|
+
expect(sessionOpts[1]).toMatchObject({ session: 'site:test-execution', windowMode: 'background', siteSession: 'persistent' });
|
|
162
|
+
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
163
|
+
expect(sessionOpts[1]?.idleTimeout).toBeUndefined();
|
|
164
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
165
|
+
vi.restoreAllMocks();
|
|
166
|
+
});
|
|
167
|
+
it('keeps default browser commands on one-shot adapter sessions', async () => {
|
|
168
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
169
|
+
const mockPage = { closeWindow };
|
|
170
|
+
const sessionOpts = [];
|
|
171
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
172
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
173
|
+
sessionOpts.push(opts ?? {});
|
|
174
|
+
return fn(mockPage);
|
|
175
|
+
});
|
|
176
|
+
const cmd = cli({
|
|
177
|
+
site: 'test-execution',
|
|
178
|
+
name: 'site-session-default', access: 'read',
|
|
179
|
+
description: 'test default one-shot browser session',
|
|
180
|
+
browser: true,
|
|
181
|
+
strategy: Strategy.PUBLIC,
|
|
182
|
+
func: async () => [{ ok: true }],
|
|
183
|
+
});
|
|
184
|
+
await executeCommand(cmd, {});
|
|
185
|
+
await executeCommand(cmd, {});
|
|
186
|
+
expect(sessionOpts).toHaveLength(2);
|
|
187
|
+
expect(sessionOpts[0]?.session).toMatch(/^site:test-execution:/);
|
|
188
|
+
expect(sessionOpts[1]?.session).toMatch(/^site:test-execution:/);
|
|
189
|
+
expect(sessionOpts[0]?.session).not.toBe(sessionOpts[1]?.session);
|
|
190
|
+
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
191
|
+
expect(sessionOpts[1]?.idleTimeout).toBeUndefined();
|
|
192
|
+
expect(sessionOpts[0]?.windowMode).toBe('background');
|
|
193
|
+
expect(sessionOpts[1]?.windowMode).toBe('background');
|
|
194
|
+
expect(closeWindow).toHaveBeenCalledTimes(2);
|
|
195
|
+
vi.restoreAllMocks();
|
|
196
|
+
});
|
|
197
|
+
it('lets user --site-session ephemeral override adapter persistent metadata', async () => {
|
|
198
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
199
|
+
const mockPage = { closeWindow };
|
|
200
|
+
const sessionOpts = [];
|
|
201
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
202
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
203
|
+
sessionOpts.push(opts ?? {});
|
|
204
|
+
return fn(mockPage);
|
|
205
|
+
});
|
|
206
|
+
try {
|
|
207
|
+
const cmd = cli({
|
|
208
|
+
site: 'test-execution',
|
|
209
|
+
name: 'site-session-override-ephemeral', access: 'read',
|
|
210
|
+
description: 'test user site-session override',
|
|
211
|
+
browser: true,
|
|
212
|
+
strategy: Strategy.PUBLIC,
|
|
213
|
+
siteSession: 'persistent',
|
|
214
|
+
func: async () => [{ ok: true }],
|
|
215
|
+
});
|
|
216
|
+
await executeCommand(cmd, {}, false, { siteSession: 'ephemeral' });
|
|
217
|
+
expect(sessionOpts).toHaveLength(1);
|
|
218
|
+
expect(sessionOpts[0]?.session).toMatch(/^site:test-execution:/);
|
|
219
|
+
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
220
|
+
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
vi.restoreAllMocks();
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
it('skips repeated domain pre-navigation for persistent site sessions', async () => {
|
|
227
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
228
|
+
const goto = vi.fn().mockResolvedValue(undefined);
|
|
229
|
+
const mockPage = {
|
|
230
|
+
closeWindow,
|
|
231
|
+
goto,
|
|
232
|
+
getCurrentUrl: vi.fn().mockResolvedValue('https://grok.com/chat/abc'),
|
|
233
|
+
};
|
|
234
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
235
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
236
|
+
const cmd = cli({
|
|
237
|
+
site: 'test-execution',
|
|
238
|
+
name: 'site-session-skip-prenav', access: 'read',
|
|
239
|
+
description: 'test reused same-domain tabs do not reset conversation state',
|
|
240
|
+
browser: true,
|
|
241
|
+
strategy: Strategy.COOKIE,
|
|
242
|
+
domain: 'grok.com',
|
|
243
|
+
siteSession: 'persistent',
|
|
244
|
+
func: async () => [{ ok: true }],
|
|
245
|
+
});
|
|
246
|
+
await executeCommand(cmd, {});
|
|
247
|
+
expect(goto).not.toHaveBeenCalled();
|
|
248
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
249
|
+
vi.restoreAllMocks();
|
|
250
|
+
});
|
|
251
|
+
it('keeps explicit path pre-navigation for persistent site sessions', async () => {
|
|
252
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
253
|
+
const goto = vi.fn().mockResolvedValue(undefined);
|
|
254
|
+
const mockPage = {
|
|
255
|
+
closeWindow,
|
|
256
|
+
goto,
|
|
257
|
+
getCurrentUrl: vi.fn().mockResolvedValue('https://example.com/other'),
|
|
258
|
+
};
|
|
259
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
260
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
261
|
+
const cmd = cli({
|
|
262
|
+
site: 'test-execution',
|
|
263
|
+
name: 'site-session-path-prenav', access: 'read',
|
|
264
|
+
description: 'test explicit path pre-navigation still runs',
|
|
265
|
+
browser: true,
|
|
266
|
+
strategy: Strategy.COOKIE,
|
|
267
|
+
domain: 'example.com',
|
|
268
|
+
navigateBefore: 'https://example.com/dashboard',
|
|
269
|
+
siteSession: 'persistent',
|
|
270
|
+
func: async () => [{ ok: true }],
|
|
271
|
+
});
|
|
272
|
+
await executeCommand(cmd, {});
|
|
273
|
+
expect(goto).toHaveBeenCalledWith('https://example.com/dashboard');
|
|
274
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
275
|
+
vi.restoreAllMocks();
|
|
276
|
+
});
|
|
277
|
+
it('respects navigateBefore=false so adapter range validation fails before browser navigation', async () => {
|
|
278
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
279
|
+
const goto = vi.fn().mockResolvedValue(undefined);
|
|
280
|
+
const mockPage = {
|
|
281
|
+
closeWindow,
|
|
282
|
+
goto,
|
|
283
|
+
getCurrentUrl: vi.fn().mockResolvedValue('about:blank'),
|
|
284
|
+
};
|
|
285
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
286
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
287
|
+
const cmd = cli({
|
|
288
|
+
site: 'test-execution',
|
|
289
|
+
name: 'browser-invalid-limit-no-prenav', access: 'read',
|
|
290
|
+
description: 'test adapter range validation can fail before pre-nav',
|
|
291
|
+
browser: true,
|
|
292
|
+
strategy: Strategy.COOKIE,
|
|
293
|
+
domain: 'www.facebook.com',
|
|
294
|
+
navigateBefore: false,
|
|
295
|
+
args: [
|
|
296
|
+
{ name: 'limit', type: 'int', required: false, default: 15, help: 'Limit' },
|
|
297
|
+
],
|
|
298
|
+
func: async (_page, args) => {
|
|
299
|
+
const limit = Number(args.limit);
|
|
300
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
301
|
+
throw new ArgumentError('--limit must be a positive integer in [1, 100]');
|
|
302
|
+
}
|
|
303
|
+
return [{ ok: true }];
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
await expect(executeCommand(cmd, { limit: 0 })).rejects.toBeInstanceOf(ArgumentError);
|
|
307
|
+
expect(goto).not.toHaveBeenCalled();
|
|
308
|
+
vi.restoreAllMocks();
|
|
309
|
+
});
|
|
310
|
+
it('rejects invalid --timeout values instead of falling back to the browser default', async () => {
|
|
311
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
312
|
+
const mockPage = { closeWindow };
|
|
313
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
314
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
315
|
+
const runWithTimeoutSpy = vi.spyOn(runtime, 'runWithTimeout');
|
|
316
|
+
const cmd = cli({
|
|
317
|
+
site: 'test-execution',
|
|
318
|
+
name: 'browser-invalid-timeout', access: 'read',
|
|
319
|
+
description: 'test invalid browser --timeout fails upfront',
|
|
320
|
+
browser: true,
|
|
321
|
+
strategy: Strategy.PUBLIC,
|
|
322
|
+
args: [
|
|
323
|
+
{ name: 'timeout', type: 'int', required: false, default: 5, help: 'Max seconds' },
|
|
324
|
+
],
|
|
325
|
+
func: async () => [{ ok: true }],
|
|
326
|
+
});
|
|
327
|
+
await expect(executeCommand(cmd, { timeout: 0 })).rejects.toBeInstanceOf(ArgumentError);
|
|
328
|
+
await expect(executeCommand(cmd, { timeout: -1 })).rejects.toBeInstanceOf(ArgumentError);
|
|
329
|
+
await expect(executeCommand(cmd, { timeout: 1.5 })).rejects.toBeInstanceOf(ArgumentError);
|
|
330
|
+
expect(runWithTimeoutSpy).not.toHaveBeenCalled();
|
|
331
|
+
vi.restoreAllMocks();
|
|
332
|
+
});
|
|
333
|
+
it('rejects invalid browser --timeout before opening a session or pre-navigating', async () => {
|
|
334
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
335
|
+
const browserSessionSpy = vi.spyOn(runtime, 'browserSession');
|
|
336
|
+
const cmd = cli({
|
|
337
|
+
site: 'test-execution',
|
|
338
|
+
name: 'browser-invalid-timeout-prenav', access: 'read',
|
|
339
|
+
description: 'test invalid browser --timeout fails before session setup',
|
|
340
|
+
browser: true,
|
|
341
|
+
strategy: Strategy.PUBLIC,
|
|
342
|
+
navigateBefore: 'https://example.com/',
|
|
343
|
+
args: [
|
|
344
|
+
{ name: 'timeout', type: 'int', required: false, default: 5, help: 'Max seconds' },
|
|
345
|
+
],
|
|
346
|
+
func: async () => [{ ok: true }],
|
|
347
|
+
});
|
|
348
|
+
await expect(executeCommand(cmd, { timeout: 0 })).rejects.toBeInstanceOf(ArgumentError);
|
|
349
|
+
expect(browserSessionSpy).not.toHaveBeenCalled();
|
|
350
|
+
vi.restoreAllMocks();
|
|
351
|
+
});
|
|
352
|
+
it('calls closeWindow on browser command failure', async () => {
|
|
353
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
354
|
+
const mockPage = { closeWindow };
|
|
355
|
+
// Mock shouldUseBrowserSession to return true
|
|
356
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
357
|
+
// Mock browserSession to invoke the callback with our mock page
|
|
358
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => {
|
|
359
|
+
return fn(mockPage);
|
|
360
|
+
});
|
|
361
|
+
const cmd = cli({
|
|
362
|
+
site: 'test-execution',
|
|
363
|
+
name: 'browser-close-on-error', access: 'read',
|
|
364
|
+
description: 'test closeWindow on failure',
|
|
365
|
+
browser: true,
|
|
366
|
+
strategy: Strategy.PUBLIC,
|
|
367
|
+
func: async () => { throw new Error('adapter failure'); },
|
|
368
|
+
});
|
|
369
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow('adapter failure');
|
|
370
|
+
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
371
|
+
vi.restoreAllMocks();
|
|
372
|
+
});
|
|
373
|
+
it('skips closeWindow when --keep-tab=true (success path)', async () => {
|
|
374
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
375
|
+
const mockPage = { closeWindow };
|
|
376
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
377
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
378
|
+
try {
|
|
379
|
+
const cmd = cli({
|
|
380
|
+
site: 'test-execution',
|
|
381
|
+
name: 'browser-keep-tab-success', access: 'read',
|
|
382
|
+
description: 'test closeWindow skipped with --keep-tab on success',
|
|
383
|
+
browser: true,
|
|
384
|
+
strategy: Strategy.PUBLIC,
|
|
385
|
+
func: async () => [{ ok: true }],
|
|
386
|
+
});
|
|
387
|
+
await executeCommand(cmd, {}, false, { keepTab: 'true' });
|
|
388
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
389
|
+
}
|
|
390
|
+
finally {
|
|
391
|
+
vi.restoreAllMocks();
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
it('skips closeWindow when --keep-tab=true (failure path)', async () => {
|
|
395
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
396
|
+
const mockPage = { closeWindow };
|
|
397
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
398
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
399
|
+
try {
|
|
400
|
+
const cmd = cli({
|
|
401
|
+
site: 'test-execution',
|
|
402
|
+
name: 'browser-keep-tab-failure', access: 'read',
|
|
403
|
+
description: 'test closeWindow skipped with --keep-tab on failure',
|
|
404
|
+
browser: true,
|
|
405
|
+
strategy: Strategy.PUBLIC,
|
|
406
|
+
func: async () => { throw new Error('adapter failure'); },
|
|
407
|
+
});
|
|
408
|
+
await expect(executeCommand(cmd, {}, false, { keepTab: 'true' })).rejects.toThrow('adapter failure');
|
|
409
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
vi.restoreAllMocks();
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
it('lets browser common options override adapter window and keep-tab defaults', async () => {
|
|
416
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
417
|
+
const mockPage = { closeWindow };
|
|
418
|
+
const sessionOpts = [];
|
|
419
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
420
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
421
|
+
sessionOpts.push(opts ?? {});
|
|
422
|
+
return fn(mockPage);
|
|
423
|
+
});
|
|
424
|
+
const cmd = cli({
|
|
425
|
+
site: 'test-execution',
|
|
426
|
+
name: 'browser-window-options', access: 'read',
|
|
427
|
+
description: 'test browser common options',
|
|
428
|
+
browser: true,
|
|
429
|
+
strategy: Strategy.PUBLIC,
|
|
430
|
+
func: async () => [{ ok: true }],
|
|
431
|
+
});
|
|
432
|
+
await executeCommand(cmd, {}, false, {
|
|
433
|
+
windowMode: 'foreground',
|
|
434
|
+
keepTab: 'true',
|
|
435
|
+
});
|
|
436
|
+
expect(sessionOpts[0]).toMatchObject({ windowMode: 'foreground' });
|
|
437
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
438
|
+
vi.restoreAllMocks();
|
|
439
|
+
});
|
|
440
|
+
it('uses command defaultWindowMode when the user does not pass --window', async () => {
|
|
441
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
442
|
+
const mockPage = { closeWindow };
|
|
443
|
+
const sessionOpts = [];
|
|
444
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
445
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
446
|
+
sessionOpts.push(opts ?? {});
|
|
447
|
+
return fn(mockPage);
|
|
448
|
+
});
|
|
449
|
+
const cmd = cli({
|
|
450
|
+
site: 'test-execution',
|
|
451
|
+
name: 'browser-default-window-mode', access: 'write',
|
|
452
|
+
description: 'test command default window mode',
|
|
453
|
+
browser: true,
|
|
454
|
+
strategy: Strategy.PUBLIC,
|
|
455
|
+
defaultWindowMode: 'foreground',
|
|
456
|
+
func: async () => [{ ok: true }],
|
|
457
|
+
});
|
|
458
|
+
await executeCommand(cmd, {});
|
|
459
|
+
expect(sessionOpts[0]).toMatchObject({ windowMode: 'foreground' });
|
|
460
|
+
vi.restoreAllMocks();
|
|
461
|
+
});
|
|
462
|
+
it('does not re-run custom validation when args are already prepared', async () => {
|
|
463
|
+
const validateArgs = vi.fn();
|
|
464
|
+
const cmd = {
|
|
465
|
+
site: 'test-execution',
|
|
466
|
+
name: 'prepared-validation', access: 'read',
|
|
467
|
+
description: 'test prepared validation path',
|
|
468
|
+
browser: false,
|
|
469
|
+
strategy: Strategy.PUBLIC,
|
|
470
|
+
args: [],
|
|
471
|
+
validateArgs,
|
|
472
|
+
func: async () => [],
|
|
473
|
+
};
|
|
474
|
+
const kwargs = prepareCommandArgs(cmd, {});
|
|
475
|
+
await executeCommand(cmd, kwargs, false, { prepared: true });
|
|
476
|
+
expect(validateArgs).toHaveBeenCalledTimes(1);
|
|
477
|
+
});
|
|
478
|
+
it('exports a profile-scoped trace artifact on browser command failure when requested', async () => {
|
|
479
|
+
const baseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-exec-trace-'));
|
|
480
|
+
const prevConfigDir = process.env.WEBCMD_CONFIG_DIR;
|
|
481
|
+
process.env.WEBCMD_CONFIG_DIR = baseDir;
|
|
482
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
483
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
484
|
+
const mockPage = {
|
|
485
|
+
closeWindow,
|
|
486
|
+
startNetworkCapture: vi.fn().mockResolvedValue(true),
|
|
487
|
+
readNetworkCapture: vi.fn().mockResolvedValue([
|
|
488
|
+
{
|
|
489
|
+
url: 'https://api.example.com/data?token=secret',
|
|
490
|
+
method: 'GET',
|
|
491
|
+
responseStatus: 500,
|
|
492
|
+
responseContentType: 'application/json',
|
|
493
|
+
responsePreview: JSON.stringify({ password: 'secret', ok: false }),
|
|
494
|
+
requestHeaders: { authorization: 'Bearer secret' },
|
|
495
|
+
timestamp: Date.now(),
|
|
496
|
+
},
|
|
497
|
+
]),
|
|
498
|
+
consoleMessages: vi.fn().mockResolvedValue([{ type: 'error', text: 'boom password=secret', timestamp: Date.now() }]),
|
|
499
|
+
snapshot: vi.fn().mockResolvedValue({ html: '<input type="password" value="secret">' }),
|
|
500
|
+
screenshot: vi.fn().mockResolvedValue(Buffer.from('png').toString('base64')),
|
|
501
|
+
getCurrentUrl: vi.fn().mockResolvedValue('https://api.example.com/app'),
|
|
502
|
+
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
503
|
+
};
|
|
504
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
505
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
506
|
+
try {
|
|
507
|
+
const cmd = cli({
|
|
508
|
+
site: 'test-execution',
|
|
509
|
+
name: 'browser-trace-failure', access: 'read',
|
|
510
|
+
description: 'test trace export',
|
|
511
|
+
browser: true,
|
|
512
|
+
strategy: Strategy.PUBLIC,
|
|
513
|
+
func: async () => { throw new Error('adapter failure'); },
|
|
514
|
+
});
|
|
515
|
+
const thrown = await executeCommand(cmd, {}, false, { trace: 'retain-on-failure' }).catch((err) => err);
|
|
516
|
+
expect(thrown).toBeInstanceOf(Error);
|
|
517
|
+
expect(thrown.message).toContain('adapter failure');
|
|
518
|
+
const tracesRoot = path.join(baseDir, 'profiles', 'default', 'traces');
|
|
519
|
+
const traceId = fs.readdirSync(tracesRoot)[0];
|
|
520
|
+
const traceDir = path.join(tracesRoot, traceId);
|
|
521
|
+
expect(fs.existsSync(path.join(traceDir, 'trace.jsonl'))).toBe(true);
|
|
522
|
+
expect(fs.existsSync(path.join(traceDir, 'receipt.json'))).toBe(true);
|
|
523
|
+
const trace = fs.readFileSync(path.join(traceDir, 'trace.jsonl'), 'utf-8');
|
|
524
|
+
expect(trace).toContain('token=[REDACTED]');
|
|
525
|
+
expect(trace).toContain('"authorization":"[REDACTED]"');
|
|
526
|
+
expect(trace).not.toContain('password=secret');
|
|
527
|
+
expect(stderrSpy.mock.calls.flat().join('\n')).not.toContain(['___OPEN', 'CLI_TRACE___'].join(''));
|
|
528
|
+
expect(toEnvelope(thrown).trace).toMatchObject({
|
|
529
|
+
traceId,
|
|
530
|
+
dir: traceDir,
|
|
531
|
+
summaryPath: path.join(traceDir, 'summary.md'),
|
|
532
|
+
receiptPath: path.join(traceDir, 'receipt.json'),
|
|
533
|
+
status: 'failure',
|
|
534
|
+
});
|
|
535
|
+
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
if (prevConfigDir === undefined)
|
|
539
|
+
delete process.env.WEBCMD_CONFIG_DIR;
|
|
540
|
+
else
|
|
541
|
+
process.env.WEBCMD_CONFIG_DIR = prevConfigDir;
|
|
542
|
+
stderrSpy.mockRestore();
|
|
543
|
+
fs.rmSync(baseDir, { recursive: true, force: true });
|
|
544
|
+
vi.restoreAllMocks();
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
it('exports a trace receipt on browser command success when trace is on', async () => {
|
|
548
|
+
const baseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-exec-trace-success-'));
|
|
549
|
+
const prevConfigDir = process.env.WEBCMD_CONFIG_DIR;
|
|
550
|
+
process.env.WEBCMD_CONFIG_DIR = baseDir;
|
|
551
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
552
|
+
const onTraceExport = vi.fn();
|
|
553
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
554
|
+
const mockPage = {
|
|
555
|
+
closeWindow,
|
|
556
|
+
startNetworkCapture: vi.fn().mockResolvedValue(true),
|
|
557
|
+
readNetworkCapture: vi.fn().mockResolvedValue([]),
|
|
558
|
+
consoleMessages: vi.fn().mockResolvedValue([]),
|
|
559
|
+
snapshot: vi.fn().mockResolvedValue('snapshot'),
|
|
560
|
+
screenshot: vi.fn().mockResolvedValue(Buffer.from('png').toString('base64')),
|
|
561
|
+
getCurrentUrl: vi.fn().mockResolvedValue('https://example.com'),
|
|
562
|
+
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
563
|
+
};
|
|
564
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
565
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
566
|
+
try {
|
|
567
|
+
const cmd = cli({
|
|
568
|
+
site: 'test-execution',
|
|
569
|
+
name: 'browser-trace-success', access: 'read',
|
|
570
|
+
description: 'test trace export on success',
|
|
571
|
+
browser: true,
|
|
572
|
+
strategy: Strategy.PUBLIC,
|
|
573
|
+
func: async () => [{ ok: true }],
|
|
574
|
+
});
|
|
575
|
+
await expect(executeCommand(cmd, {}, false, { trace: 'on', onTraceExport })).resolves.toEqual([{ ok: true }]);
|
|
576
|
+
const stderr = stderrSpy.mock.calls.flat().join('\n');
|
|
577
|
+
expect(stderr).toContain('Webcmd trace artifact:');
|
|
578
|
+
const tracesRoot = path.join(baseDir, 'profiles', 'default', 'traces');
|
|
579
|
+
const traceId = fs.readdirSync(tracesRoot)[0];
|
|
580
|
+
const receipt = JSON.parse(fs.readFileSync(path.join(tracesRoot, traceId, 'receipt.json'), 'utf-8'));
|
|
581
|
+
expect(receipt.status).toBe('success');
|
|
582
|
+
expect(receipt.traceDir).toContain(path.join(baseDir, 'profiles', 'default', 'traces'));
|
|
583
|
+
expect(receipt.scope).toMatchObject({
|
|
584
|
+
site: 'test-execution',
|
|
585
|
+
command: 'test-execution/browser-trace-success',
|
|
586
|
+
});
|
|
587
|
+
expect(receipt.error).toBeUndefined();
|
|
588
|
+
expect(onTraceExport).toHaveBeenCalledWith(expect.objectContaining({
|
|
589
|
+
traceId,
|
|
590
|
+
receipt: expect.objectContaining({ status: 'success' }),
|
|
591
|
+
}));
|
|
592
|
+
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
593
|
+
}
|
|
594
|
+
finally {
|
|
595
|
+
if (prevConfigDir === undefined)
|
|
596
|
+
delete process.env.WEBCMD_CONFIG_DIR;
|
|
597
|
+
else
|
|
598
|
+
process.env.WEBCMD_CONFIG_DIR = prevConfigDir;
|
|
599
|
+
stderrSpy.mockRestore();
|
|
600
|
+
fs.rmSync(baseDir, { recursive: true, force: true });
|
|
601
|
+
vi.restoreAllMocks();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
it('keeps the original adapter error when trace export fails', async () => {
|
|
605
|
+
const baseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-exec-trace-fail-'));
|
|
606
|
+
const blockedPath = path.join(baseDir, 'not-a-dir');
|
|
607
|
+
fs.writeFileSync(blockedPath, 'file');
|
|
608
|
+
const prevConfigDir = process.env.WEBCMD_CONFIG_DIR;
|
|
609
|
+
process.env.WEBCMD_CONFIG_DIR = blockedPath;
|
|
610
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
611
|
+
const mockPage = {
|
|
612
|
+
closeWindow: vi.fn().mockResolvedValue(undefined),
|
|
613
|
+
startNetworkCapture: vi.fn().mockResolvedValue(true),
|
|
614
|
+
readNetworkCapture: vi.fn().mockResolvedValue([]),
|
|
615
|
+
consoleMessages: vi.fn().mockResolvedValue([]),
|
|
616
|
+
snapshot: vi.fn().mockResolvedValue('snapshot'),
|
|
617
|
+
screenshot: vi.fn().mockResolvedValue(Buffer.from('png').toString('base64')),
|
|
618
|
+
getCurrentUrl: vi.fn().mockResolvedValue('https://example.com'),
|
|
619
|
+
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
620
|
+
};
|
|
621
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
622
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
623
|
+
try {
|
|
624
|
+
const cmd = cli({
|
|
625
|
+
site: 'test-execution',
|
|
626
|
+
name: 'browser-trace-export-fails', access: 'read',
|
|
627
|
+
description: 'test trace export failure handling',
|
|
628
|
+
browser: true,
|
|
629
|
+
strategy: Strategy.PUBLIC,
|
|
630
|
+
func: async () => { throw new Error('adapter failure'); },
|
|
631
|
+
});
|
|
632
|
+
await expect(executeCommand(cmd, {}, false, { trace: 'retain-on-failure' })).rejects.toThrow('adapter failure');
|
|
633
|
+
expect(stderrSpy.mock.calls.flat().join('\n')).toContain('[trace] Failed to export trace artifact');
|
|
634
|
+
}
|
|
635
|
+
finally {
|
|
636
|
+
if (prevConfigDir === undefined)
|
|
637
|
+
delete process.env.WEBCMD_CONFIG_DIR;
|
|
638
|
+
else
|
|
639
|
+
process.env.WEBCMD_CONFIG_DIR = prevConfigDir;
|
|
640
|
+
stderrSpy.mockRestore();
|
|
641
|
+
fs.rmSync(baseDir, { recursive: true, force: true });
|
|
642
|
+
vi.restoreAllMocks();
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
});
|