@testdriverai/agent 7.8.0-test.38
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/.claude/settings.local.json +7 -0
- package/.env.example +4 -0
- package/.prettierignore +4 -0
- package/.prettierrc +1 -0
- package/CHANGELOG.md +953 -0
- package/README.md +81 -0
- package/agent/events.js +135 -0
- package/agent/index.js +2450 -0
- package/agent/interface.js +35 -0
- package/agent/lib/analytics.js +22 -0
- package/agent/lib/censorship.js +75 -0
- package/agent/lib/commander.js +246 -0
- package/agent/lib/commands.js +1684 -0
- package/agent/lib/config.js +60 -0
- package/agent/lib/generator.js +91 -0
- package/agent/lib/http.js +144 -0
- package/agent/lib/logger.js +56 -0
- package/agent/lib/outputs.js +29 -0
- package/agent/lib/parser.js +209 -0
- package/agent/lib/redraw.js +386 -0
- package/agent/lib/resources/cursor-2.png +0 -0
- package/agent/lib/sandbox.js +1104 -0
- package/agent/lib/sdk.js +633 -0
- package/agent/lib/session.js +25 -0
- package/agent/lib/source-mapper.js +342 -0
- package/agent/lib/subimage/index.js +77 -0
- package/agent/lib/subimage/opencv.js +69 -0
- package/agent/lib/system.js +204 -0
- package/agent/lib/theme.js +14 -0
- package/agent/lib/valid-version.js +21 -0
- package/agent/lib/validation.js +169 -0
- package/ai/.claude-plugin/plugin.json +9 -0
- package/ai/agents/testdriver.md +638 -0
- package/ai/skills/testdriver-ai/SKILL.md +204 -0
- package/ai/skills/testdriver-assert/SKILL.md +315 -0
- package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
- package/ai/skills/testdriver-cache/SKILL.md +221 -0
- package/ai/skills/testdriver-caching/SKILL.md +124 -0
- package/ai/skills/testdriver-captcha/SKILL.md +158 -0
- package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
- package/ai/skills/testdriver-click/SKILL.md +286 -0
- package/ai/skills/testdriver-client/SKILL.md +477 -0
- package/ai/skills/testdriver-cloud/SKILL.md +119 -0
- package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
- package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
- package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
- package/ai/skills/testdriver-device-config/SKILL.md +317 -0
- package/ai/skills/testdriver-double-click/SKILL.md +102 -0
- package/ai/skills/testdriver-elements/SKILL.md +605 -0
- package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
- package/ai/skills/testdriver-errors/SKILL.md +246 -0
- package/ai/skills/testdriver-events/SKILL.md +356 -0
- package/ai/skills/testdriver-examples/SKILL.md +7 -0
- package/ai/skills/testdriver-exec/SKILL.md +317 -0
- package/ai/skills/testdriver-find/SKILL.md +829 -0
- package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
- package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
- package/ai/skills/testdriver-hover/SKILL.md +278 -0
- package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
- package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
- package/ai/skills/testdriver-mcp/SKILL.md +7 -0
- package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
- package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
- package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
- package/ai/skills/testdriver-parse/SKILL.md +236 -0
- package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
- package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
- package/ai/skills/testdriver-provision/SKILL.md +331 -0
- package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
- package/ai/skills/testdriver-redraw/SKILL.md +214 -0
- package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
- package/ai/skills/testdriver-right-click/SKILL.md +123 -0
- package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
- package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
- package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
- package/ai/skills/testdriver-scroll/SKILL.md +335 -0
- package/ai/skills/testdriver-secrets/SKILL.md +115 -0
- package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
- package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
- package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
- package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
- package/ai/skills/testdriver-type/SKILL.md +357 -0
- package/ai/skills/testdriver-variables/SKILL.md +111 -0
- package/ai/skills/testdriver-wait/SKILL.md +50 -0
- package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
- package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
- package/bin/testdriverai.js +22 -0
- package/debugger/bg.png +0 -0
- package/debugger/icon.png +0 -0
- package/debugger/index.html +469 -0
- package/debugger/td.png +0 -0
- package/debugger/tray-buffered.png +0 -0
- package/debugger/tray.png +0 -0
- package/docs/GITHUB_COMMENTS.md +330 -0
- package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
- package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
- package/docs/TEST-GITHUB-COMMENTS.md +129 -0
- package/docs/_data/examples-manifest.json +177 -0
- package/docs/_data/examples-manifest.schema.json +41 -0
- package/docs/_scripts/extract-example-urls.js +165 -0
- package/docs/_scripts/generate-examples.js +560 -0
- package/docs/_scripts/generate-skills.js +154 -0
- package/docs/_scripts/link-replacer.js +164 -0
- package/docs/_scripts/upload-docs-to-openai.js +284 -0
- package/docs/changelog.mdx +161 -0
- package/docs/claude-mcp-plugin.mdx +160 -0
- package/docs/docs.json +442 -0
- package/docs/github-integration-setup.md +266 -0
- package/docs/guide/best-practices-polling.mdx +174 -0
- package/docs/images/content/account/newprojectsettings.png +0 -0
- package/docs/images/content/account/projectpage.png +0 -0
- package/docs/images/content/account/projectreplays.png +0 -0
- package/docs/images/content/account/team-manage.png +0 -0
- package/docs/images/content/account/teampage.png +0 -0
- package/docs/images/content/extension/cursor.svg +1 -0
- package/docs/images/content/extension/vscode.svg +57 -0
- package/docs/images/content/extension/windsurf.svg +3 -0
- package/docs/images/content/parse/output.png +0 -0
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/side-by-side.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/docs/images/content/vscode/v7-chat.png +0 -0
- package/docs/images/content/vscode/v7-choose-agent.png +0 -0
- package/docs/images/content/vscode/v7-full.png +0 -0
- package/docs/images/content/vscode/v7-onboarding.png +0 -0
- package/docs/images/content/vscode/vscode-2-assert.png +0 -0
- package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
- package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
- package/docs/images/content/vscode/vscode-file-creation.png +0 -0
- package/docs/images/content/vscode/vscode-install.png +0 -0
- package/docs/images/content/vscode/vscode-overview.png +0 -0
- package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
- package/docs/images/content/vscode/vscode-stopchat.png +0 -0
- package/docs/images/content/vscode/vscode-stoptest.png +0 -0
- package/docs/images/content/vscode/vscode-tdservice.png +0 -0
- package/docs/images/content/vscode/vscode-test-output.png +0 -0
- package/docs/images/content/vscode/vscode-testhistory.png +0 -0
- package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
- package/docs/images/content/vscode/vscode-testpane.png +0 -0
- package/docs/images/template/dark.png +0 -0
- package/docs/images/template/icon.png +0 -0
- package/docs/images/template/light.png +0 -0
- package/docs/snippets/calendar-link.mdx +4 -0
- package/docs/snippets/gitignore-warning.mdx +7 -0
- package/docs/snippets/lifecycle-warning.mdx +6 -0
- package/docs/snippets/test-prereqs.mdx +12 -0
- package/docs/snippets/tests/assert-replay.mdx +7 -0
- package/docs/snippets/tests/assert-yaml.mdx +8 -0
- package/docs/snippets/tests/exec-js-replay.mdx +7 -0
- package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
- package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
- package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
- package/docs/snippets/tests/hover-image-replay.mdx +7 -0
- package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
- package/docs/snippets/tests/hover-text-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
- package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
- package/docs/snippets/tests/match-image-replay.mdx +7 -0
- package/docs/snippets/tests/match-image-yaml.mdx +17 -0
- package/docs/snippets/tests/press-keys-replay.mdx +7 -0
- package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
- package/docs/snippets/tests/remember-replay.mdx +7 -0
- package/docs/snippets/tests/remember-yaml.mdx +28 -0
- package/docs/snippets/tests/scroll-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
- package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
- package/docs/snippets/tests/scroll-yaml.mdx +30 -0
- package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
- package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
- package/docs/snippets/tests/type-replay.mdx +7 -0
- package/docs/snippets/tests/type-yaml.mdx +28 -0
- package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-replay.mdx +7 -0
- package/docs/snippets/tests/wait-yaml.mdx +13 -0
- package/docs/styles.css +65 -0
- package/docs/v6/account/dashboard.mdx +16 -0
- package/docs/v6/account/enterprise.mdx +110 -0
- package/docs/v6/account/pricing.mdx +33 -0
- package/docs/v6/account/projects.mdx +33 -0
- package/docs/v6/account/team.mdx +35 -0
- package/docs/v6/action/ami.mdx +109 -0
- package/docs/v6/action/performance.mdx +105 -0
- package/docs/v6/action/secrets.mdx +93 -0
- package/docs/v6/apps/chrome-extensions.mdx +48 -0
- package/docs/v6/apps/desktop-apps.mdx +93 -0
- package/docs/v6/apps/mobile-apps.mdx +26 -0
- package/docs/v6/apps/static-websites.mdx +54 -0
- package/docs/v6/apps/tauri-apps.mdx +361 -0
- package/docs/v6/bugs/jira.mdx +232 -0
- package/docs/v6/cli/overview.mdx +66 -0
- package/docs/v6/commands/assert.mdx +45 -0
- package/docs/v6/commands/exec.mdx +276 -0
- package/docs/v6/commands/focus-application.mdx +44 -0
- package/docs/v6/commands/hover-image.mdx +69 -0
- package/docs/v6/commands/hover-text.mdx +47 -0
- package/docs/v6/commands/if.mdx +53 -0
- package/docs/v6/commands/match-image.mdx +67 -0
- package/docs/v6/commands/press-keys.mdx +87 -0
- package/docs/v6/commands/remember.mdx +49 -0
- package/docs/v6/commands/run.mdx +44 -0
- package/docs/v6/commands/scroll-until-image.mdx +66 -0
- package/docs/v6/commands/scroll-until-text.mdx +60 -0
- package/docs/v6/commands/scroll.mdx +69 -0
- package/docs/v6/commands/type.mdx +45 -0
- package/docs/v6/commands/wait-for-image.mdx +54 -0
- package/docs/v6/commands/wait-for-text.mdx +48 -0
- package/docs/v6/commands/wait.mdx +45 -0
- package/docs/v6/exporting/junit.mdx +218 -0
- package/docs/v6/exporting/playwright.mdx +197 -0
- package/docs/v6/features/auto-healing.mdx +144 -0
- package/docs/v6/features/generation.mdx +116 -0
- package/docs/v6/features/parallel-testing.mdx +151 -0
- package/docs/v6/features/reusable-snippets.mdx +131 -0
- package/docs/v6/features/selectorless.mdx +80 -0
- package/docs/v6/features/visual-assertions.mdx +139 -0
- package/docs/v6/getting-started/ci.mdx +146 -0
- package/docs/v6/getting-started/cli.mdx +91 -0
- package/docs/v6/getting-started/editing.mdx +100 -0
- package/docs/v6/getting-started/playwright.mdx +342 -0
- package/docs/v6/getting-started/running.mdx +48 -0
- package/docs/v6/getting-started/self-hosting.mdx +408 -0
- package/docs/v6/getting-started/vscode.mdx +88 -0
- package/docs/v6/guide/assertions.mdx +189 -0
- package/docs/v6/guide/authentication.mdx +136 -0
- package/docs/v6/guide/code.mdx +65 -0
- package/docs/v6/guide/dashcam.mdx +118 -0
- package/docs/v6/guide/environment-variables.mdx +26 -0
- package/docs/v6/guide/lifecycle.mdx +242 -0
- package/docs/v6/guide/locating.mdx +141 -0
- package/docs/v6/guide/protips.mdx +43 -0
- package/docs/v6/guide/variables.mdx +143 -0
- package/docs/v6/guide/waiting.mdx +130 -0
- package/docs/v6/importing/csv.mdx +196 -0
- package/docs/v6/importing/gherkin.mdx +143 -0
- package/docs/v6/importing/jira.mdx +164 -0
- package/docs/v6/importing/testrail.mdx +162 -0
- package/docs/v6/integrations/electron.mdx +146 -0
- package/docs/v6/integrations/netlify.mdx +100 -0
- package/docs/v6/integrations/vercel.mdx +125 -0
- package/docs/v6/interactive/explore.mdx +99 -0
- package/docs/v6/interactive/run.mdx +52 -0
- package/docs/v6/interactive/save.mdx +63 -0
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v6/overview/faq.mdx +162 -0
- package/docs/v6/overview/performance.mdx +52 -0
- package/docs/v6/overview/quickstart.mdx +137 -0
- package/docs/v6/overview/what-is-testdriver.mdx +85 -0
- package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
- package/docs/v6/scenarios/cookie-banner.mdx +32 -0
- package/docs/v6/scenarios/file-upload.mdx +33 -0
- package/docs/v6/scenarios/form-filling.mdx +32 -0
- package/docs/v6/scenarios/log-in.mdx +75 -0
- package/docs/v6/scenarios/pdf-generation.mdx +25 -0
- package/docs/v6/scenarios/spell-check.mdx +22 -0
- package/docs/v6/security/action.mdx +84 -0
- package/docs/v6/security/agent.mdx +73 -0
- package/docs/v6/security/platform.mdx +77 -0
- package/docs/v6/tutorials/advanced-test.mdx +81 -0
- package/docs/v6/tutorials/basic-test.mdx +45 -0
- package/docs/v7/_drafts/agents.mdx +843 -0
- package/docs/v7/_drafts/architecture.mdx +399 -0
- package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
- package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
- package/docs/v7/_drafts/best-practices.mdx +486 -0
- package/docs/v7/_drafts/caching-ai.mdx +215 -0
- package/docs/v7/_drafts/caching-selectors.mdx +424 -0
- package/docs/v7/_drafts/caching.mdx +366 -0
- package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
- package/docs/v7/_drafts/commands/assert.mdx +45 -0
- package/docs/v7/_drafts/commands/exec.mdx +276 -0
- package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
- package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
- package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
- package/docs/v7/_drafts/commands/if.mdx +53 -0
- package/docs/v7/_drafts/commands/match-image.mdx +67 -0
- package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
- package/docs/v7/_drafts/commands/remember.mdx +49 -0
- package/docs/v7/_drafts/commands/run.mdx +44 -0
- package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/_drafts/commands/scroll.mdx +69 -0
- package/docs/v7/_drafts/commands/type.mdx +45 -0
- package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
- package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
- package/docs/v7/_drafts/commands/wait.mdx +45 -0
- package/docs/v7/_drafts/configuration.mdx +378 -0
- package/docs/v7/_drafts/contributing.mdx +174 -0
- package/docs/v7/_drafts/core.mdx +458 -0
- package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
- package/docs/v7/_drafts/debugging.mdx +349 -0
- package/docs/v7/_drafts/error-handling.mdx +501 -0
- package/docs/v7/_drafts/faq.mdx +393 -0
- package/docs/v7/_drafts/hooks.mdx +360 -0
- package/docs/v7/_drafts/init-command.mdx +95 -0
- package/docs/v7/_drafts/installation.mdx +420 -0
- package/docs/v7/_drafts/migration.mdx +562 -0
- package/docs/v7/_drafts/observable.mdx +604 -0
- package/docs/v7/_drafts/playwright.mdx +342 -0
- package/docs/v7/_drafts/plugin-migration.mdx +220 -0
- package/docs/v7/_drafts/powerful.mdx +419 -0
- package/docs/v7/_drafts/presets.mdx +210 -0
- package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
- package/docs/v7/_drafts/prompt-cache.mdx +200 -0
- package/docs/v7/_drafts/provision.mdx +390 -0
- package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
- package/docs/v7/_drafts/readme.mdx +135 -0
- package/docs/v7/_drafts/reports.mdx +414 -0
- package/docs/v7/_drafts/scalable.mdx +763 -0
- package/docs/v7/_drafts/screenshot.mdx +155 -0
- package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
- package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
- package/docs/v7/_drafts/sdk-migration.mdx +474 -0
- package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
- package/docs/v7/_drafts/self-hosting.mdx +369 -0
- package/docs/v7/_drafts/test-recording.mdx +382 -0
- package/docs/v7/_drafts/troubleshooting.mdx +526 -0
- package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
- package/docs/v7/_drafts/vitest.mdx +535 -0
- package/docs/v7/_drafts/writing-tests.mdx +25 -0
- package/docs/v7/ai.mdx +205 -0
- package/docs/v7/assert.mdx +316 -0
- package/docs/v7/aws-setup.mdx +449 -0
- package/docs/v7/cache.mdx +223 -0
- package/docs/v7/caching.mdx +128 -0
- package/docs/v7/captcha.mdx +159 -0
- package/docs/v7/ci-cd.mdx +603 -0
- package/docs/v7/click.mdx +287 -0
- package/docs/v7/client.mdx +478 -0
- package/docs/v7/copilot/auto-healing.mdx +265 -0
- package/docs/v7/copilot/creating-tests.mdx +156 -0
- package/docs/v7/copilot/github.mdx +143 -0
- package/docs/v7/copilot/running-tests.mdx +149 -0
- package/docs/v7/copilot/setup.mdx +143 -0
- package/docs/v7/customizing-devices.mdx +319 -0
- package/docs/v7/dashcam.mdx +419 -0
- package/docs/v7/debugging-with-screenshots.mdx +402 -0
- package/docs/v7/device-config.mdx +317 -0
- package/docs/v7/double-click.mdx +102 -0
- package/docs/v7/elements.mdx +606 -0
- package/docs/v7/enterprise.mdx +9 -0
- package/docs/v7/errors.mdx +248 -0
- package/docs/v7/events.mdx +358 -0
- package/docs/v7/examples/ai.mdx +72 -0
- package/docs/v7/examples/assert.mdx +72 -0
- package/docs/v7/examples/captcha-api.mdx +92 -0
- package/docs/v7/examples/chrome-extension.mdx +132 -0
- package/docs/v7/examples/drag-and-drop.mdx +100 -0
- package/docs/v7/examples/element-not-found.mdx +67 -0
- package/docs/v7/examples/exec-output.mdx +85 -0
- package/docs/v7/examples/exec-pwsh.mdx +83 -0
- package/docs/v7/examples/focus-window.mdx +62 -0
- package/docs/v7/examples/hover-image.mdx +94 -0
- package/docs/v7/examples/hover-text.mdx +69 -0
- package/docs/v7/examples/installer.mdx +91 -0
- package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
- package/docs/v7/examples/match-image.mdx +96 -0
- package/docs/v7/examples/press-keys.mdx +92 -0
- package/docs/v7/examples/scroll-keyboard.mdx +79 -0
- package/docs/v7/examples/scroll-until-image.mdx +81 -0
- package/docs/v7/examples/scroll-until-text.mdx +109 -0
- package/docs/v7/examples/scroll.mdx +81 -0
- package/docs/v7/examples/type.mdx +92 -0
- package/docs/v7/examples/windows-installer.mdx +89 -0
- package/docs/v7/exec.mdx +318 -0
- package/docs/v7/find.mdx +830 -0
- package/docs/v7/focus-application.mdx +294 -0
- package/docs/v7/generating-tests.mdx +36 -0
- package/docs/v7/hosted.mdx +158 -0
- package/docs/v7/hover.mdx +279 -0
- package/docs/v7/locating-elements.mdx +71 -0
- package/docs/v7/making-assertions.mdx +32 -0
- package/docs/v7/mcp.mdx +9 -0
- package/docs/v7/mouse-down.mdx +161 -0
- package/docs/v7/mouse-up.mdx +164 -0
- package/docs/v7/parse.mdx +237 -0
- package/docs/v7/performing-actions.mdx +54 -0
- package/docs/v7/press-keys.mdx +349 -0
- package/docs/v7/provision.mdx +333 -0
- package/docs/v7/quickstart.mdx +173 -0
- package/docs/v7/redraw.mdx +216 -0
- package/docs/v7/reusable-code.mdx +249 -0
- package/docs/v7/right-click.mdx +123 -0
- package/docs/v7/running-tests.mdx +185 -0
- package/docs/v7/screenshot.mdx +249 -0
- package/docs/v7/screenshots.mdx +186 -0
- package/docs/v7/scroll.mdx +336 -0
- package/docs/v7/secrets.mdx +115 -0
- package/docs/v7/self-hosted.mdx +149 -0
- package/docs/v7/type.mdx +358 -0
- package/docs/v7/variables.mdx +111 -0
- package/docs/v7/wait.mdx +52 -0
- package/docs/v7/waiting-for-elements.mdx +90 -0
- package/docs/v7/what-is-testdriver.mdx +54 -0
- package/eslint.config.js +67 -0
- package/examples/ai.test.mjs +31 -0
- package/examples/assert.test.mjs +47 -0
- package/examples/chrome-extension.test.mjs +97 -0
- package/examples/config.mjs +5 -0
- package/examples/element-not-found.test.mjs +27 -0
- package/examples/exec-output.test.mjs +60 -0
- package/examples/exec-pwsh.test.mjs +58 -0
- package/examples/findall-coffee-icons.test.mjs +42 -0
- package/examples/focus-window.test.mjs +37 -0
- package/examples/formatted-logging.test.mjs +27 -0
- package/examples/hover-image.test.mjs +53 -0
- package/examples/hover-text-with-description.test.mjs +57 -0
- package/examples/hover-text.test.mjs +28 -0
- package/examples/installer.test.mjs +50 -0
- package/examples/launch-vscode-linux.test.mjs +55 -0
- package/examples/match-image.test.mjs +55 -0
- package/examples/parse.test.mjs +19 -0
- package/examples/press-keys.test.mjs +44 -0
- package/examples/prompt.test.mjs +34 -0
- package/examples/scroll-keyboard.test.mjs +38 -0
- package/examples/scroll-until-image.test.mjs +40 -0
- package/examples/scroll.test.mjs +42 -0
- package/examples/type.test.mjs +46 -0
- package/examples/windows-installer.test.mjs +54 -0
- package/index.js +2 -0
- package/interfaces/cli/commands/init.js +438 -0
- package/interfaces/cli/commands/setup.js +382 -0
- package/interfaces/cli/lib/base.js +285 -0
- package/interfaces/cli.js +20 -0
- package/interfaces/junit-reporter.js +290 -0
- package/interfaces/logger.js +388 -0
- package/interfaces/readline.js +234 -0
- package/interfaces/shared-test-state.mjs +64 -0
- package/interfaces/vitest-plugin.d.ts +115 -0
- package/interfaces/vitest-plugin.mjs +1698 -0
- package/lib/captcha/solver.js +358 -0
- package/lib/core/Dashcam.js +533 -0
- package/lib/core/index.d.ts +172 -0
- package/lib/core/index.js +12 -0
- package/lib/environments.json +18 -0
- package/lib/github-comment-formatter.js +263 -0
- package/lib/github-comment.mjs +452 -0
- package/lib/init-project.js +575 -0
- package/lib/presets/index.mjs +331 -0
- package/lib/resolve-channel.js +46 -0
- package/lib/sentry.js +417 -0
- package/lib/vitest/hooks.d.ts +57 -0
- package/lib/vitest/hooks.mjs +674 -0
- package/lib/vitest/setup-aws.mjs +247 -0
- package/lib/vitest/setup-self-hosted.mjs +151 -0
- package/lib/vitest/setup.mjs +46 -0
- package/manual/captcha-api.test.mjs +51 -0
- package/manual/drag-and-drop.test.mjs +59 -0
- package/manual/flake-diffthreshold-001.test.mjs +9 -0
- package/manual/flake-diffthreshold-01.test.mjs +9 -0
- package/manual/flake-diffthreshold-05.test.mjs +9 -0
- package/manual/flake-noredraw-cache.test.mjs +9 -0
- package/manual/flake-noredraw-nocache.test.mjs +9 -0
- package/manual/flake-redraw-cache.test.mjs +9 -0
- package/manual/flake-redraw-nocache.test.mjs +9 -0
- package/manual/flake-rocket-match.test.mjs +30 -0
- package/manual/flake-shared.mjs +51 -0
- package/manual/no-provision.test.mjs +31 -0
- package/manual/packer-hover-image.test.mjs +176 -0
- package/manual/scroll-until-text.test.mjs +68 -0
- package/manual/test-init-command.js +223 -0
- package/mcp-server/README.md +322 -0
- package/mcp-server/dist/codegen.d.ts +9 -0
- package/mcp-server/dist/codegen.js +165 -0
- package/mcp-server/dist/mcp-app.html +114 -0
- package/mcp-server/dist/package.json +1 -0
- package/mcp-server/dist/provision-types.d.ts +290 -0
- package/mcp-server/dist/provision-types.js +174 -0
- package/mcp-server/dist/server.d.ts +6 -0
- package/mcp-server/dist/server.mjs +1925 -0
- package/mcp-server/dist/session.d.ts +85 -0
- package/mcp-server/dist/session.js +152 -0
- package/mcp-server/mcp-app.html +28 -0
- package/mcp-server/mcp-config.example.json +19 -0
- package/mcp-server/package-lock.json +4027 -0
- package/mcp-server/package.json +31 -0
- package/mcp-server/src/codegen.ts +189 -0
- package/mcp-server/src/mcp-app.css +360 -0
- package/mcp-server/src/mcp-app.ts +547 -0
- package/mcp-server/src/provision-types.ts +209 -0
- package/mcp-server/src/server.ts +2391 -0
- package/mcp-server/src/session.ts +194 -0
- package/mcp-server/tsconfig.json +16 -0
- package/mcp-server/vite.config.ts +23 -0
- package/package.json +158 -0
- package/schema.json +1046 -0
- package/scripts/generate-skills.js +94 -0
- package/sdk-log-formatter.js +1157 -0
- package/sdk.d.ts +1486 -0
- package/sdk.js +4336 -0
- package/setup/aws/cloudformation.yaml +463 -0
- package/setup/aws/disable-defender.sh +42 -0
- package/setup/aws/install-dev-runner.sh +79 -0
- package/setup/aws/spawn-runner.sh +289 -0
- package/test/captcha-solver.test.mjs +152 -0
- package/test/chrome-remote-debugging.test.mjs +66 -0
- package/test/duckduckgo/experiment.test.mjs +28 -0
- package/test/duckduckgo/setup.test.mjs +29 -0
- package/test/manual/debug-locate-response.js +82 -0
- package/test/manual/reconnect-provision.test.mjs +49 -0
- package/test/manual/test-console-logs.test.mjs +42 -0
- package/test/manual/test-find-api.js +73 -0
- package/test/manual/test-init.sh +54 -0
- package/test/manual/test-prompt-cache.js +97 -0
- package/test/manual/test-provision-auth.mjs +22 -0
- package/test/manual/test-sandbox-render.js +29 -0
- package/test/manual/test-sdk-methods.js +15 -0
- package/test/manual/test-sdk-refactor.js +53 -0
- package/test/manual/test-stack-trace.mjs +57 -0
- package/test/manual/verify-element-api.js +89 -0
- package/test/manual/verify-types.js +0 -0
- package/test/manual-unawaited-promise.test.mjs +31 -0
- package/vitest.config.mjs +58 -0
- package/vitest.runner.config.mjs +33 -0
- package/vscode-extension/.vscodeignore +12 -0
- package/vscode-extension/README.md +94 -0
- package/vscode-extension/media/icon.png +0 -0
- package/vscode-extension/package-lock.json +4126 -0
- package/vscode-extension/package.json +86 -0
- package/vscode-extension/src/extension.ts +829 -0
- package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
- package/vscode-extension/tsconfig.json +16 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashcam Class
|
|
3
|
+
* Manages Dashcam CLI recording lifecycle
|
|
4
|
+
*
|
|
5
|
+
* Provides a clean interface for:
|
|
6
|
+
* - Authentication
|
|
7
|
+
* - Log tracking
|
|
8
|
+
* - Starting/stopping recordings
|
|
9
|
+
* - Retrieving replay URLs
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { logger } = require("../../interfaces/logger");
|
|
13
|
+
|
|
14
|
+
class Dashcam {
|
|
15
|
+
/**
|
|
16
|
+
* Create a Dashcam instance
|
|
17
|
+
* @param {Object} client - TestDriver client instance
|
|
18
|
+
* @param {Object} options - Configuration options
|
|
19
|
+
* @param {string} [options.apiKey] - Dashcam API key
|
|
20
|
+
* @param {boolean} [options.autoStart=false] - Auto-start recording
|
|
21
|
+
* @param {Array} [options.logs=[]] - Log configurations to add
|
|
22
|
+
* @param {string} [options.title] - Recording title (defaults to generated title)
|
|
23
|
+
*/
|
|
24
|
+
constructor(client, options = {}) {
|
|
25
|
+
if (!client) {
|
|
26
|
+
throw new Error("Dashcam requires a TestDriver client instance");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.client = client;
|
|
30
|
+
// Use provided apiKey, or client's apiKey, or fallback to a default
|
|
31
|
+
this.apiKey =
|
|
32
|
+
options.apiKey ||
|
|
33
|
+
client.apiKey ||
|
|
34
|
+
client.config?.TD_API_KEY ||
|
|
35
|
+
"4e93d8bf-3886-4d26-a144-116c4063522d";
|
|
36
|
+
this.autoStart = options.autoStart ?? false;
|
|
37
|
+
this.logs = options.logs || [];
|
|
38
|
+
this.recording = false;
|
|
39
|
+
this._authenticated = false;
|
|
40
|
+
this.startTime = null; // Track when dashcam recording started
|
|
41
|
+
this.title = options.title || this._generateDefaultTitle();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate a default title for the recording
|
|
46
|
+
* Uses test context if available, otherwise falls back to timestamp
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
_generateDefaultTitle() {
|
|
50
|
+
// Check for Vitest context
|
|
51
|
+
if (this.client.__vitestContext) {
|
|
52
|
+
const task = this.client.__vitestContext;
|
|
53
|
+
const testName = task.name || "Test";
|
|
54
|
+
const fileName = task.file?.name || task.file?.filepath;
|
|
55
|
+
if (fileName) {
|
|
56
|
+
const baseName = fileName
|
|
57
|
+
.split("/")
|
|
58
|
+
.pop()
|
|
59
|
+
.replace(/\.(test|spec)\.(js|mjs|ts|tsx)$/, "");
|
|
60
|
+
return `${baseName} - ${testName}`;
|
|
61
|
+
}
|
|
62
|
+
return testName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fallback to timestamp
|
|
66
|
+
const now = new Date();
|
|
67
|
+
return `Recording ${now.toISOString().replace(/T/, " ").replace(/\..+/, "")}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get shell type based on client OS
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
_getShell() {
|
|
75
|
+
return this.client.os === "windows" ? "pwsh" : "sh";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get TD_API_ROOT from client config
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
_getApiRoot() {
|
|
83
|
+
const channelConfig = require("../../lib/resolve-channel.js");
|
|
84
|
+
return (
|
|
85
|
+
this.client.config?.TD_API_ROOT || channelConfig.channels[channelConfig.active]
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get console URL based on API root
|
|
91
|
+
* Maps API endpoints to their corresponding web console URLs
|
|
92
|
+
* @param {string} apiRoot - The API root URL
|
|
93
|
+
* @returns {string} The corresponding console URL
|
|
94
|
+
*/
|
|
95
|
+
static getConsoleUrl(apiRoot = (() => { const c = require("../../lib/resolve-channel.js"); return c.channels[c.active]; })()) {
|
|
96
|
+
// Allow explicit override via env (e.g. VITE_DOMAIN from .env)
|
|
97
|
+
if (process.env.VITE_DOMAIN) return process.env.VITE_DOMAIN;
|
|
98
|
+
|
|
99
|
+
if (!apiRoot) return "https://console.testdriver.ai";
|
|
100
|
+
|
|
101
|
+
// Dash-separated environments: api-{env}.testdriver.ai -> console-{env}.testdriver.ai
|
|
102
|
+
const envMatch = apiRoot.match(/^https:\/\/api-(test|canary)\.testdriver\.ai/);
|
|
103
|
+
if (envMatch) {
|
|
104
|
+
return `https://console-${envMatch[1]}.testdriver.ai`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Production: API on custom domain or v6 -> Console on testdriver.ai
|
|
108
|
+
if (
|
|
109
|
+
apiRoot.includes("api.testdriver.ai") ||
|
|
110
|
+
apiRoot.includes("v6.testdriver.ai")
|
|
111
|
+
) {
|
|
112
|
+
return "https://console.testdriver.ai";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Local development via ngrok -> localhost web app
|
|
116
|
+
if (apiRoot.includes("ngrok.io")) {
|
|
117
|
+
return "http://localhost:3001";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Fly.io PR previews: map API app to Web app
|
|
121
|
+
// pr-123-api.fly.dev -> pr-123-web.fly.dev
|
|
122
|
+
const flyPrMatch = apiRoot.match(/https:\/\/(pr-\d+)-api\.fly\.dev/);
|
|
123
|
+
if (flyPrMatch) {
|
|
124
|
+
const [, prPrefix] = flyPrMatch;
|
|
125
|
+
return `https://${prPrefix}-web.fly.dev`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Fly.io environment apps: test-api.fly.dev -> test-web.fly.dev
|
|
129
|
+
const flyEnvMatch = apiRoot.match(/https:\/\/([\w-]+)-api\.fly\.dev/);
|
|
130
|
+
if (flyEnvMatch) {
|
|
131
|
+
const [, prefix] = flyEnvMatch;
|
|
132
|
+
return `https://${prefix}-web.fly.dev`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Render PR previews: map API service to Web service
|
|
136
|
+
// canary-api-pr-123.onrender.com -> canary-web-pr-123.onrender.com
|
|
137
|
+
// testdriver-api-i4m4-pr-123.onrender.com -> web-i4m4-pr-123.onrender.com
|
|
138
|
+
const renderPrMatch = apiRoot.match(/https:\/\/([\w-]+)-api(-[\w]+)?(-pr-\d+)?\.onrender\.com/);
|
|
139
|
+
if (renderPrMatch) {
|
|
140
|
+
const [, prefix, suffix, prSuffix] = renderPrMatch;
|
|
141
|
+
let webPrefix;
|
|
142
|
+
if (prefix === 'testdriver' && suffix) {
|
|
143
|
+
webPrefix = 'web' + suffix;
|
|
144
|
+
} else {
|
|
145
|
+
webPrefix = prefix + '-web';
|
|
146
|
+
}
|
|
147
|
+
return `https://${webPrefix}${prSuffix || ''}.onrender.com`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Cloudflare tunnels, custom domains, etc.: the web console is served
|
|
151
|
+
// from the same origin as the API, so return apiRoot as-is.
|
|
152
|
+
return apiRoot;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get dashcam executable path
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
async _getDashcamPath() {
|
|
160
|
+
|
|
161
|
+
if (this.client.os === "windows") {
|
|
162
|
+
return "C:\\Program Files\\nodejs\\dashcam.cmd";
|
|
163
|
+
} else {
|
|
164
|
+
return "/usr/bin/dashcam";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Authenticate dashcam with API key
|
|
171
|
+
* @param {string} [apiKey] - Override API key
|
|
172
|
+
* @returns {Promise<void>}
|
|
173
|
+
*/
|
|
174
|
+
async auth(apiKey) {
|
|
175
|
+
const key = apiKey || this.apiKey;
|
|
176
|
+
const shell = this._getShell();
|
|
177
|
+
const apiRoot = this._getApiRoot();
|
|
178
|
+
|
|
179
|
+
if (this.client.os === "windows") {
|
|
180
|
+
const dashcamPath = await this._getDashcamPath();
|
|
181
|
+
this._log("debug", "Dashcam executable path:", dashcamPath);
|
|
182
|
+
|
|
183
|
+
// Authenticate with TD_API_ROOT
|
|
184
|
+
const authOutput = await this.client.exec(
|
|
185
|
+
shell,
|
|
186
|
+
`$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" auth ${key}`,
|
|
187
|
+
120000,
|
|
188
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
189
|
+
);
|
|
190
|
+
this._log("debug", "Auth output:", authOutput);
|
|
191
|
+
} else {
|
|
192
|
+
// Linux/Mac authentication with TD_API_ROOT
|
|
193
|
+
const authOutput = await this.client.exec(
|
|
194
|
+
shell,
|
|
195
|
+
`TD_API_ROOT="${apiRoot}" dashcam auth ${key}`,
|
|
196
|
+
120000,
|
|
197
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
198
|
+
);
|
|
199
|
+
this._log("debug", "Auth output:", authOutput);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this._authenticated = true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Add file log tracking
|
|
207
|
+
* @param {string} path - Path to log file
|
|
208
|
+
* @param {string} name - Display name
|
|
209
|
+
* @returns {Promise<void>}
|
|
210
|
+
*/
|
|
211
|
+
async addFileLog(path, name) {
|
|
212
|
+
const shell = this._getShell();
|
|
213
|
+
const apiRoot = this._getApiRoot();
|
|
214
|
+
|
|
215
|
+
if (this.client.os === "windows") {
|
|
216
|
+
// Create log file if it doesn't exist
|
|
217
|
+
const createFileOutput = await this.client.exec(
|
|
218
|
+
shell,
|
|
219
|
+
`New-Item -ItemType File -Path "${path}" -Force`,
|
|
220
|
+
10000,
|
|
221
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
222
|
+
);
|
|
223
|
+
this._log("debug", "Create log file output:", createFileOutput);
|
|
224
|
+
|
|
225
|
+
const dashcamPath = await this._getDashcamPath();
|
|
226
|
+
const addLogOutput = await this.client.exec(
|
|
227
|
+
shell,
|
|
228
|
+
`$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=file --file="${path}" --name="${name}"`,
|
|
229
|
+
120000,
|
|
230
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
231
|
+
);
|
|
232
|
+
this._log("debug", "Add log tracking output:", addLogOutput);
|
|
233
|
+
} else {
|
|
234
|
+
// Create log file
|
|
235
|
+
await this.client.exec(
|
|
236
|
+
shell,
|
|
237
|
+
`touch ${path}`,
|
|
238
|
+
10000,
|
|
239
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Add log tracking with TD_API_ROOT
|
|
243
|
+
const addLogOutput = await this.client.exec(
|
|
244
|
+
shell,
|
|
245
|
+
`TD_API_ROOT="${apiRoot}" dashcam logs --add --type=file --file="${path}" --name="${name}"`,
|
|
246
|
+
10000,
|
|
247
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
248
|
+
);
|
|
249
|
+
this._log("debug", "Add log tracking output:", addLogOutput);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Add application log tracking
|
|
255
|
+
* @param {string} application - Application name
|
|
256
|
+
* @param {string} name - Display name
|
|
257
|
+
* @returns {Promise<void>}
|
|
258
|
+
*/
|
|
259
|
+
async addApplicationLog(application, name) {
|
|
260
|
+
const shell = this._getShell();
|
|
261
|
+
const dashcamPath = await this._getDashcamPath();
|
|
262
|
+
const apiRoot = this._getApiRoot();
|
|
263
|
+
|
|
264
|
+
if (this.client.os === "windows") {
|
|
265
|
+
const addLogOutput = await this.client.exec(
|
|
266
|
+
shell,
|
|
267
|
+
`$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=application --application="${application}" --name="${name}"`,
|
|
268
|
+
120000,
|
|
269
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
270
|
+
);
|
|
271
|
+
this._log("debug", "Add application log tracking output:", addLogOutput);
|
|
272
|
+
} else {
|
|
273
|
+
const addLogOutput = await this.client.exec(
|
|
274
|
+
shell,
|
|
275
|
+
`TD_API_ROOT="${apiRoot}" dashcam logs --add --type=application --application="${application}" --name="${name}"`,
|
|
276
|
+
10000,
|
|
277
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
278
|
+
);
|
|
279
|
+
this._log("debug", "Add application log tracking output:", addLogOutput);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Add web log tracking
|
|
285
|
+
* @param {string} pattern - URL pattern to match (e.g., "*example.com*")
|
|
286
|
+
* @param {string} name - Display name
|
|
287
|
+
* @returns {Promise<void>}
|
|
288
|
+
*/
|
|
289
|
+
async addWebLog(pattern, name) {
|
|
290
|
+
const shell = this._getShell();
|
|
291
|
+
const dashcamPath = await this._getDashcamPath();
|
|
292
|
+
const apiRoot = this._getApiRoot();
|
|
293
|
+
|
|
294
|
+
if (this.client.os === "windows") {
|
|
295
|
+
try {
|
|
296
|
+
const addLogOutput = await this.client.exec(
|
|
297
|
+
shell,
|
|
298
|
+
`$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=web --pattern="${pattern}" --name="${name}"`,
|
|
299
|
+
120000,
|
|
300
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
301
|
+
);
|
|
302
|
+
this._log("debug", "Add web log tracking output:", addLogOutput);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
this._log("warn", "Add web log tracking failed:", err.message);
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
const addLogOutput = await this.client.exec(
|
|
308
|
+
shell,
|
|
309
|
+
`TD_API_ROOT="${apiRoot}" dashcam logs --add --type=web --pattern="${pattern}" --name="${name}"`,
|
|
310
|
+
10000,
|
|
311
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
312
|
+
);
|
|
313
|
+
this._log("debug", "Add web log tracking output:", addLogOutput);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Start dashcam recording
|
|
319
|
+
* @returns {Promise<void>}
|
|
320
|
+
*/
|
|
321
|
+
async start() {
|
|
322
|
+
if (this.recording) {
|
|
323
|
+
this._log("warn", "Dashcam already recording");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Auto-authenticate if not already done
|
|
328
|
+
if (!this._authenticated) {
|
|
329
|
+
await this.auth();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const shell = this._getShell();
|
|
333
|
+
const apiRoot = this._getApiRoot();
|
|
334
|
+
|
|
335
|
+
if (this.client.os === "windows") {
|
|
336
|
+
const dashcamPath = await this._getDashcamPath();
|
|
337
|
+
|
|
338
|
+
// Start dashcam record and redirect output with TD_API_ROOT
|
|
339
|
+
const outputFile =
|
|
340
|
+
"C:\\Users\\testdriver\\.dashcam-cli\\dashcam-start.log";
|
|
341
|
+
// const titleArg = this.title ? ` --title=\`"${this.title.replace(/"/g, '`"')}\`"` : '';
|
|
342
|
+
let titleArg = "";
|
|
343
|
+
const startScript = `
|
|
344
|
+
try {
|
|
345
|
+
$env:TD_API_ROOT="${apiRoot}"
|
|
346
|
+
$process = Start-Process "cmd.exe" -ArgumentList "/c", "\`"${dashcamPath}\`" record${titleArg}"
|
|
347
|
+
Write-Output "Process started with PID: $($process.Id)"
|
|
348
|
+
Start-Sleep -Seconds 2
|
|
349
|
+
if ($process.HasExited) {
|
|
350
|
+
Write-Output "Process has already exited with code: $($process.ExitCode)"
|
|
351
|
+
} else {
|
|
352
|
+
Write-Output "Process is still running"
|
|
353
|
+
}
|
|
354
|
+
} catch {
|
|
355
|
+
Write-Output "ERROR: $_"
|
|
356
|
+
}
|
|
357
|
+
`;
|
|
358
|
+
|
|
359
|
+
// add 2>&1" -PassThru
|
|
360
|
+
|
|
361
|
+
// Capture startTime right before issuing the dashcam command to sync with actual recording start
|
|
362
|
+
this.startTime = Date.now();
|
|
363
|
+
const startOutput = await this.client.exec(
|
|
364
|
+
shell,
|
|
365
|
+
startScript,
|
|
366
|
+
10000,
|
|
367
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
368
|
+
);
|
|
369
|
+
this._log("debug", "Start-Process output:", startOutput);
|
|
370
|
+
|
|
371
|
+
// Wait and check output
|
|
372
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
373
|
+
const dashcamOutput = await this.client.exec(
|
|
374
|
+
shell,
|
|
375
|
+
`Get-Content "${outputFile}" -ErrorAction SilentlyContinue`,
|
|
376
|
+
10000,
|
|
377
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
378
|
+
);
|
|
379
|
+
this._log("debug", "Dashcam record output:", dashcamOutput);
|
|
380
|
+
|
|
381
|
+
// Give process time to initialize
|
|
382
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
383
|
+
|
|
384
|
+
this._log("debug", "Dashcam recording started");
|
|
385
|
+
} else {
|
|
386
|
+
// Linux/Mac with TD_API_ROOT
|
|
387
|
+
this._log("debug", "Starting dashcam recording on Linux/Mac...");
|
|
388
|
+
const titleArg = this.title
|
|
389
|
+
? ` --title="${this.title.replace(/"/g, '"')}"`
|
|
390
|
+
: "";
|
|
391
|
+
// Capture startTime right before issuing the dashcam command to sync with actual recording start
|
|
392
|
+
this.startTime = Date.now();
|
|
393
|
+
await this.client.exec(
|
|
394
|
+
shell,
|
|
395
|
+
`TD_API_ROOT="${apiRoot}" dashcam record${titleArg} >/dev/null 2>&1 &`,
|
|
396
|
+
10000,
|
|
397
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
398
|
+
);
|
|
399
|
+
this._log("debug", "Dashcam recording started");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
this.recording = true;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Set the recording title
|
|
407
|
+
* This can be called before start() to customize the title
|
|
408
|
+
* @param {string} title - Custom recording title
|
|
409
|
+
*/
|
|
410
|
+
setTitle(title) {
|
|
411
|
+
this.title = title;
|
|
412
|
+
this._log("debug", `Set dashcam recording title: ${title}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Stop dashcam recording and retrieve replay URL
|
|
417
|
+
* @returns {Promise<string|null>} Replay URL if available
|
|
418
|
+
*/
|
|
419
|
+
async stop() {
|
|
420
|
+
if (!this.recording) {
|
|
421
|
+
// Internal log only - don't spam user console
|
|
422
|
+
this._log("warn", "Dashcam not recording");
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
this._log("debug", "Stopping dashcam and retrieving URL...");
|
|
427
|
+
const shell = this._getShell();
|
|
428
|
+
const apiRoot = this._getApiRoot();
|
|
429
|
+
let output;
|
|
430
|
+
|
|
431
|
+
if (this.client.os === "windows") {
|
|
432
|
+
this._log("debug", "Stopping dashcam process on Windows...");
|
|
433
|
+
|
|
434
|
+
const dashcamPath = await this._getDashcamPath();
|
|
435
|
+
|
|
436
|
+
// Stop and get output with TD_API_ROOT
|
|
437
|
+
output = await this.client.exec(
|
|
438
|
+
shell,
|
|
439
|
+
`$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" stop`,
|
|
440
|
+
300000,
|
|
441
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
442
|
+
);
|
|
443
|
+
this._log("debug", "Dashcam stop command output:", output);
|
|
444
|
+
} else {
|
|
445
|
+
// Linux/Mac with TD_API_ROOT
|
|
446
|
+
const dashcamPath = await this._getDashcamPath();
|
|
447
|
+
output = await this.client.exec(
|
|
448
|
+
shell,
|
|
449
|
+
`TD_API_ROOT="${apiRoot}" "${dashcamPath}" stop`,
|
|
450
|
+
300000,
|
|
451
|
+
process.env.TD_DEBUG == "true" ? false : true,
|
|
452
|
+
);
|
|
453
|
+
this._log("debug", "Dashcam command output:", output);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
this.recording = false;
|
|
457
|
+
|
|
458
|
+
// Extract the /replay/... path from CLI output and reconstruct the URL
|
|
459
|
+
// using getConsoleUrl(). The CLI may return a wrong domain
|
|
460
|
+
// so we always rewrite the base URL to match the current environment.
|
|
461
|
+
if (output) {
|
|
462
|
+
// Match /replay/{id} with optional query params from any URL or broken prefix
|
|
463
|
+
const replayPathMatch = output.match(
|
|
464
|
+
/(?:https?:\/\/[^\s"',}]+|undefined|null)?(\/replay\/[^\s"',}]+)/,
|
|
465
|
+
);
|
|
466
|
+
if (replayPathMatch) {
|
|
467
|
+
const replayPath = replayPathMatch[1].replace(/[.,;:!\)\]]+$/, "").trim();
|
|
468
|
+
const consoleUrl = Dashcam.getConsoleUrl(this._getApiRoot());
|
|
469
|
+
const url = consoleUrl + replayPath;
|
|
470
|
+
this._log("debug", "Replay URL:", url);
|
|
471
|
+
return url;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
this._log("warn", "No replay URL found in dashcam output");
|
|
475
|
+
} else {
|
|
476
|
+
this._log("warn", "Dashcam command returned no output");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Internal logging - uses TestDriver logger
|
|
484
|
+
* @private
|
|
485
|
+
*/
|
|
486
|
+
_log(level, ...args) {
|
|
487
|
+
const message = args
|
|
488
|
+
.map((arg) =>
|
|
489
|
+
typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg),
|
|
490
|
+
)
|
|
491
|
+
.join(" ");
|
|
492
|
+
|
|
493
|
+
const logMessage = `[DASHCAM] ${message}`;
|
|
494
|
+
|
|
495
|
+
// Use the TestDriver logger based on level
|
|
496
|
+
switch (level) {
|
|
497
|
+
case "error":
|
|
498
|
+
logger.error(logMessage);
|
|
499
|
+
break;
|
|
500
|
+
case "warn":
|
|
501
|
+
logger.warn(logMessage);
|
|
502
|
+
break;
|
|
503
|
+
case "debug":
|
|
504
|
+
logger.debug(logMessage);
|
|
505
|
+
break;
|
|
506
|
+
case "info":
|
|
507
|
+
default:
|
|
508
|
+
logger.info(logMessage);
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Check if currently recording
|
|
515
|
+
* @returns {Promise<boolean>}
|
|
516
|
+
*/
|
|
517
|
+
async isRecording() {
|
|
518
|
+
return this.recording;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get milliseconds elapsed since dashcam started recording
|
|
523
|
+
* @returns {number|null} Milliseconds since start, or null if not recording
|
|
524
|
+
*/
|
|
525
|
+
getElapsedTime() {
|
|
526
|
+
if (!this.recording || !this.startTime) {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
return Date.now() - this.startTime;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
module.exports = Dashcam;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript definitions for TestDriver Core Module
|
|
3
|
+
* @module testdriverai/core
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class Dashcam {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new Dashcam instance
|
|
9
|
+
* @param client - TestDriver client instance
|
|
10
|
+
* @param options - Dashcam options
|
|
11
|
+
*/
|
|
12
|
+
constructor(client: any, options?: DashcamOptions);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Authenticate with Dashcam CLI
|
|
16
|
+
* @param apiKey - Dashcam API key (optional, uses DASHCAM_API_KEY env var if not provided)
|
|
17
|
+
* @returns Promise that resolves when authenticated
|
|
18
|
+
*/
|
|
19
|
+
auth(apiKey?: string): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Add a log entry to Dashcam
|
|
23
|
+
* @param config - Log configuration
|
|
24
|
+
*/
|
|
25
|
+
addLog(config: LogConfig): Promise<void>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Add a file log to Dashcam
|
|
29
|
+
* @param path - Path to file to log
|
|
30
|
+
* @param name - Name/description for the log entry
|
|
31
|
+
*/
|
|
32
|
+
addFileLog(path: string, name: string): Promise<void>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Add an application log to Dashcam
|
|
36
|
+
* @param application - Application name to track
|
|
37
|
+
* @param name - Name/description for the log entry
|
|
38
|
+
*/
|
|
39
|
+
addApplicationLog(application: string, name: string): Promise<void>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Start recording
|
|
43
|
+
* @returns Promise that resolves when recording starts
|
|
44
|
+
*/
|
|
45
|
+
start(): Promise<void>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Stop recording and get replay URL
|
|
49
|
+
* @returns Promise that resolves to the replay URL (or null if not recording)
|
|
50
|
+
*/
|
|
51
|
+
stop(): Promise<string | null>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if currently recording
|
|
55
|
+
* @returns true if recording, false otherwise
|
|
56
|
+
*/
|
|
57
|
+
isRecording(): boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface DashcamOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Dashcam API key (defaults to DASHCAM_API_KEY env var)
|
|
63
|
+
*/
|
|
64
|
+
apiKey?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LogConfig {
|
|
68
|
+
/**
|
|
69
|
+
* Type of log entry
|
|
70
|
+
*/
|
|
71
|
+
type: 'file' | 'application';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Path to file (for file logs)
|
|
75
|
+
*/
|
|
76
|
+
path?: string;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Application name (for application logs)
|
|
80
|
+
*/
|
|
81
|
+
application?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Name/description for the log entry
|
|
85
|
+
*/
|
|
86
|
+
name: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* TestDriver SDK class
|
|
91
|
+
* Re-exported from main module for convenience
|
|
92
|
+
*/
|
|
93
|
+
export class TestDriver {
|
|
94
|
+
constructor(apiKey: string, options?: TestDriverOptions);
|
|
95
|
+
|
|
96
|
+
auth(): Promise<void>;
|
|
97
|
+
connect(options?: ConnectOptions): Promise<any>;
|
|
98
|
+
disconnect(): Promise<void>;
|
|
99
|
+
|
|
100
|
+
find(query: string): Promise<any>;
|
|
101
|
+
findAll(query: string): Promise<any[]>;
|
|
102
|
+
click(target: string): Promise<void>;
|
|
103
|
+
type(target: string, text: string): Promise<void>;
|
|
104
|
+
exec(shell: string, command: string, timeout?: number, ignoreError?: boolean): Promise<string>;
|
|
105
|
+
focusApplication(appName: string): Promise<void>;
|
|
106
|
+
|
|
107
|
+
// Add other TestDriver methods as needed
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface TestDriverOptions {
|
|
111
|
+
/**
|
|
112
|
+
* API endpoint URL
|
|
113
|
+
*/
|
|
114
|
+
apiRoot?: string;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Target OS: 'linux', 'mac', or 'windows'
|
|
118
|
+
*/
|
|
119
|
+
os?: 'linux' | 'mac' | 'windows';
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Create new sandbox
|
|
123
|
+
*/
|
|
124
|
+
newSandbox?: boolean;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Screen resolution
|
|
128
|
+
*/
|
|
129
|
+
resolution?: string;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Enable analytics
|
|
133
|
+
*/
|
|
134
|
+
analytics?: boolean;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Cache configuration
|
|
138
|
+
* Set to false to disable caching entirely.
|
|
139
|
+
* Set to an object to configure thresholds.
|
|
140
|
+
* @example { cache: { enabled: true, thresholds: { find: { screen: 0.05, element: 0.8 }, assert: 0.05 } } }
|
|
141
|
+
*/
|
|
142
|
+
cache?: boolean | {
|
|
143
|
+
enabled?: boolean;
|
|
144
|
+
thresholds?: {
|
|
145
|
+
/** Thresholds for find operations */
|
|
146
|
+
find?: {
|
|
147
|
+
/** Pixel diff threshold for screen comparison (0-1, default 0.05 = 5% diff allowed) */
|
|
148
|
+
screen?: number;
|
|
149
|
+
/** OpenCV template match threshold for element matching (0-1, default 0.8 = 80% correlation) */
|
|
150
|
+
element?: number;
|
|
151
|
+
};
|
|
152
|
+
/** Pixel diff threshold for assert operations (0-1, default 0.05 = 5% diff allowed) */
|
|
153
|
+
assert?: number;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @deprecated Use cache.thresholds instead
|
|
159
|
+
* Cache thresholds for find operations
|
|
160
|
+
*/
|
|
161
|
+
cacheThresholds?: {
|
|
162
|
+
find?: number;
|
|
163
|
+
findAll?: number;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface ConnectOptions {
|
|
168
|
+
/**
|
|
169
|
+
* Create new sandbox instance
|
|
170
|
+
*/
|
|
171
|
+
new?: boolean;
|
|
172
|
+
}
|