testdriverai 7.3.2 โ 7.3.4
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/.github/workflows/acceptance-linux-scheduled.yaml +45 -0
- package/.github/workflows/acceptance-windows-scheduled.yaml +54 -0
- package/.github/workflows/acceptance.yaml +106 -0
- package/.github/workflows/publish.yaml +75 -0
- package/.github/workflows/test-init.yml +157 -0
- package/.github/workflows/testdriver.yml +170 -0
- package/.github/workflows/windows-self-hosted.yaml +82 -0
- package/.prettierignore +4 -0
- package/.prettierrc +1 -0
- package/CHANGELOG.md +162 -0
- package/SKILLs.md +17 -0
- package/ai/.claude-plugin/plugin.json +9 -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/_scripts/generate-skills.js +149 -0
- package/docs/_scripts/link-replacer.js +164 -0
- package/docs/_scripts/upload-docs-to-openai.js +284 -0
- package/docs/claude-mcp-plugin.mdx +160 -0
- package/docs/docs.json +394 -0
- package/docs/github-integration-setup.md +266 -0
- package/docs/guide/best-practices-polling.mdx +154 -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/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/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 +282 -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 +852 -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 +282 -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 +754 -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/{ai/skills/testdriver:ai/SKILL.md โ docs/v7/ai.mdx} +4 -3
- package/{ai/skills/testdriver:assert/SKILL.md โ docs/v7/assert.mdx} +4 -3
- package/{ai/skills/testdriver:aws-setup/SKILL.md โ docs/v7/aws-setup.mdx} +4 -3
- package/{ai/skills/testdriver:caching/SKILL.md โ docs/v7/caching.mdx} +7 -3
- package/{ai/skills/testdriver:captcha/SKILL.md โ docs/v7/captcha.mdx} +4 -3
- package/{ai/skills/testdriver:ci-cd/SKILL.md โ docs/v7/ci-cd.mdx} +4 -3
- package/{ai/skills/testdriver:click/SKILL.md โ docs/v7/click.mdx} +4 -3
- package/{ai/skills/testdriver:client/SKILL.md โ docs/v7/client.mdx} +8 -3
- package/{ai/skills/testdriver:cloud/SKILL.md โ docs/v7/cloud.mdx} +4 -3
- package/{ai/skills/testdriver:customizing-devices/SKILL.md โ docs/v7/customizing-devices.mdx} +3 -3
- package/{ai/skills/testdriver:dashcam/SKILL.md โ docs/v7/dashcam.mdx} +4 -3
- package/docs/v7/debugging-with-screenshots.mdx +402 -0
- package/{ai/skills/testdriver:device-config/SKILL.md โ docs/v7/device-config.mdx} +3 -3
- package/{ai/skills/testdriver:double-click/SKILL.md โ docs/v7/double-click.mdx} +3 -3
- package/{ai/skills/testdriver:elements/SKILL.md โ docs/v7/elements.mdx} +4 -3
- package/{ai/skills/testdriver:enterprise/SKILL.md โ docs/v7/enterprise.mdx} +5 -3
- package/docs/v7/examples.mdx +5 -0
- package/{ai/skills/testdriver:exec/SKILL.md โ docs/v7/exec.mdx} +4 -3
- package/{ai/skills/testdriver:find/SKILL.md โ docs/v7/find.mdx} +4 -3
- package/{ai/skills/testdriver:focus-application/SKILL.md โ docs/v7/focus-application.mdx} +4 -3
- package/{ai/skills/testdriver:generating-tests/SKILL.md โ docs/v7/generating-tests.mdx} +3 -3
- package/{ai/skills/testdriver:hover/SKILL.md โ docs/v7/hover.mdx} +4 -3
- package/{ai/skills/testdriver:locating-elements/SKILL.md โ docs/v7/locating-elements.mdx} +3 -3
- package/{ai/skills/testdriver:making-assertions/SKILL.md โ docs/v7/making-assertions.mdx} +3 -3
- package/{ai/skills/testdriver:mouse-down/SKILL.md โ docs/v7/mouse-down.mdx} +3 -3
- package/{ai/skills/testdriver:mouse-up/SKILL.md โ docs/v7/mouse-up.mdx} +3 -3
- package/docs/v7/ocr.mdx +236 -0
- package/{ai/skills/testdriver:performing-actions/SKILL.md โ docs/v7/performing-actions.mdx} +3 -3
- package/{ai/skills/testdriver:press-keys/SKILL.md โ docs/v7/press-keys.mdx} +4 -3
- package/{ai/skills/testdriver:quickstart/SKILL.md โ docs/v7/quickstart.mdx} +6 -20
- package/{ai/skills/testdriver:reusable-code/SKILL.md โ docs/v7/reusable-code.mdx} +3 -3
- package/{ai/skills/testdriver:right-click/SKILL.md โ docs/v7/right-click.mdx} +3 -3
- package/{ai/skills/testdriver:running-tests/SKILL.md โ docs/v7/running-tests.mdx} +7 -3
- package/{ai/skills/testdriver:screenshot/SKILL.md โ docs/v7/screenshot.mdx} +88 -6
- package/{ai/skills/testdriver:scroll/SKILL.md โ docs/v7/scroll.mdx} +40 -3
- package/{ai/skills/testdriver:secrets/SKILL.md โ docs/v7/secrets.mdx} +3 -3
- package/{ai/skills/testdriver:self-hosted/SKILL.md โ docs/v7/self-hosted.mdx} +4 -3
- package/{ai/skills/testdriver:type/SKILL.md โ docs/v7/type.mdx} +4 -3
- package/{ai/skills/testdriver:variables/SKILL.md โ docs/v7/variables.mdx} +3 -3
- package/{ai/skills/testdriver:waiting-for-elements/SKILL.md โ docs/v7/waiting-for-elements.mdx} +3 -3
- package/{ai/skills/testdriver:what-is-testdriver/SKILL.md โ docs/v7/what-is-testdriver.mdx} +3 -3
- package/eslint.config.js +67 -0
- package/examples/ai.test.mjs +30 -0
- package/examples/assert.test.mjs +47 -0
- package/examples/captcha-api.test.mjs +50 -0
- package/examples/chrome-extension.test.mjs +94 -0
- package/examples/drag-and-drop.test.mjs +58 -0
- package/examples/element-not-found.test.mjs +26 -0
- package/examples/exec-output.test.mjs +59 -0
- package/examples/exec-pwsh.test.mjs +57 -0
- package/examples/focus-window.test.mjs +36 -0
- package/examples/formatted-logging.test.mjs +26 -0
- package/examples/hover-image.test.mjs +52 -0
- package/examples/hover-text-with-description.test.mjs +56 -0
- package/examples/hover-text.test.mjs +27 -0
- package/examples/installer.test.mjs +49 -0
- package/examples/launch-vscode-linux.test.mjs +54 -0
- package/examples/match-image.test.mjs +54 -0
- package/examples/no-provision.test.mjs +23 -0
- package/examples/press-keys.test.mjs +50 -0
- package/examples/prompt.test.mjs +33 -0
- package/examples/scroll-keyboard.test.mjs +37 -0
- package/examples/scroll-until-image.test.mjs +39 -0
- package/examples/scroll-until-text.test.mjs +67 -0
- package/examples/scroll.test.mjs +41 -0
- package/examples/type.test.mjs +45 -0
- package/examples/windows-installer.test.mjs +53 -0
- package/jsconfig.json +26 -0
- package/manual/test-init-command.js +223 -0
- package/mcp-server/README.md +312 -0
- package/mcp-server/mcp-app.html +28 -0
- package/mcp-server/mcp-config.example.json +19 -0
- package/mcp-server/package-lock.json +4018 -0
- package/mcp-server/package.json +29 -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 +2313 -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 +2 -17
- package/scripts/generate-skills.js +94 -0
- package/setup/aws/cloudformation.yaml +470 -0
- package/setup/aws/spawn-runner.sh +190 -0
- package/test/api-resilience.test.mjs +0 -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/test-ide-preview.mjs +17 -0
- package/tests/airbnb-booking.test.mjs +39 -0
- package/tests/airbnb-search.test.mjs +43 -0
- package/tests/example.test.js +33 -0
- package/tests/login.js +28 -0
- package/vitest.config.mjs +24 -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
- package/ai/skills/testdriver:examples/SKILL.md +0 -7
- package/ai/skills/testdriver:mcp-workflow/SKILL.md +0 -410
- package/ai/skills/testdriver:testdriver/SKILL.md +0 -523
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the captcha solver script
|
|
3
|
+
* These tests verify the solver script is valid JavaScript and can be loaded correctly
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import vm from "vm";
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const solverPath = path.join(__dirname, "..", "lib", "captcha", "solver.js");
|
|
13
|
+
|
|
14
|
+
// Extract safeParseJson function from the solver script for testing
|
|
15
|
+
function getSafeParseJson() {
|
|
16
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
17
|
+
// Extract the safeParseJson function source
|
|
18
|
+
const funcMatch = script.match(
|
|
19
|
+
/function safeParseJson\(text\) \{[\s\S]*?^}/m,
|
|
20
|
+
);
|
|
21
|
+
if (!funcMatch) {
|
|
22
|
+
throw new Error("Could not find safeParseJson function in solver script");
|
|
23
|
+
}
|
|
24
|
+
// Create and return the function
|
|
25
|
+
const func = new Function("return " + funcMatch[0])();
|
|
26
|
+
return func;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("Captcha Solver Script", () => {
|
|
30
|
+
it("should exist", () => {
|
|
31
|
+
expect(fs.existsSync(solverPath)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should be valid JavaScript syntax", () => {
|
|
35
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
36
|
+
|
|
37
|
+
// This will throw if the syntax is invalid
|
|
38
|
+
expect(() => {
|
|
39
|
+
new vm.Script(script, { filename: "solver.js" });
|
|
40
|
+
}).not.toThrow();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should contain required functions and variables", () => {
|
|
44
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
45
|
+
|
|
46
|
+
// Check for key components
|
|
47
|
+
expect(script).toContain('require("https")');
|
|
48
|
+
expect(script).toContain('require("chrome-remote-interface")');
|
|
49
|
+
expect(script).toContain("detectCaptchaScript");
|
|
50
|
+
expect(script).toContain("getInjectScript");
|
|
51
|
+
expect(script).toContain("autoSubmitScript");
|
|
52
|
+
expect(script).toContain("checkSuccessScript");
|
|
53
|
+
expect(script).toContain("2captcha.com");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should handle all supported captcha types", () => {
|
|
57
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
58
|
+
|
|
59
|
+
// Check for captcha type handling
|
|
60
|
+
expect(script).toContain("recaptcha_v2");
|
|
61
|
+
expect(script).toContain("recaptcha_v3");
|
|
62
|
+
expect(script).toContain("hcaptcha");
|
|
63
|
+
expect(script).toContain("turnstile");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should have proper auto-detection for captcha elements", () => {
|
|
67
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
68
|
+
|
|
69
|
+
// Check for DOM selectors used in auto-detection
|
|
70
|
+
expect(script).toContain(".g-recaptcha[data-sitekey]");
|
|
71
|
+
expect(script).toContain(".h-captcha[data-sitekey]");
|
|
72
|
+
expect(script).toContain(".cf-turnstile[data-sitekey]");
|
|
73
|
+
expect(script).toContain("[data-sitekey]");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should properly inject tokens into response fields", () => {
|
|
77
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
78
|
+
|
|
79
|
+
// Check for token injection selectors
|
|
80
|
+
expect(script).toContain("[name=g-recaptcha-response]");
|
|
81
|
+
expect(script).toContain("[name=h-captcha-response]");
|
|
82
|
+
expect(script).toContain("[name=cf-turnstile-response]");
|
|
83
|
+
expect(script).toContain("___grecaptcha_cfg");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should contain safeParseJson function for robust JSON parsing", () => {
|
|
87
|
+
const script = fs.readFileSync(solverPath, "utf8");
|
|
88
|
+
expect(script).toContain("function safeParseJson");
|
|
89
|
+
expect(script).toContain("safeParseJson(await httpsGet");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("safeParseJson", () => {
|
|
94
|
+
let safeParseJson;
|
|
95
|
+
let extractionFailed = false;
|
|
96
|
+
|
|
97
|
+
// Try to extract the function for testing
|
|
98
|
+
try {
|
|
99
|
+
safeParseJson = getSafeParseJson();
|
|
100
|
+
} catch {
|
|
101
|
+
extractionFailed = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Use it.skipIf to clearly indicate skipped tests
|
|
105
|
+
const testFn = extractionFailed ? it.skip : it;
|
|
106
|
+
|
|
107
|
+
testFn("should parse valid JSON normally", () => {
|
|
108
|
+
const result = safeParseJson('{"status":1,"request":"abc123"}');
|
|
109
|
+
expect(result).toEqual({ status: 1, request: "abc123" });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
testFn("should handle JSON with leading/trailing whitespace", () => {
|
|
113
|
+
const result = safeParseJson(' {"status":1} \n');
|
|
114
|
+
expect(result).toEqual({ status: 1 });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
testFn("should extract first JSON object from concatenated responses", () => {
|
|
118
|
+
// This is the exact error case: multiple JSON objects concatenated
|
|
119
|
+
const result = safeParseJson('{"status":1}{"status":2}');
|
|
120
|
+
expect(result).toEqual({ status: 1 });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
testFn("should handle JSON with trailing garbage characters", () => {
|
|
124
|
+
const result = safeParseJson('{"status":1}xxx');
|
|
125
|
+
expect(result).toEqual({ status: 1 });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
testFn("should handle nested objects correctly", () => {
|
|
129
|
+
const result = safeParseJson('{"data":{"nested":true}}extra');
|
|
130
|
+
expect(result).toEqual({ data: { nested: true } });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
testFn("should handle strings containing braces", () => {
|
|
134
|
+
const result = safeParseJson('{"text":"hello {world}"}extra');
|
|
135
|
+
expect(result).toEqual({ text: "hello {world}" });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
testFn("should handle escaped quotes in strings", () => {
|
|
139
|
+
const result = safeParseJson('{"text":"say \\"hello\\""}extra');
|
|
140
|
+
expect(result).toEqual({ text: 'say "hello"' });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
testFn("should throw for responses without JSON", () => {
|
|
144
|
+
expect(() => safeParseJson("not json at all")).toThrow(
|
|
145
|
+
"No JSON object found",
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
testFn("should throw for incomplete JSON", () => {
|
|
150
|
+
expect(() => safeParseJson('{"incomplete')).toThrow("Invalid JSON");
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test that validates Chrome remote debugging is working
|
|
3
|
+
* Installs chrome-remote-interface and connects to the active tab
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { TestDriver } from "../lib/vitest/hooks.mjs";
|
|
7
|
+
|
|
8
|
+
describe("Chrome Remote Debugging", () => {
|
|
9
|
+
|
|
10
|
+
it("should connect to Chrome via CDP and get page title", async (context) => {
|
|
11
|
+
const testdriver = TestDriver(context);
|
|
12
|
+
|
|
13
|
+
// Launch Chrome with a known URL
|
|
14
|
+
await testdriver.provision.chrome({
|
|
15
|
+
url: 'https://example.com',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Take a screenshot to verify Chrome launched
|
|
19
|
+
await testdriver.screenshot();
|
|
20
|
+
|
|
21
|
+
// Install chrome-remote-interface (needs sudo for global install)
|
|
22
|
+
const installResult = await testdriver.exec(
|
|
23
|
+
'sh',
|
|
24
|
+
'sudo npm install -g chrome-remote-interface',
|
|
25
|
+
60000
|
|
26
|
+
);
|
|
27
|
+
console.log('Install result:', installResult);
|
|
28
|
+
|
|
29
|
+
// Write the CDP script to a file to avoid quoting issues
|
|
30
|
+
const cdpScript = `
|
|
31
|
+
const CDP = require('chrome-remote-interface');
|
|
32
|
+
(async () => {
|
|
33
|
+
try {
|
|
34
|
+
const client = await CDP();
|
|
35
|
+
const { Runtime } = client;
|
|
36
|
+
const result = await Runtime.evaluate({
|
|
37
|
+
expression: 'document.title'
|
|
38
|
+
});
|
|
39
|
+
console.log('PAGE_TITLE:' + result.result.value);
|
|
40
|
+
await client.close();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('CDP_ERROR:' + err.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
// Write script to file
|
|
50
|
+
await testdriver.exec('sh', `cat > /tmp/cdp-test.js << 'SCRIPT'
|
|
51
|
+
${cdpScript}
|
|
52
|
+
SCRIPT`, 5000);
|
|
53
|
+
|
|
54
|
+
// Run the CDP script with NODE_PATH set to find globally installed modules
|
|
55
|
+
const cdpResult = await testdriver.exec(
|
|
56
|
+
'sh',
|
|
57
|
+
'NODE_PATH=/usr/lib/node_modules node /tmp/cdp-test.js 2>&1 || echo "CDP_EXIT_CODE:$?"',
|
|
58
|
+
30000
|
|
59
|
+
);
|
|
60
|
+
console.log('CDP result:', cdpResult);
|
|
61
|
+
|
|
62
|
+
// Verify we got the page title
|
|
63
|
+
expect(cdpResult).toContain('PAGE_TITLE:');
|
|
64
|
+
expect(cdpResult).toContain('Example Domain');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Experiment file - reconnects to existing sandbox
|
|
3
|
+
* Run AFTER setup.test.mjs passes
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
7
|
+
|
|
8
|
+
describe("Experiment DuckDuckGo", () => {
|
|
9
|
+
it("should search for apples", async (context) => {
|
|
10
|
+
const testdriver = TestDriver(context, {
|
|
11
|
+
reconnect: true, // โ Key: reconnects to last sandbox
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// NO provision here! The sandbox is already running from setup.test.mjs
|
|
15
|
+
|
|
16
|
+
// Find search input and type
|
|
17
|
+
const searchInput = await testdriver.find("search input");
|
|
18
|
+
await searchInput.click();
|
|
19
|
+
await testdriver.type("apples");
|
|
20
|
+
await testdriver.pressKeys(["enter"]);
|
|
21
|
+
|
|
22
|
+
// Assert results
|
|
23
|
+
const result = await testdriver.assert(
|
|
24
|
+
"I can see search results for apples",
|
|
25
|
+
);
|
|
26
|
+
expect(result).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup file - MINIMAL steps to get to starting state
|
|
3
|
+
* Only add more steps AFTER this passes!
|
|
4
|
+
*/
|
|
5
|
+
import { afterAll, describe, expect, it } from "vitest";
|
|
6
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
7
|
+
|
|
8
|
+
describe("Setup DuckDuckGo", () => {
|
|
9
|
+
afterAll(async () => {
|
|
10
|
+
// DO NOT disconnect - keep sandbox alive for reconnect
|
|
11
|
+
console.log("Sandbox staying alive for 30 seconds (keepAlive)");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should set up the application state", async (context) => {
|
|
15
|
+
const testdriver = TestDriver(context);
|
|
16
|
+
|
|
17
|
+
await testdriver.provision.chrome({
|
|
18
|
+
url: "https://duckduckgo.com",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Start with just ONE assertion to verify we're on the right page
|
|
22
|
+
const result = await testdriver.assert(
|
|
23
|
+
"I can see the DuckDuckGo search page",
|
|
24
|
+
);
|
|
25
|
+
expect(result).toBeTruthy();
|
|
26
|
+
|
|
27
|
+
console.log("โ
Setup ready - run experiment.test.mjs now");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Debug script to inspect the full locate API response
|
|
5
|
+
* Run this with: TD_API_KEY=your_key node debug-locate-response.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TestDriverSDK = require("./sdk.js");
|
|
9
|
+
|
|
10
|
+
async function debugLocateResponse() {
|
|
11
|
+
const client = new TestDriverSDK(process.env.TD_API_KEY);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log("Connecting to sandbox (Linux)...");
|
|
15
|
+
await client.connect({ headless: true });
|
|
16
|
+
|
|
17
|
+
console.log("Opening a test page...");
|
|
18
|
+
await client.focusApplication("Google Chrome");
|
|
19
|
+
await client.type("https://example.com");
|
|
20
|
+
await client.pressKeys(["enter"]);
|
|
21
|
+
|
|
22
|
+
// Wait for page to load
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
24
|
+
|
|
25
|
+
console.log("\nFinding an element to inspect the response...");
|
|
26
|
+
const element = await client.find("the heading that says Example Domain");
|
|
27
|
+
|
|
28
|
+
console.log("\n=".repeat(60));
|
|
29
|
+
console.log("FULL LOCATE API RESPONSE:");
|
|
30
|
+
console.log("=".repeat(60));
|
|
31
|
+
|
|
32
|
+
const response = element.getResponse();
|
|
33
|
+
console.log(JSON.stringify(response, null, 2));
|
|
34
|
+
|
|
35
|
+
console.log("\n=".repeat(60));
|
|
36
|
+
console.log("RESPONSE KEYS:");
|
|
37
|
+
console.log("=".repeat(60));
|
|
38
|
+
|
|
39
|
+
if (response) {
|
|
40
|
+
Object.keys(response).forEach((key) => {
|
|
41
|
+
const value = response[key];
|
|
42
|
+
const type = Array.isArray(value) ? "array" : typeof value;
|
|
43
|
+
const preview =
|
|
44
|
+
typeof value === "string" && value.length > 100
|
|
45
|
+
? `${value.substring(0, 100)}... (${value.length} chars)`
|
|
46
|
+
: typeof value === "object"
|
|
47
|
+
? JSON.stringify(value)
|
|
48
|
+
: value;
|
|
49
|
+
|
|
50
|
+
console.log(` ${key} (${type}): ${preview}`);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log("\n=".repeat(60));
|
|
55
|
+
console.log("ELEMENT PROPERTIES:");
|
|
56
|
+
console.log("=".repeat(60));
|
|
57
|
+
console.log(" found:", element.found());
|
|
58
|
+
console.log(" x:", element.x);
|
|
59
|
+
console.log(" y:", element.y);
|
|
60
|
+
console.log(" centerX:", element.centerX);
|
|
61
|
+
console.log(" centerY:", element.centerY);
|
|
62
|
+
console.log(" width:", element.width);
|
|
63
|
+
console.log(" height:", element.height);
|
|
64
|
+
console.log(" confidence:", element.confidence);
|
|
65
|
+
console.log(" text:", element.text);
|
|
66
|
+
console.log(" label:", element.label);
|
|
67
|
+
console.log(
|
|
68
|
+
" screenshot:",
|
|
69
|
+
element.screenshot ? `${element.screenshot.length} chars` : null,
|
|
70
|
+
);
|
|
71
|
+
console.log(" boundingBox:", element.boundingBox);
|
|
72
|
+
|
|
73
|
+
await client.disconnect();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error("Error:", error.message);
|
|
76
|
+
console.error(error.stack);
|
|
77
|
+
await client.disconnect();
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
debugLocateResponse();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver SDK - Reconnect Test Part 1: Provision
|
|
3
|
+
*
|
|
4
|
+
* This test provisions a new sandbox and navigates to the login page.
|
|
5
|
+
* The sandbox ID is saved to .testdriver/last-sandbox for the next test.
|
|
6
|
+
*
|
|
7
|
+
* The sandbox has keepAlive: 120000 (2 minutes) after disconnect.
|
|
8
|
+
* Run reconnect-signin.test.mjs within 2 minutes of this test completing.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* 1. npm test -- examples/reconnect-provision.test.mjs
|
|
12
|
+
* 2. (within 2 minutes) examples/reconnect-signin.test.mjs
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { afterAll, describe, expect, it } from "vitest";
|
|
16
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
17
|
+
|
|
18
|
+
describe("Reconnect Test - Part 1: Provision", () => {
|
|
19
|
+
|
|
20
|
+
afterAll(async () => {
|
|
21
|
+
// Explicitly DO NOT disconnect - we want the sandbox to stay alive
|
|
22
|
+
// for the reconnect test. The sandbox will auto-terminate after keepAlive TTL.
|
|
23
|
+
console.log("\nโ ๏ธ NOT disconnecting - sandbox will stay alive for ~2 minutes (keepAlive: 120000)");
|
|
24
|
+
console.log(" Run reconnect-signin.test.mjs within 2 minutes to test reconnect\n");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should provision sandbox and navigate to login page", async (context) => {
|
|
28
|
+
|
|
29
|
+
const testdriver = TestDriver(context, { newSandbox: true, headless: false });
|
|
30
|
+
|
|
31
|
+
// Provision Chrome and navigate to login page
|
|
32
|
+
await testdriver.provision.chrome({
|
|
33
|
+
url: 'http://testdriver-sandbox.vercel.app/login',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// Verify we're on the login page
|
|
38
|
+
const result = await testdriver.assert("I can see a Sign In button");
|
|
39
|
+
expect(result).toBeTruthy();
|
|
40
|
+
|
|
41
|
+
// Get the sandbox ID that was saved
|
|
42
|
+
const lastSandbox = testdriver.getLastSandboxId();
|
|
43
|
+
console.log("\nโ
Sandbox provisioned:", lastSandbox?.sandboxId);
|
|
44
|
+
console.log(" Sandbox info saved to .testdriver/last-sandbox");
|
|
45
|
+
|
|
46
|
+
expect(lastSandbox).toBeTruthy();
|
|
47
|
+
expect(lastSandbox.sandboxId).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver SDK - Console Log Test
|
|
3
|
+
* Tests that console.log statements are captured and sent to dashcam
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
8
|
+
|
|
9
|
+
describe("Console Log Test", () => {
|
|
10
|
+
it("should capture console logs and send them to dashcam", async (context) => {
|
|
11
|
+
console.log("๐ฌ Test starting - this should appear in dashcam");
|
|
12
|
+
|
|
13
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
14
|
+
await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
|
|
15
|
+
|
|
16
|
+
console.log("โ
Chrome launched successfully");
|
|
17
|
+
|
|
18
|
+
// Give Chrome a moment to fully render the UI
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
20
|
+
|
|
21
|
+
console.log("๐ Looking for Sign In button");
|
|
22
|
+
|
|
23
|
+
// Find and click the sign in button
|
|
24
|
+
const signInButton = await testdriver.find(
|
|
25
|
+
"Sign In, black button below the password field",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
console.log("๐ Clicking Sign In button");
|
|
29
|
+
await signInButton.click();
|
|
30
|
+
|
|
31
|
+
console.log("๐งช Asserting error message appears");
|
|
32
|
+
|
|
33
|
+
// Assert error shows
|
|
34
|
+
const result = await testdriver.assert(
|
|
35
|
+
"an error shows that fields are required",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
console.log("โ
Test completed successfully - all logs should be in dashcam");
|
|
39
|
+
|
|
40
|
+
expect(result).toBeTruthy();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quick test of the new find() API
|
|
5
|
+
* This is a simple smoke test to verify the Element class works
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TestDriver = require("../sdk");
|
|
9
|
+
|
|
10
|
+
async function testFindAPI() {
|
|
11
|
+
console.log("Testing new find() API...\n");
|
|
12
|
+
|
|
13
|
+
const client = new TestDriver("test-key", {
|
|
14
|
+
logging: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Test 1: Create element without connecting
|
|
18
|
+
console.log("โ Test 1: Creating Element instance");
|
|
19
|
+
const element = client.find("test element");
|
|
20
|
+
console.log(" Element description:", element.description);
|
|
21
|
+
console.log(" Element found():", element.found());
|
|
22
|
+
console.log(" Element coordinates:", element.getCoordinates());
|
|
23
|
+
|
|
24
|
+
// Test 2: Verify element methods exist
|
|
25
|
+
console.log("\nโ Test 2: Verifying Element methods");
|
|
26
|
+
console.log(" Has find():", typeof element.find === "function");
|
|
27
|
+
console.log(" Has click():", typeof element.click === "function");
|
|
28
|
+
console.log(" Has hover():", typeof element.hover === "function");
|
|
29
|
+
console.log(
|
|
30
|
+
" Has doubleClick():",
|
|
31
|
+
typeof element.doubleClick === "function",
|
|
32
|
+
);
|
|
33
|
+
console.log(" Has rightClick():", typeof element.rightClick === "function");
|
|
34
|
+
console.log(" Has mouseDown():", typeof element.mouseDown === "function");
|
|
35
|
+
console.log(" Has mouseUp():", typeof element.mouseUp === "function");
|
|
36
|
+
console.log(" Has found():", typeof element.found === "function");
|
|
37
|
+
console.log(
|
|
38
|
+
" Has getCoordinates():",
|
|
39
|
+
typeof element.getCoordinates === "function",
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Test 3: Verify error handling for clicking unfound element
|
|
43
|
+
console.log("\nโ Test 3: Error handling for unfound element");
|
|
44
|
+
try {
|
|
45
|
+
await element.click();
|
|
46
|
+
console.log(" โ Should have thrown error");
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.log(" โ Correctly throws error:", error.message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Test 4: Verify TypeScript types exist (if running from TypeScript)
|
|
52
|
+
console.log("\nโ Test 4: SDK methods");
|
|
53
|
+
console.log(" Has find():", typeof client.find === "function");
|
|
54
|
+
console.log(
|
|
55
|
+
" Has deprecated hoverText():",
|
|
56
|
+
typeof client.hoverText === "undefined" ? "not yet connected" : "exists",
|
|
57
|
+
);
|
|
58
|
+
console.log(
|
|
59
|
+
" Has deprecated waitForText():",
|
|
60
|
+
typeof client.waitForText === "undefined" ? "not yet connected" : "exists",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
console.log("\nโ
All basic tests passed!");
|
|
64
|
+
console.log(
|
|
65
|
+
"\nNote: Full integration tests require connection to TestDriver sandbox.",
|
|
66
|
+
);
|
|
67
|
+
console.log("See examples/sdk-find-example.js for complete usage examples.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
testFindAPI().catch((error) => {
|
|
71
|
+
console.error("Test failed:", error);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Test script for the init command
|
|
4
|
+
# Usage: ./test-init.sh
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "๐งช Testing testdriverai init command..."
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
# Cleanup function
|
|
12
|
+
cleanup() {
|
|
13
|
+
echo "๐งน Cleaning up test directories..."
|
|
14
|
+
rm -rf /tmp/testdriver-test-*
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
trap cleanup EXIT
|
|
18
|
+
|
|
19
|
+
# Test 1: Full init with all features
|
|
20
|
+
echo "1๏ธโฃ Testing full initialization (package.json, tests, workflow, npm install)..."
|
|
21
|
+
cd /tmp
|
|
22
|
+
mkdir -p testdriver-test-init
|
|
23
|
+
cd testdriver-test-init
|
|
24
|
+
node /Users/ianjennings/Development/cli/bin/testdriverai.js init
|
|
25
|
+
echo ""
|
|
26
|
+
echo "โ
Files created:"
|
|
27
|
+
find . -type f -not -path "./node_modules/*" | sort
|
|
28
|
+
echo ""
|
|
29
|
+
echo "๐ package.json:"
|
|
30
|
+
cat package.json
|
|
31
|
+
echo ""
|
|
32
|
+
echo "๐ vitest.config.js:"
|
|
33
|
+
cat vitest.config.js
|
|
34
|
+
echo ""
|
|
35
|
+
echo "๐ GitHub workflow:"
|
|
36
|
+
cat .github/workflows/testdriver.yml
|
|
37
|
+
echo ""
|
|
38
|
+
echo "๐ First 15 lines of tests/example.test.js:"
|
|
39
|
+
head -15 tests/example.test.js
|
|
40
|
+
echo ""
|
|
41
|
+
echo "๐ฆ Installed dependencies:"
|
|
42
|
+
npm list --depth=0
|
|
43
|
+
echo ""
|
|
44
|
+
echo "---"
|
|
45
|
+
echo ""
|
|
46
|
+
|
|
47
|
+
# Test 2: Help command
|
|
48
|
+
echo "2๏ธโฃ Testing help command..."
|
|
49
|
+
node /Users/ianjennings/Development/cli/bin/testdriverai.js init --help
|
|
50
|
+
echo ""
|
|
51
|
+
echo "---"
|
|
52
|
+
echo ""
|
|
53
|
+
|
|
54
|
+
echo "โ
All tests passed!"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple test to verify prompt caching functionality
|
|
5
|
+
*
|
|
6
|
+
* This test demonstrates that:
|
|
7
|
+
* 1. First .prompt() call makes an API request and caches the YAML response
|
|
8
|
+
* 2. Second .prompt() call with the same prompt uses the cached YAML
|
|
9
|
+
* 3. Cache can be disabled with TD_NO_PROMPT_CACHE=true
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const TestDriver = require("./sdk.js");
|
|
13
|
+
const promptCache = require("./agent/lib/cache.js");
|
|
14
|
+
|
|
15
|
+
async function testPromptCache() {
|
|
16
|
+
console.log("Testing prompt caching functionality...\n");
|
|
17
|
+
|
|
18
|
+
// API key loaded automatically from .env
|
|
19
|
+
const client = new TestDriver({
|
|
20
|
+
os: "linux",
|
|
21
|
+
logging: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Connect to sandbox
|
|
26
|
+
console.log("Connecting to sandbox...");
|
|
27
|
+
await client.connect();
|
|
28
|
+
console.log("Connected!\n");
|
|
29
|
+
|
|
30
|
+
const testPrompt = "click the search button";
|
|
31
|
+
|
|
32
|
+
// Clear cache for this prompt to start fresh
|
|
33
|
+
const cachePath = promptCache.getCachePath(testPrompt);
|
|
34
|
+
console.log(`Cache path for "${testPrompt}": ${cachePath}\n`);
|
|
35
|
+
|
|
36
|
+
// Test 1: First call (should make API request and cache)
|
|
37
|
+
console.log("Test 1: First .ai() call (should cache the response)");
|
|
38
|
+
const stats1 = promptCache.getCacheStats();
|
|
39
|
+
console.log(`Cache before: ${stats1.count} files`);
|
|
40
|
+
|
|
41
|
+
await client.ai(testPrompt);
|
|
42
|
+
|
|
43
|
+
const stats2 = promptCache.getCacheStats();
|
|
44
|
+
console.log(`Cache after: ${stats2.count} files`);
|
|
45
|
+
console.log(
|
|
46
|
+
`Cache hit: ${promptCache.hasCache(testPrompt) ? "YES" : "NO"}\n`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Test 2: Second call (should use cache)
|
|
50
|
+
console.log(
|
|
51
|
+
"Test 2: Second .ai() call with same prompt (should use cache)",
|
|
52
|
+
);
|
|
53
|
+
console.log('Look for "(using cached response)" message above...\n');
|
|
54
|
+
await client.ai(testPrompt);
|
|
55
|
+
|
|
56
|
+
// Test 3: Third call with cache disabled (should make API call)
|
|
57
|
+
console.log(
|
|
58
|
+
"\nTest 3: Third .ai() call with cache=false (should bypass cache)",
|
|
59
|
+
);
|
|
60
|
+
await client.ai(testPrompt, false);
|
|
61
|
+
|
|
62
|
+
// Test 4: Show cache contents
|
|
63
|
+
console.log("\nTest 4: Cache contents");
|
|
64
|
+
const cachedYaml = promptCache.readCache(testPrompt);
|
|
65
|
+
if (cachedYaml) {
|
|
66
|
+
console.log("Cached YAML preview (first 500 chars):");
|
|
67
|
+
console.log(cachedYaml.substring(0, 500));
|
|
68
|
+
console.log("...\n");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Test 5: Cache statistics
|
|
72
|
+
console.log("Test 5: Cache statistics");
|
|
73
|
+
const finalStats = promptCache.getCacheStats();
|
|
74
|
+
console.log(`Total cached prompts: ${finalStats.count}`);
|
|
75
|
+
console.log(`Cache files:`, finalStats.files.slice(0, 5));
|
|
76
|
+
|
|
77
|
+
console.log("\nโ
Prompt caching test completed!");
|
|
78
|
+
console.log("\nTo disable caching, pass false: client.ai(prompt, false)");
|
|
79
|
+
console.log("To clear cache, delete .testdriver/.cache/*.yaml files");
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("โ Test failed:", error.message);
|
|
82
|
+
throw error;
|
|
83
|
+
} finally {
|
|
84
|
+
// Disconnect
|
|
85
|
+
await client.disconnect();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Run the test
|
|
90
|
+
if (require.main === module) {
|
|
91
|
+
testPromptCache().catch((error) => {
|
|
92
|
+
console.error("Test error:", error);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = testPromptCache;
|