@testdriverai/agent 7.8.0-canary.10
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,829 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as vscode from 'vscode';
|
|
7
|
+
import WebSocket from 'ws';
|
|
8
|
+
|
|
9
|
+
// Store active debugger panels by session ID
|
|
10
|
+
const debuggerPanels: Map<string, vscode.WebviewPanel> = new Map();
|
|
11
|
+
const websocketConnections: Map<string, WebSocket> = new Map();
|
|
12
|
+
let processedSessions: Set<string> = new Set(); // Track sessions we've already opened
|
|
13
|
+
|
|
14
|
+
// File watchers for .testdriver/.previews/ in each workspace folder
|
|
15
|
+
const previewWatchers: Map<string, vscode.FileSystemWatcher> = new Map();
|
|
16
|
+
|
|
17
|
+
// Local HTTP server for receiving session notifications from SDK
|
|
18
|
+
let httpServer: http.Server | undefined;
|
|
19
|
+
let serverPort: number | undefined;
|
|
20
|
+
|
|
21
|
+
// Path to the TestDriver directory (used for IPC between SDK and extension)
|
|
22
|
+
const SESSION_DIR = path.join(os.homedir(), '.testdriver');
|
|
23
|
+
const INSTANCES_DIR = path.join(SESSION_DIR, 'ide-instances');
|
|
24
|
+
|
|
25
|
+
// Generate a unique instance ID for this VS Code window
|
|
26
|
+
const instanceId = crypto.randomUUID();
|
|
27
|
+
|
|
28
|
+
interface SessionData {
|
|
29
|
+
sessionId?: string; // Unique identifier for this test session
|
|
30
|
+
debuggerUrl: string;
|
|
31
|
+
resolution: [number, number];
|
|
32
|
+
testFile?: string;
|
|
33
|
+
os?: string;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Instance registration data written to disk for SDK discovery
|
|
38
|
+
interface InstanceRegistration {
|
|
39
|
+
instanceId: string;
|
|
40
|
+
port: number;
|
|
41
|
+
workspacePaths: string[];
|
|
42
|
+
pid: number;
|
|
43
|
+
timestamp: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function activate(context: vscode.ExtensionContext) {
|
|
47
|
+
console.log('TestDriver.ai extension is now active');
|
|
48
|
+
|
|
49
|
+
// Ensure directories exist
|
|
50
|
+
if (!fs.existsSync(SESSION_DIR)) {
|
|
51
|
+
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
if (!fs.existsSync(INSTANCES_DIR)) {
|
|
54
|
+
fs.mkdirSync(INSTANCES_DIR, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Register commands
|
|
58
|
+
const openDebuggerCommand = vscode.commands.registerCommand(
|
|
59
|
+
'testdriverai.openDebugger',
|
|
60
|
+
() => openDebuggerPanel(context)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const closeDebuggerCommand = vscode.commands.registerCommand(
|
|
64
|
+
'testdriverai.closeDebugger',
|
|
65
|
+
() => closeAllDebuggerPanels()
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const installMcpCommand = vscode.commands.registerCommand(
|
|
69
|
+
'testdriverai.installMcp',
|
|
70
|
+
() => installMcpServer()
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
context.subscriptions.push(openDebuggerCommand, closeDebuggerCommand, installMcpCommand);
|
|
74
|
+
|
|
75
|
+
// Start local HTTP server for receiving session notifications
|
|
76
|
+
startHttpServer(context);
|
|
77
|
+
|
|
78
|
+
// Set up file watchers for .testdriver/.previews/ folders
|
|
79
|
+
setupPreviewWatchers(context);
|
|
80
|
+
|
|
81
|
+
// Listen for workspace folder changes to update watchers
|
|
82
|
+
context.subscriptions.push(
|
|
83
|
+
vscode.workspace.onDidChangeWorkspaceFolders(() => {
|
|
84
|
+
setupPreviewWatchers(context);
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Auto-install MCP on first activation
|
|
89
|
+
autoInstallMcp();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Start HTTP server to receive session notifications from SDK
|
|
93
|
+
function startHttpServer(context: vscode.ExtensionContext) {
|
|
94
|
+
httpServer = http.createServer((req, res) => {
|
|
95
|
+
// Enable CORS
|
|
96
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
97
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
98
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
99
|
+
|
|
100
|
+
if (req.method === 'OPTIONS') {
|
|
101
|
+
res.writeHead(200);
|
|
102
|
+
res.end();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (req.method === 'POST' && req.url === '/session') {
|
|
107
|
+
let body = '';
|
|
108
|
+
req.on('data', chunk => {
|
|
109
|
+
body += chunk.toString();
|
|
110
|
+
});
|
|
111
|
+
req.on('end', () => {
|
|
112
|
+
try {
|
|
113
|
+
const sessionData: SessionData = JSON.parse(body);
|
|
114
|
+
handleSessionNotification(context, sessionData);
|
|
115
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
116
|
+
res.end(JSON.stringify({ success: true }));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error parsing session data:', error);
|
|
119
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
120
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
} else if (req.method === 'GET' && req.url === '/health') {
|
|
124
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
125
|
+
res.end(JSON.stringify({ status: 'ok', instanceId }));
|
|
126
|
+
} else {
|
|
127
|
+
res.writeHead(404);
|
|
128
|
+
res.end();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Listen on a random available port
|
|
133
|
+
httpServer.listen(0, '127.0.0.1', () => {
|
|
134
|
+
const address = httpServer!.address();
|
|
135
|
+
if (address && typeof address === 'object') {
|
|
136
|
+
serverPort = address.port;
|
|
137
|
+
console.log(`TestDriver extension server listening on port ${serverPort}`);
|
|
138
|
+
|
|
139
|
+
// Register this instance so SDK can discover it
|
|
140
|
+
registerInstance();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
httpServer.on('error', (error) => {
|
|
145
|
+
console.error('HTTP server error:', error);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Register this VS Code instance for SDK discovery
|
|
150
|
+
function registerInstance() {
|
|
151
|
+
if (!serverPort) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
156
|
+
const workspacePaths = workspaceFolders
|
|
157
|
+
? workspaceFolders.map(f => f.uri.fsPath)
|
|
158
|
+
: [];
|
|
159
|
+
|
|
160
|
+
const registration: InstanceRegistration = {
|
|
161
|
+
instanceId,
|
|
162
|
+
port: serverPort,
|
|
163
|
+
workspacePaths,
|
|
164
|
+
pid: process.pid,
|
|
165
|
+
timestamp: Date.now()
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const registrationFile = path.join(INSTANCES_DIR, `${instanceId}.json`);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
fs.writeFileSync(registrationFile, JSON.stringify(registration, null, 2));
|
|
172
|
+
console.log(`Registered VS Code instance: ${registrationFile}`);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error('Failed to register instance:', error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Unregister this instance on deactivation
|
|
179
|
+
function unregisterInstance() {
|
|
180
|
+
const registrationFile = path.join(INSTANCES_DIR, `${instanceId}.json`);
|
|
181
|
+
try {
|
|
182
|
+
if (fs.existsSync(registrationFile)) {
|
|
183
|
+
fs.unlinkSync(registrationFile);
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
// Ignore cleanup errors
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle incoming session notification from SDK
|
|
191
|
+
function handleSessionNotification(context: vscode.ExtensionContext, sessionData: SessionData) {
|
|
192
|
+
// Generate session ID if not present
|
|
193
|
+
if (!sessionData.sessionId) {
|
|
194
|
+
sessionData.sessionId = `session-${Date.now()}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const config = vscode.workspace.getConfiguration('testdriverai');
|
|
198
|
+
const autoOpen = config.get<boolean>('autoOpenPreview', true);
|
|
199
|
+
|
|
200
|
+
if (autoOpen && !processedSessions.has(sessionData.sessionId)) {
|
|
201
|
+
processedSessions.add(sessionData.sessionId);
|
|
202
|
+
openDebuggerPanel(context, sessionData);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Set up file watchers for .testdriver/.previews/ in each workspace folder
|
|
207
|
+
function setupPreviewWatchers(context: vscode.ExtensionContext) {
|
|
208
|
+
console.log('[TestDriver] Setting up preview watchers...');
|
|
209
|
+
|
|
210
|
+
// Dispose existing watchers
|
|
211
|
+
for (const [_, watcher] of previewWatchers) {
|
|
212
|
+
watcher.dispose();
|
|
213
|
+
}
|
|
214
|
+
previewWatchers.clear();
|
|
215
|
+
|
|
216
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
217
|
+
if (!workspaceFolders) {
|
|
218
|
+
console.log('[TestDriver] No workspace folders found');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log(`[TestDriver] Found ${workspaceFolders.length} workspace folders`);
|
|
223
|
+
|
|
224
|
+
for (const folder of workspaceFolders) {
|
|
225
|
+
const previewsDir = path.join(folder.uri.fsPath, '.testdriver', '.previews');
|
|
226
|
+
console.log(`[TestDriver] Watching: ${previewsDir}`);
|
|
227
|
+
|
|
228
|
+
// Ensure the previews directory exists
|
|
229
|
+
if (!fs.existsSync(previewsDir)) {
|
|
230
|
+
try {
|
|
231
|
+
fs.mkdirSync(previewsDir, { recursive: true });
|
|
232
|
+
console.log(`[TestDriver] Created directory: ${previewsDir}`);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error(`Failed to create previews directory: ${error}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Create a file watcher for this workspace's previews folder
|
|
240
|
+
const pattern = new vscode.RelativePattern(folder, '.testdriver/.previews/*.json');
|
|
241
|
+
const watcher = vscode.workspace.createFileSystemWatcher(pattern);
|
|
242
|
+
|
|
243
|
+
// Handle new preview files
|
|
244
|
+
watcher.onDidCreate((uri) => {
|
|
245
|
+
console.log(`[TestDriver] File created: ${uri.fsPath}`);
|
|
246
|
+
handlePreviewFile(context, uri);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Also handle file changes (in case file is created empty then written)
|
|
250
|
+
watcher.onDidChange((uri) => {
|
|
251
|
+
console.log(`[TestDriver] File changed: ${uri.fsPath}`);
|
|
252
|
+
handlePreviewFile(context, uri);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
previewWatchers.set(folder.uri.fsPath, watcher);
|
|
256
|
+
context.subscriptions.push(watcher);
|
|
257
|
+
|
|
258
|
+
// Clean up any stale preview files from previous sessions (don't open them)
|
|
259
|
+
cleanupStalePreviewFiles(previewsDir);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Clean up stale preview files from previous sessions (don't open them)
|
|
264
|
+
function cleanupStalePreviewFiles(previewsDir: string) {
|
|
265
|
+
try {
|
|
266
|
+
const files = fs.readdirSync(previewsDir);
|
|
267
|
+
for (const file of files) {
|
|
268
|
+
if (file.endsWith('.json')) {
|
|
269
|
+
const filePath = path.join(previewsDir, file);
|
|
270
|
+
try {
|
|
271
|
+
fs.unlinkSync(filePath);
|
|
272
|
+
console.log(`[TestDriver] Cleaned up stale preview file: ${filePath}`);
|
|
273
|
+
} catch {
|
|
274
|
+
// Ignore deletion errors
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
// Directory might not exist yet, that's fine
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Handle a preview file being created or changed
|
|
284
|
+
function handlePreviewFile(context: vscode.ExtensionContext, uri: vscode.Uri) {
|
|
285
|
+
try {
|
|
286
|
+
const content = fs.readFileSync(uri.fsPath, 'utf-8');
|
|
287
|
+
if (!content.trim()) {
|
|
288
|
+
// File is empty, wait for content
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const sessionData: SessionData = JSON.parse(content);
|
|
293
|
+
|
|
294
|
+
// Generate session ID if not present
|
|
295
|
+
if (!sessionData.sessionId) {
|
|
296
|
+
sessionData.sessionId = path.basename(uri.fsPath, '.json');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Check if we've already processed this session
|
|
300
|
+
if (processedSessions.has(sessionData.sessionId)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Use the existing session notification handler
|
|
305
|
+
handleSessionNotification(context, sessionData);
|
|
306
|
+
|
|
307
|
+
// Delete the preview file after processing
|
|
308
|
+
try {
|
|
309
|
+
fs.unlinkSync(uri.fsPath);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error(`Failed to delete preview file: ${error}`);
|
|
312
|
+
}
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error(`Error processing preview file ${uri.fsPath}:`, error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Helper to get test file name from path (just the filename, not full path)
|
|
319
|
+
function getTestFileName(testFile?: string): string {
|
|
320
|
+
if (!testFile) {
|
|
321
|
+
return 'TestDriver';
|
|
322
|
+
}
|
|
323
|
+
// Handle both forward and backslashes
|
|
324
|
+
return testFile.split('/').pop()?.split('\\').pop() || 'TestDriver';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Format the panel title to match debugger.html: [status] filename
|
|
328
|
+
function formatPanelTitle(status: string, testFile?: string): string {
|
|
329
|
+
const fileName = getTestFileName(testFile);
|
|
330
|
+
return `[${status}] ${fileName}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function openDebuggerPanel(context: vscode.ExtensionContext, sessionData?: SessionData) {
|
|
334
|
+
// Generate or use existing session ID
|
|
335
|
+
const sessionId = sessionData?.sessionId || `manual-${Date.now()}`;
|
|
336
|
+
|
|
337
|
+
// Check if we already have a panel for this session
|
|
338
|
+
const existingPanel = debuggerPanels.get(sessionId);
|
|
339
|
+
if (existingPanel) {
|
|
340
|
+
existingPanel.reveal(vscode.ViewColumn.Active);
|
|
341
|
+
// Update content if we have new session data
|
|
342
|
+
if (sessionData) {
|
|
343
|
+
updateDebuggerContent(existingPanel, sessionData, context, sessionId);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Determine the initial title
|
|
349
|
+
const initialTitle = sessionData
|
|
350
|
+
? formatPanelTitle('Loading', sessionData.testFile)
|
|
351
|
+
: 'TestDriver Live Preview';
|
|
352
|
+
|
|
353
|
+
// Create a new webview panel for this session
|
|
354
|
+
const panel = vscode.window.createWebviewPanel(
|
|
355
|
+
'testdriverDebugger',
|
|
356
|
+
initialTitle,
|
|
357
|
+
vscode.ViewColumn.Beside, // Open beside current editor to show multiple
|
|
358
|
+
{
|
|
359
|
+
enableScripts: true,
|
|
360
|
+
retainContextWhenHidden: true,
|
|
361
|
+
localResourceRoots: [
|
|
362
|
+
vscode.Uri.file(path.join(context.extensionPath, 'media')),
|
|
363
|
+
vscode.Uri.file(path.join(__dirname, '..', '..', 'debugger'))
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Store the panel
|
|
369
|
+
debuggerPanels.set(sessionId, panel);
|
|
370
|
+
|
|
371
|
+
// Set the webview icon
|
|
372
|
+
panel.iconPath = {
|
|
373
|
+
light: vscode.Uri.file(path.join(context.extensionPath, 'media', 'icon.png')),
|
|
374
|
+
dark: vscode.Uri.file(path.join(context.extensionPath, 'media', 'icon.png'))
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// Handle panel disposal
|
|
378
|
+
panel.onDidDispose(() => {
|
|
379
|
+
debuggerPanels.delete(sessionId);
|
|
380
|
+
disconnectWebSocket(sessionId);
|
|
381
|
+
processedSessions.delete(sessionId);
|
|
382
|
+
}, null, context.subscriptions);
|
|
383
|
+
|
|
384
|
+
// Update content
|
|
385
|
+
if (sessionData) {
|
|
386
|
+
updateDebuggerContent(panel, sessionData, context, sessionId);
|
|
387
|
+
} else {
|
|
388
|
+
// Show waiting state
|
|
389
|
+
panel.webview.html = getWaitingHtml();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function updateDebuggerContent(panel: vscode.WebviewPanel, sessionData: SessionData, context: vscode.ExtensionContext, sessionId: string) {
|
|
394
|
+
// Connect to the WebSocket server for live updates
|
|
395
|
+
connectToWebSocket(sessionData.debuggerUrl, panel, sessionId, sessionData.testFile);
|
|
396
|
+
|
|
397
|
+
// Build the data parameter for the debugger
|
|
398
|
+
const data = {
|
|
399
|
+
resolution: sessionData.resolution,
|
|
400
|
+
url: extractVncUrl(sessionData.debuggerUrl),
|
|
401
|
+
token: 'V3b8wG9',
|
|
402
|
+
testFile: sessionData.testFile || null,
|
|
403
|
+
os: sessionData.os || 'linux'
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const encodedData = Buffer.from(JSON.stringify(data)).toString('base64');
|
|
407
|
+
|
|
408
|
+
// Update the panel title to show it's running
|
|
409
|
+
panel.title = formatPanelTitle('Running', sessionData.testFile);
|
|
410
|
+
|
|
411
|
+
// Update the webview content with the debugger
|
|
412
|
+
panel.webview.html = getDebuggerHtml(sessionData.debuggerUrl, encodedData, panel.webview, context);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function extractVncUrl(debuggerUrl: string): string {
|
|
416
|
+
try {
|
|
417
|
+
const url = new URL(debuggerUrl);
|
|
418
|
+
const dataParam = url.searchParams.get('data');
|
|
419
|
+
if (dataParam) {
|
|
420
|
+
const data = JSON.parse(Buffer.from(dataParam, 'base64').toString());
|
|
421
|
+
return data.url || '';
|
|
422
|
+
}
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error('Error extracting VNC URL:', error);
|
|
425
|
+
}
|
|
426
|
+
return '';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function connectToWebSocket(debuggerUrl: string, panel: vscode.WebviewPanel, sessionId: string, testFile?: string) {
|
|
430
|
+
// Disconnect existing connection for this session
|
|
431
|
+
disconnectWebSocket(sessionId);
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
const url = new URL(debuggerUrl);
|
|
435
|
+
const wsUrl = `ws://${url.host}`;
|
|
436
|
+
|
|
437
|
+
const ws = new WebSocket(wsUrl);
|
|
438
|
+
websocketConnections.set(sessionId, ws);
|
|
439
|
+
|
|
440
|
+
ws.on('open', () => {
|
|
441
|
+
console.log(`Connected to TestDriver debugger WebSocket for session: ${sessionId}`);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
ws.on('message', (data: Buffer) => {
|
|
445
|
+
try {
|
|
446
|
+
const message = JSON.parse(data.toString());
|
|
447
|
+
// Forward events to the webview
|
|
448
|
+
panel.webview.postMessage(message);
|
|
449
|
+
|
|
450
|
+
// Update panel title based on test events (matching debugger.html behavior)
|
|
451
|
+
if (message.event) {
|
|
452
|
+
switch (message.event) {
|
|
453
|
+
case 'test:start':
|
|
454
|
+
panel.title = formatPanelTitle('Running', testFile);
|
|
455
|
+
break;
|
|
456
|
+
case 'test:stop':
|
|
457
|
+
panel.title = formatPanelTitle('Stopped', testFile);
|
|
458
|
+
break;
|
|
459
|
+
case 'test:success':
|
|
460
|
+
panel.title = formatPanelTitle('Passed', testFile);
|
|
461
|
+
break;
|
|
462
|
+
case 'test:error':
|
|
463
|
+
panel.title = formatPanelTitle('Failed', testFile);
|
|
464
|
+
break;
|
|
465
|
+
case 'error:fatal':
|
|
466
|
+
case 'error:sdk':
|
|
467
|
+
panel.title = formatPanelTitle('Error', testFile);
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.error('Error parsing WebSocket message:', error);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
ws.on('close', () => {
|
|
477
|
+
console.log(`WebSocket connection closed for session: ${sessionId}`);
|
|
478
|
+
// Update panel title to show disconnected/done state
|
|
479
|
+
panel.title = formatPanelTitle('Done', testFile);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
ws.on('error', (error: Error) => {
|
|
483
|
+
console.error(`WebSocket error for session ${sessionId}:`, error);
|
|
484
|
+
});
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error('Error connecting to WebSocket:', error);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function disconnectWebSocket(sessionId: string) {
|
|
491
|
+
const ws = websocketConnections.get(sessionId);
|
|
492
|
+
if (ws) {
|
|
493
|
+
ws.close();
|
|
494
|
+
websocketConnections.delete(sessionId);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function closeAllDebuggerPanels() {
|
|
499
|
+
// Close all panels
|
|
500
|
+
for (const [sessionId, panel] of debuggerPanels) {
|
|
501
|
+
panel.dispose();
|
|
502
|
+
disconnectWebSocket(sessionId);
|
|
503
|
+
}
|
|
504
|
+
debuggerPanels.clear();
|
|
505
|
+
processedSessions.clear();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function getWaitingHtml(): string {
|
|
509
|
+
return `<!DOCTYPE html>
|
|
510
|
+
<html lang="en">
|
|
511
|
+
<head>
|
|
512
|
+
<meta charset="UTF-8">
|
|
513
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
514
|
+
<title>TestDriver Live Preview</title>
|
|
515
|
+
<style>
|
|
516
|
+
body {
|
|
517
|
+
background-color: #1e1e1e;
|
|
518
|
+
color: #cccccc;
|
|
519
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
520
|
+
display: flex;
|
|
521
|
+
flex-direction: column;
|
|
522
|
+
align-items: center;
|
|
523
|
+
justify-content: center;
|
|
524
|
+
height: 100vh;
|
|
525
|
+
margin: 0;
|
|
526
|
+
text-align: center;
|
|
527
|
+
}
|
|
528
|
+
.logo {
|
|
529
|
+
width: 120px;
|
|
530
|
+
height: 120px;
|
|
531
|
+
margin-bottom: 24px;
|
|
532
|
+
animation: pulse 2s ease-in-out infinite;
|
|
533
|
+
}
|
|
534
|
+
h1 {
|
|
535
|
+
font-size: 24px;
|
|
536
|
+
font-weight: 500;
|
|
537
|
+
margin-bottom: 16px;
|
|
538
|
+
}
|
|
539
|
+
p {
|
|
540
|
+
font-size: 14px;
|
|
541
|
+
color: #888;
|
|
542
|
+
max-width: 400px;
|
|
543
|
+
line-height: 1.6;
|
|
544
|
+
}
|
|
545
|
+
code {
|
|
546
|
+
background: #2d2d2d;
|
|
547
|
+
padding: 2px 6px;
|
|
548
|
+
border-radius: 4px;
|
|
549
|
+
font-family: monospace;
|
|
550
|
+
}
|
|
551
|
+
@keyframes pulse {
|
|
552
|
+
0%, 100% { opacity: 0.5; transform: scale(1); }
|
|
553
|
+
50% { opacity: 1; transform: scale(1.05); }
|
|
554
|
+
}
|
|
555
|
+
.spinner {
|
|
556
|
+
width: 40px;
|
|
557
|
+
height: 40px;
|
|
558
|
+
border: 3px solid #333;
|
|
559
|
+
border-top-color: #b0cf34;
|
|
560
|
+
border-radius: 50%;
|
|
561
|
+
animation: spin 1s linear infinite;
|
|
562
|
+
margin-bottom: 24px;
|
|
563
|
+
}
|
|
564
|
+
@keyframes spin {
|
|
565
|
+
to { transform: rotate(360deg); }
|
|
566
|
+
}
|
|
567
|
+
</style>
|
|
568
|
+
</head>
|
|
569
|
+
<body>
|
|
570
|
+
<div class="spinner"></div>
|
|
571
|
+
<h1>Waiting for TestDriver...</h1>
|
|
572
|
+
<p>
|
|
573
|
+
Run a test with <code>preview: "ide"</code> to see the live execution here.
|
|
574
|
+
</p>
|
|
575
|
+
<p style="margin-top: 16px;">
|
|
576
|
+
<code>const testdriver = TestDriver(context, { preview: "ide" });</code>
|
|
577
|
+
</p>
|
|
578
|
+
</body>
|
|
579
|
+
</html>`;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function getDebuggerHtml(debuggerUrl: string, encodedData: string, webview: vscode.Webview, context: vscode.ExtensionContext): string {
|
|
583
|
+
// We'll embed the debugger in an iframe pointing to the local server
|
|
584
|
+
// The debugger server must be running for this to work
|
|
585
|
+
// Parse the URL properly to handle existing query parameters
|
|
586
|
+
const url = new URL(debuggerUrl);
|
|
587
|
+
url.searchParams.set('data', encodedData);
|
|
588
|
+
const fullUrl = url.toString();
|
|
589
|
+
|
|
590
|
+
return `<!DOCTYPE html>
|
|
591
|
+
<html lang="en">
|
|
592
|
+
<head>
|
|
593
|
+
<meta charset="UTF-8">
|
|
594
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
595
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'; script-src 'unsafe-inline';">
|
|
596
|
+
<title>TestDriver Live Preview</title>
|
|
597
|
+
<style>
|
|
598
|
+
html, body {
|
|
599
|
+
margin: 0;
|
|
600
|
+
padding: 0;
|
|
601
|
+
width: 100%;
|
|
602
|
+
height: 100%;
|
|
603
|
+
overflow: hidden;
|
|
604
|
+
background-color: #1e1e1e;
|
|
605
|
+
}
|
|
606
|
+
iframe {
|
|
607
|
+
width: 100%;
|
|
608
|
+
height: 100%;
|
|
609
|
+
border: none;
|
|
610
|
+
}
|
|
611
|
+
.error {
|
|
612
|
+
display: none;
|
|
613
|
+
flex-direction: column;
|
|
614
|
+
align-items: center;
|
|
615
|
+
justify-content: center;
|
|
616
|
+
height: 100%;
|
|
617
|
+
color: #cccccc;
|
|
618
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
619
|
+
text-align: center;
|
|
620
|
+
padding: 20px;
|
|
621
|
+
}
|
|
622
|
+
.error.visible {
|
|
623
|
+
display: flex;
|
|
624
|
+
}
|
|
625
|
+
.error h2 {
|
|
626
|
+
color: #f44336;
|
|
627
|
+
margin-bottom: 16px;
|
|
628
|
+
}
|
|
629
|
+
.error p {
|
|
630
|
+
color: #888;
|
|
631
|
+
max-width: 400px;
|
|
632
|
+
line-height: 1.6;
|
|
633
|
+
}
|
|
634
|
+
</style>
|
|
635
|
+
</head>
|
|
636
|
+
<body>
|
|
637
|
+
<iframe
|
|
638
|
+
id="debugger-frame"
|
|
639
|
+
src="${fullUrl}"
|
|
640
|
+
sandbox="allow-scripts allow-same-origin"
|
|
641
|
+
></iframe>
|
|
642
|
+
<div class="error" id="error-message">
|
|
643
|
+
<h2>Connection Lost</h2>
|
|
644
|
+
<p>The TestDriver debugger server is no longer running. Start a new test to reconnect.</p>
|
|
645
|
+
</div>
|
|
646
|
+
<script>
|
|
647
|
+
const iframe = document.getElementById('debugger-frame');
|
|
648
|
+
const errorDiv = document.getElementById('error-message');
|
|
649
|
+
|
|
650
|
+
iframe.onerror = function() {
|
|
651
|
+
iframe.style.display = 'none';
|
|
652
|
+
errorDiv.classList.add('visible');
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// Listen for messages from the extension
|
|
656
|
+
window.addEventListener('message', event => {
|
|
657
|
+
const message = event.data;
|
|
658
|
+
// Forward WebSocket events to the iframe if needed
|
|
659
|
+
if (iframe.contentWindow) {
|
|
660
|
+
iframe.contentWindow.postMessage(message, '*');
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
</script>
|
|
664
|
+
</body>
|
|
665
|
+
</html>`;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async function installMcpServer() {
|
|
669
|
+
// Get the workspace folder
|
|
670
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
671
|
+
if (!workspaceFolders || workspaceFolders.length === 0) {
|
|
672
|
+
vscode.window.showWarningMessage('Please open a folder before installing TestDriver MCP.');
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const workspaceRoot = workspaceFolders[0].uri.fsPath;
|
|
677
|
+
|
|
678
|
+
// Check for various MCP config locations
|
|
679
|
+
const mcpConfigPaths = [
|
|
680
|
+
path.join(workspaceRoot, '.vscode', 'mcp.json'),
|
|
681
|
+
path.join(workspaceRoot, '.cursor', 'mcp.json'),
|
|
682
|
+
path.join(os.homedir(), '.vscode', 'mcp.json'),
|
|
683
|
+
path.join(os.homedir(), '.cursor', 'mcp.json')
|
|
684
|
+
];
|
|
685
|
+
|
|
686
|
+
// Try to find existing config or create in workspace
|
|
687
|
+
let configPath = mcpConfigPaths.find(p => fs.existsSync(p));
|
|
688
|
+
|
|
689
|
+
if (!configPath) {
|
|
690
|
+
// Ask user where to install
|
|
691
|
+
const choice = await vscode.window.showQuickPick(
|
|
692
|
+
[
|
|
693
|
+
{ label: 'Workspace (.vscode/mcp.json)', value: mcpConfigPaths[0] },
|
|
694
|
+
{ label: 'Workspace (.cursor/mcp.json)', value: mcpConfigPaths[1] },
|
|
695
|
+
{ label: 'Global (~/.vscode/mcp.json)', value: mcpConfigPaths[2] },
|
|
696
|
+
{ label: 'Global (~/.cursor/mcp.json)', value: mcpConfigPaths[3] }
|
|
697
|
+
],
|
|
698
|
+
{ placeHolder: 'Where would you like to install the TestDriver MCP server?' }
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
if (!choice) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
configPath = choice.value;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Ensure directory exists
|
|
709
|
+
const configDir = path.dirname(configPath);
|
|
710
|
+
if (!fs.existsSync(configDir)) {
|
|
711
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Read existing config or create new
|
|
715
|
+
let config: { mcpServers?: Record<string, unknown> } = {};
|
|
716
|
+
if (fs.existsSync(configPath)) {
|
|
717
|
+
try {
|
|
718
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
719
|
+
} catch (error) {
|
|
720
|
+
vscode.window.showErrorMessage(`Error reading MCP config: ${error}`);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Initialize mcpServers if not present
|
|
726
|
+
if (!config.mcpServers) {
|
|
727
|
+
config.mcpServers = {};
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Check if TestDriver MCP is already configured
|
|
731
|
+
if (config.mcpServers['testdriver']) {
|
|
732
|
+
const overwrite = await vscode.window.showWarningMessage(
|
|
733
|
+
'TestDriver MCP is already configured. Overwrite?',
|
|
734
|
+
'Yes',
|
|
735
|
+
'No'
|
|
736
|
+
);
|
|
737
|
+
if (overwrite !== 'Yes') {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Add TestDriver MCP configuration
|
|
743
|
+
// Set TD_PREVIEW=ide so the live preview opens in IDE panel (VSCode, Cursor, etc.)
|
|
744
|
+
config.mcpServers['testdriver'] = {
|
|
745
|
+
command: 'npx',
|
|
746
|
+
args: ['-y', 'testdriverai', 'mcp'],
|
|
747
|
+
env: {
|
|
748
|
+
TD_API_KEY: '${env:TD_API_KEY}',
|
|
749
|
+
TD_PREVIEW: 'ide'
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// Write config
|
|
754
|
+
try {
|
|
755
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
756
|
+
vscode.window.showInformationMessage(
|
|
757
|
+
`TestDriver MCP installed successfully at ${configPath}. Don't forget to set your TD_API_KEY environment variable.`
|
|
758
|
+
);
|
|
759
|
+
} catch (error) {
|
|
760
|
+
vscode.window.showErrorMessage(`Error writing MCP config: ${error}`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
async function autoInstallMcp() {
|
|
765
|
+
// Check if MCP is already configured in common locations
|
|
766
|
+
const mcpConfigPaths = [
|
|
767
|
+
path.join(os.homedir(), '.vscode', 'mcp.json'),
|
|
768
|
+
path.join(os.homedir(), '.cursor', 'mcp.json')
|
|
769
|
+
];
|
|
770
|
+
|
|
771
|
+
// Check workspace configs if available
|
|
772
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
773
|
+
if (workspaceFolders) {
|
|
774
|
+
mcpConfigPaths.unshift(
|
|
775
|
+
path.join(workspaceFolders[0].uri.fsPath, '.vscode', 'mcp.json'),
|
|
776
|
+
path.join(workspaceFolders[0].uri.fsPath, '.cursor', 'mcp.json')
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Check if TestDriver MCP is already configured
|
|
781
|
+
for (const configPath of mcpConfigPaths) {
|
|
782
|
+
if (fs.existsSync(configPath)) {
|
|
783
|
+
try {
|
|
784
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
785
|
+
if (config.mcpServers?.['testdriver']) {
|
|
786
|
+
// Already configured
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
// Ignore parse errors
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Prompt user to install MCP
|
|
796
|
+
const install = await vscode.window.showInformationMessage(
|
|
797
|
+
'Would you like to install the TestDriver MCP server for AI-assisted test creation?',
|
|
798
|
+
'Install',
|
|
799
|
+
'Not Now',
|
|
800
|
+
'Never'
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
if (install === 'Install') {
|
|
804
|
+
await installMcpServer();
|
|
805
|
+
} else if (install === 'Never') {
|
|
806
|
+
// Store preference to not ask again
|
|
807
|
+
const config = vscode.workspace.getConfiguration('testdriverai');
|
|
808
|
+
await config.update('mcpPromptDismissed', true, vscode.ConfigurationTarget.Global);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export function deactivate() {
|
|
813
|
+
closeAllDebuggerPanels();
|
|
814
|
+
|
|
815
|
+
// Stop HTTP server
|
|
816
|
+
if (httpServer) {
|
|
817
|
+
httpServer.close();
|
|
818
|
+
httpServer = undefined;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Dispose preview file watchers
|
|
822
|
+
for (const [_, watcher] of previewWatchers) {
|
|
823
|
+
watcher.dispose();
|
|
824
|
+
}
|
|
825
|
+
previewWatchers.clear();
|
|
826
|
+
|
|
827
|
+
// Unregister this instance
|
|
828
|
+
unregisterInstance();
|
|
829
|
+
}
|