@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,575 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { execSync, spawn } = require("child_process");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run an npm install command with an animated progress bar
|
|
7
|
+
* @param {string} cmd - The command to run (e.g. "npm")
|
|
8
|
+
* @param {string[]} args - Command arguments
|
|
9
|
+
* @param {string} cwd - Working directory
|
|
10
|
+
* @param {string} label - Label to show (e.g. "vitest testdriverai")
|
|
11
|
+
* @returns {Promise<void>}
|
|
12
|
+
*/
|
|
13
|
+
function runInstall(cmd, args, cwd, label) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const child = spawn(cmd, args, {
|
|
16
|
+
cwd,
|
|
17
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
+
shell: process.platform === "win32",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
22
|
+
const barWidth = 20;
|
|
23
|
+
let frame = 0;
|
|
24
|
+
let status = "resolving";
|
|
25
|
+
let filled = 0;
|
|
26
|
+
|
|
27
|
+
// Parse npm stderr for progress hints
|
|
28
|
+
const handleData = (data) => {
|
|
29
|
+
const text = data.toString();
|
|
30
|
+
if (text.includes("idealTree")) {
|
|
31
|
+
status = "resolving packages";
|
|
32
|
+
filled = Math.max(filled, 3);
|
|
33
|
+
} else if (text.includes("reify:")) {
|
|
34
|
+
status = "installing";
|
|
35
|
+
filled = Math.max(filled, 8);
|
|
36
|
+
// Try to extract package name from reify output
|
|
37
|
+
const match = text.match(/reify:([^\s:]+)/);
|
|
38
|
+
if (match) {
|
|
39
|
+
status = `installing ${match[1]}`;
|
|
40
|
+
}
|
|
41
|
+
} else if (text.includes("timing")) {
|
|
42
|
+
filled = Math.max(filled, 14);
|
|
43
|
+
status = "finalizing";
|
|
44
|
+
} else if (text.includes("added")) {
|
|
45
|
+
filled = barWidth;
|
|
46
|
+
status = "done";
|
|
47
|
+
}
|
|
48
|
+
// Slowly increment to show activity
|
|
49
|
+
if (filled < barWidth - 2) {
|
|
50
|
+
filled = Math.min(filled + 1, barWidth - 2);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
child.stdout.on("data", handleData);
|
|
55
|
+
child.stderr.on("data", handleData);
|
|
56
|
+
|
|
57
|
+
const isTTY = process.stderr.isTTY;
|
|
58
|
+
|
|
59
|
+
const interval = setInterval(() => {
|
|
60
|
+
frame = (frame + 1) % spinnerFrames.length;
|
|
61
|
+
const spinner = spinnerFrames[frame];
|
|
62
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
63
|
+
const line = ` ${spinner} ${label} [${bar}] ${status}`;
|
|
64
|
+
if (isTTY) {
|
|
65
|
+
process.stderr.clearLine(0);
|
|
66
|
+
process.stderr.cursorTo(0);
|
|
67
|
+
process.stderr.write(line);
|
|
68
|
+
}
|
|
69
|
+
}, 80);
|
|
70
|
+
|
|
71
|
+
child.on("close", (code) => {
|
|
72
|
+
clearInterval(interval);
|
|
73
|
+
if (isTTY) {
|
|
74
|
+
process.stderr.clearLine(0);
|
|
75
|
+
process.stderr.cursorTo(0);
|
|
76
|
+
}
|
|
77
|
+
if (code === 0) {
|
|
78
|
+
resolve();
|
|
79
|
+
} else {
|
|
80
|
+
reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
child.on("error", (err) => {
|
|
85
|
+
clearInterval(interval);
|
|
86
|
+
if (isTTY) {
|
|
87
|
+
process.stderr.clearLine(0);
|
|
88
|
+
process.stderr.cursorTo(0);
|
|
89
|
+
}
|
|
90
|
+
reject(err);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Initialize a TestDriver project with all necessary files and configuration
|
|
97
|
+
* @param {Object} options - Initialization options
|
|
98
|
+
* @param {string} [options.targetDir] - Target directory (defaults to current working directory)
|
|
99
|
+
* @param {string} [options.apiKey] - TestDriver API key (will be saved to .env)
|
|
100
|
+
* @param {boolean} [options.skipInstall=false] - Skip npm install step
|
|
101
|
+
* @param {boolean} [options.interactive=false] - Whether to prompt for missing values (CLI mode)
|
|
102
|
+
* @param {function} [options.onProgress] - Callback for progress updates (receives message string)
|
|
103
|
+
* @returns {Promise<{success: boolean, results: string[], errors: string[]}>}
|
|
104
|
+
*/
|
|
105
|
+
async function initProject(options = {}) {
|
|
106
|
+
const targetDir = options.targetDir || process.cwd();
|
|
107
|
+
const results = [];
|
|
108
|
+
const errors = [];
|
|
109
|
+
|
|
110
|
+
// Helper to report progress in real-time if callback provided
|
|
111
|
+
const progress = (msg) => {
|
|
112
|
+
results.push(msg);
|
|
113
|
+
if (options.onProgress) {
|
|
114
|
+
options.onProgress(msg);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
// Create target directory if it doesn't exist
|
|
120
|
+
if (!fs.existsSync(targetDir)) {
|
|
121
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
122
|
+
progress(`✓ Created directory: ${targetDir}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 1. Setup package.json
|
|
126
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
127
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
128
|
+
const packageJson = {
|
|
129
|
+
name: path.basename(targetDir),
|
|
130
|
+
version: "1.0.0",
|
|
131
|
+
description: "TestDriver.ai test suite",
|
|
132
|
+
type: "module",
|
|
133
|
+
scripts: {
|
|
134
|
+
test: "vitest run",
|
|
135
|
+
"test:watch": "vitest",
|
|
136
|
+
"test:ui": "vitest --ui",
|
|
137
|
+
},
|
|
138
|
+
keywords: ["testdriver", "testing", "e2e"],
|
|
139
|
+
author: "",
|
|
140
|
+
license: "ISC",
|
|
141
|
+
engines: {
|
|
142
|
+
node: ">=20.19.0",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
146
|
+
progress("✓ Created package.json");
|
|
147
|
+
} else {
|
|
148
|
+
progress("⊘ package.json already exists, skipping");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 2. Create test directory and example files
|
|
152
|
+
const testDir = path.join(targetDir, "tests");
|
|
153
|
+
if (!fs.existsSync(testDir)) {
|
|
154
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Create login snippet file
|
|
158
|
+
const loginSnippetFile = path.join(testDir, "login.js");
|
|
159
|
+
if (!fs.existsSync(loginSnippetFile)) {
|
|
160
|
+
const loginSnippetContent = `/**
|
|
161
|
+
* Login snippet - reusable login function
|
|
162
|
+
*
|
|
163
|
+
* This demonstrates how to create reusable test snippets that can be
|
|
164
|
+
* imported and used across multiple test files.
|
|
165
|
+
*/
|
|
166
|
+
export async function login(testdriver) {
|
|
167
|
+
|
|
168
|
+
// The password is displayed on screen, have TestDriver extract it
|
|
169
|
+
const password = await testdriver.extract('the password');
|
|
170
|
+
|
|
171
|
+
// Find the username field
|
|
172
|
+
const usernameField = await testdriver.find(
|
|
173
|
+
'username input'
|
|
174
|
+
);
|
|
175
|
+
await usernameField.click();
|
|
176
|
+
|
|
177
|
+
// Type username
|
|
178
|
+
await testdriver.type('standard_user');
|
|
179
|
+
|
|
180
|
+
// Enter password form earlier
|
|
181
|
+
// Marked as secret so it's not logged or stored
|
|
182
|
+
await testdriver.pressKeys(['tab']);
|
|
183
|
+
await testdriver.type(password, { secret: true });
|
|
184
|
+
|
|
185
|
+
// Submit the form
|
|
186
|
+
await testdriver.find('submit button on the login form').click();
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
fs.writeFileSync(loginSnippetFile, loginSnippetContent);
|
|
190
|
+
progress("✓ Created login snippet: tests/login.js");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Create example test file
|
|
194
|
+
const testFile = path.join(testDir, "example.test.js");
|
|
195
|
+
if (!fs.existsSync(testFile)) {
|
|
196
|
+
const vitestContent = `import { test, expect } from 'vitest';
|
|
197
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
198
|
+
import { login } from './login.js';
|
|
199
|
+
|
|
200
|
+
test('should login and add item to cart', async (context) => {
|
|
201
|
+
|
|
202
|
+
// Create TestDriver instance - automatically connects to sandbox
|
|
203
|
+
const testdriver = TestDriver(context);
|
|
204
|
+
|
|
205
|
+
// Launch chrome and navigate to demo app
|
|
206
|
+
await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
|
|
207
|
+
|
|
208
|
+
// Use the login snippet to handle authentication
|
|
209
|
+
// This demonstrates how to reuse test logic across multiple tests
|
|
210
|
+
await login(testdriver);
|
|
211
|
+
|
|
212
|
+
// Add item to cart
|
|
213
|
+
const addToCartButton = await testdriver.find(
|
|
214
|
+
'add to cart button under TestDriver Hat'
|
|
215
|
+
);
|
|
216
|
+
await addToCartButton.click();
|
|
217
|
+
|
|
218
|
+
// Open cart
|
|
219
|
+
const cartButton = await testdriver.find(
|
|
220
|
+
'cart button in the top right corner'
|
|
221
|
+
);
|
|
222
|
+
await cartButton.click();
|
|
223
|
+
|
|
224
|
+
// Verify item in cart
|
|
225
|
+
const result = await testdriver.assert('There is an item in the cart');
|
|
226
|
+
expect(result).toBeTruthy();
|
|
227
|
+
|
|
228
|
+
});
|
|
229
|
+
`;
|
|
230
|
+
fs.writeFileSync(testFile, vitestContent);
|
|
231
|
+
progress("✓ Created test file: tests/example.test.js");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 3. Create vitest.config.js
|
|
235
|
+
const configFile = path.join(targetDir, "vitest.config.js");
|
|
236
|
+
if (!fs.existsSync(configFile)) {
|
|
237
|
+
const configContent = `import { defineConfig } from 'vitest/config';
|
|
238
|
+
import TestDriver from 'testdriverai/vitest';
|
|
239
|
+
|
|
240
|
+
// Note: dotenv is loaded automatically by the TestDriver SDK
|
|
241
|
+
export default defineConfig({
|
|
242
|
+
test: {
|
|
243
|
+
testTimeout: 300000,
|
|
244
|
+
hookTimeout: 300000,
|
|
245
|
+
reporters: [
|
|
246
|
+
'default',
|
|
247
|
+
TestDriver(),
|
|
248
|
+
],
|
|
249
|
+
setupFiles: ['testdriverai/vitest/setup'],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
`;
|
|
253
|
+
fs.writeFileSync(configFile, configContent);
|
|
254
|
+
progress("✓ Created vitest.config.js");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 4. Create/update .gitignore
|
|
258
|
+
const gitignorePath = path.join(targetDir, ".gitignore");
|
|
259
|
+
let gitignoreContent = "";
|
|
260
|
+
if (fs.existsSync(gitignorePath)) {
|
|
261
|
+
gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
|
|
262
|
+
if (!gitignoreContent.includes(".env")) {
|
|
263
|
+
const ignoresToAdd = [
|
|
264
|
+
"",
|
|
265
|
+
"# TestDriver.ai",
|
|
266
|
+
".env",
|
|
267
|
+
"node_modules/",
|
|
268
|
+
"test-results/",
|
|
269
|
+
"*.log",
|
|
270
|
+
];
|
|
271
|
+
const newContent = gitignoreContent.trim() + "\n" + ignoresToAdd.join("\n") + "\n";
|
|
272
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
273
|
+
progress("✓ Updated .gitignore");
|
|
274
|
+
} else {
|
|
275
|
+
progress("⊘ .env already in .gitignore");
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
const ignoresToAdd = [
|
|
279
|
+
"# TestDriver.ai",
|
|
280
|
+
".env",
|
|
281
|
+
"node_modules/",
|
|
282
|
+
"test-results/",
|
|
283
|
+
"*.log",
|
|
284
|
+
];
|
|
285
|
+
fs.writeFileSync(gitignorePath, ignoresToAdd.join("\n") + "\n");
|
|
286
|
+
progress("✓ Created .gitignore");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 5. Create GitHub Actions workflow
|
|
290
|
+
const workflowDir = path.join(targetDir, ".github", "workflows");
|
|
291
|
+
if (!fs.existsSync(workflowDir)) {
|
|
292
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const workflowFile = path.join(workflowDir, "testdriver.yml");
|
|
296
|
+
if (!fs.existsSync(workflowFile)) {
|
|
297
|
+
const workflowContent = `name: TestDriver.ai Tests
|
|
298
|
+
|
|
299
|
+
on:
|
|
300
|
+
push:
|
|
301
|
+
branches: [ main, master ]
|
|
302
|
+
pull_request:
|
|
303
|
+
branches: [ main, master ]
|
|
304
|
+
|
|
305
|
+
jobs:
|
|
306
|
+
test:
|
|
307
|
+
runs-on: ubuntu-latest
|
|
308
|
+
|
|
309
|
+
steps:
|
|
310
|
+
- uses: actions/checkout@v4
|
|
311
|
+
|
|
312
|
+
- name: Setup Node.js
|
|
313
|
+
uses: actions/setup-node@v4
|
|
314
|
+
with:
|
|
315
|
+
node-version: '20'
|
|
316
|
+
cache: 'npm'
|
|
317
|
+
|
|
318
|
+
- name: Install dependencies
|
|
319
|
+
run: npm ci
|
|
320
|
+
|
|
321
|
+
- name: Run TestDriver.ai tests
|
|
322
|
+
env:
|
|
323
|
+
TD_API_KEY: \${{ secrets.TD_API_KEY }}
|
|
324
|
+
run: npx vitest run
|
|
325
|
+
|
|
326
|
+
- name: Upload test results
|
|
327
|
+
if: always()
|
|
328
|
+
uses: actions/upload-artifact@v4
|
|
329
|
+
with:
|
|
330
|
+
name: test-results
|
|
331
|
+
path: test-results/
|
|
332
|
+
retention-days: 30
|
|
333
|
+
`;
|
|
334
|
+
fs.writeFileSync(workflowFile, workflowContent);
|
|
335
|
+
progress("✓ Created GitHub workflow: .github/workflows/testdriver.yml");
|
|
336
|
+
} else {
|
|
337
|
+
progress("⊘ GitHub workflow already exists");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 6. Setup MCP configuration
|
|
341
|
+
// When triggered from VS Code extension, create .vscode/mcp.json silently
|
|
342
|
+
// When triggered from CLI, use interactive add-mcp for user to select their MCP client
|
|
343
|
+
const isVscodeInit = process.env.TD_INIT_SOURCE === "vscode";
|
|
344
|
+
|
|
345
|
+
if (isVscodeInit) {
|
|
346
|
+
// VS Code extension: create .vscode/mcp.json directly
|
|
347
|
+
const vscodeDir = path.join(targetDir, ".vscode");
|
|
348
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
349
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const mcpConfigFile = path.join(vscodeDir, "mcp.json");
|
|
353
|
+
if (!fs.existsSync(mcpConfigFile)) {
|
|
354
|
+
const mcpConfig = {
|
|
355
|
+
inputs: [
|
|
356
|
+
{
|
|
357
|
+
type: "promptString",
|
|
358
|
+
id: "testdriver-api-key",
|
|
359
|
+
description: "TestDriver API Key From https://console.testdriver.ai/team",
|
|
360
|
+
password: true,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
servers: {
|
|
364
|
+
testdriver: {
|
|
365
|
+
command: "npx",
|
|
366
|
+
args: ["-p", "testdriverai", "testdriverai-mcp"],
|
|
367
|
+
env: {
|
|
368
|
+
TD_API_KEY: "${input:testdriver-api-key}",
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
fs.writeFileSync(mcpConfigFile, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
374
|
+
progress("✓ Created MCP config: .vscode/mcp.json");
|
|
375
|
+
} else {
|
|
376
|
+
progress("⊘ MCP config already exists");
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
// CLI: use add-mcp for interactive MCP client selection
|
|
380
|
+
progress("🔧 Setting up MCP integration...");
|
|
381
|
+
try {
|
|
382
|
+
const addMcpResult = require("child_process").spawnSync(
|
|
383
|
+
"npx",
|
|
384
|
+
[
|
|
385
|
+
"--yes",
|
|
386
|
+
"add-mcp",
|
|
387
|
+
"testdriver",
|
|
388
|
+
"--command",
|
|
389
|
+
"npx -p testdriverai testdriverai-mcp",
|
|
390
|
+
"--env",
|
|
391
|
+
"TD_API_KEY",
|
|
392
|
+
],
|
|
393
|
+
{
|
|
394
|
+
cwd: targetDir,
|
|
395
|
+
stdio: "inherit", // Pass through stdin/stdout for interactive prompts
|
|
396
|
+
shell: process.platform === "win32",
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
if (addMcpResult.status === 0) {
|
|
401
|
+
progress("✓ MCP configured via add-mcp");
|
|
402
|
+
} else if (addMcpResult.status !== null) {
|
|
403
|
+
progress("⚠ MCP setup skipped or failed - you can run 'npx add-mcp testdriver' later");
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
progress("⚠ Could not run add-mcp - you can run 'npx add-mcp testdriver' later");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 7. Create VSCode extensions recommendations
|
|
411
|
+
const vscodeDir = path.join(targetDir, ".vscode");
|
|
412
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
413
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
414
|
+
}
|
|
415
|
+
const extensionsFile = path.join(vscodeDir, "extensions.json");
|
|
416
|
+
if (!fs.existsSync(extensionsFile)) {
|
|
417
|
+
const extensionsConfig = {
|
|
418
|
+
recommendations: [
|
|
419
|
+
"vitest.explorer",
|
|
420
|
+
],
|
|
421
|
+
};
|
|
422
|
+
fs.writeFileSync(extensionsFile, JSON.stringify(extensionsConfig, null, 2) + "\n");
|
|
423
|
+
progress("✓ Created extensions config: .vscode/extensions.json");
|
|
424
|
+
} else {
|
|
425
|
+
progress("⊘ Extensions config already exists");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// 8. Copy TestDriver skills
|
|
429
|
+
const skillsDestDir = path.join(targetDir, ".github", "skills");
|
|
430
|
+
const possibleSkillsSources = [
|
|
431
|
+
path.join(targetDir, "node_modules", "testdriverai", "ai", "skills"),
|
|
432
|
+
path.join(__dirname, "..", "ai", "skills"),
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
let skillsSourceDir = null;
|
|
436
|
+
for (const source of possibleSkillsSources) {
|
|
437
|
+
if (fs.existsSync(source)) {
|
|
438
|
+
skillsSourceDir = source;
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (skillsSourceDir) {
|
|
444
|
+
if (!fs.existsSync(skillsDestDir)) {
|
|
445
|
+
fs.mkdirSync(skillsDestDir, { recursive: true });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const skillDirs = fs.readdirSync(skillsSourceDir);
|
|
449
|
+
let copiedCount = 0;
|
|
450
|
+
|
|
451
|
+
for (const skillDir of skillDirs) {
|
|
452
|
+
const sourcePath = path.join(skillsSourceDir, skillDir);
|
|
453
|
+
const destPath = path.join(skillsDestDir, skillDir);
|
|
454
|
+
|
|
455
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
456
|
+
if (!fs.existsSync(destPath)) {
|
|
457
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const skillFile = path.join(sourcePath, "SKILL.md");
|
|
461
|
+
if (fs.existsSync(skillFile)) {
|
|
462
|
+
fs.copyFileSync(skillFile, path.join(destPath, "SKILL.md"));
|
|
463
|
+
copiedCount++;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (copiedCount > 0) {
|
|
469
|
+
progress(`✓ Copied ${copiedCount} skills to .github/skills/`);
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
progress("⚠ Skills directory not found (will be available after npm install)");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 9. Copy TestDriver agents
|
|
476
|
+
const agentsDestDir = path.join(targetDir, ".github", "agents");
|
|
477
|
+
const possibleAgentsSources = [
|
|
478
|
+
path.join(targetDir, "node_modules", "testdriverai", "ai", "agents"),
|
|
479
|
+
path.join(__dirname, "..", "ai", "agents"),
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
let agentsSourceDir = null;
|
|
483
|
+
for (const source of possibleAgentsSources) {
|
|
484
|
+
if (fs.existsSync(source)) {
|
|
485
|
+
agentsSourceDir = source;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (agentsSourceDir) {
|
|
491
|
+
if (!fs.existsSync(agentsDestDir)) {
|
|
492
|
+
fs.mkdirSync(agentsDestDir, { recursive: true });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const agentFiles = fs.readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
|
|
496
|
+
let copiedCount = 0;
|
|
497
|
+
|
|
498
|
+
for (const agentFile of agentFiles) {
|
|
499
|
+
const sourcePath = path.join(agentsSourceDir, agentFile);
|
|
500
|
+
const agentName = agentFile.replace(".md", "");
|
|
501
|
+
const destPath = path.join(agentsDestDir, `${agentName}.agent.md`);
|
|
502
|
+
|
|
503
|
+
if (!fs.existsSync(destPath)) {
|
|
504
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
505
|
+
copiedCount++;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (copiedCount > 0) {
|
|
510
|
+
progress(`✓ Copied ${copiedCount} agent(s) to .github/agents/`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Also set testdriver.md as copilot-instructions.md if it doesn't already exist
|
|
514
|
+
const copilotInstructionsPath = path.join(targetDir, ".github", "copilot-instructions.md");
|
|
515
|
+
const testdriverAgentSource = path.join(agentsSourceDir, "testdriver.md");
|
|
516
|
+
if (!fs.existsSync(copilotInstructionsPath) && fs.existsSync(testdriverAgentSource)) {
|
|
517
|
+
fs.copyFileSync(testdriverAgentSource, copilotInstructionsPath);
|
|
518
|
+
progress("✓ Created .github/copilot-instructions.md");
|
|
519
|
+
} else if (fs.existsSync(copilotInstructionsPath)) {
|
|
520
|
+
progress("⊘ copilot-instructions.md already exists, skipping");
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
progress("⚠ Agents directory not found (will be available after npm install)");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// 10. Handle API key if provided
|
|
527
|
+
if (options.apiKey) {
|
|
528
|
+
const envPath = path.join(targetDir, ".env");
|
|
529
|
+
let envContent = "";
|
|
530
|
+
|
|
531
|
+
if (fs.existsSync(envPath)) {
|
|
532
|
+
envContent = fs.readFileSync(envPath, "utf8");
|
|
533
|
+
if (!envContent.includes("TD_API_KEY=")) {
|
|
534
|
+
envContent += "\n";
|
|
535
|
+
} else {
|
|
536
|
+
// Replace existing key
|
|
537
|
+
envContent = envContent.replace(/^TD_API_KEY=.*$/m, "");
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const newEnvContent = envContent.trim() + `\nTD_API_KEY=${options.apiKey}\n`;
|
|
542
|
+
fs.writeFileSync(envPath, newEnvContent);
|
|
543
|
+
progress("✓ Saved API key to .env");
|
|
544
|
+
} else {
|
|
545
|
+
progress("ℹ No API key provided - add it to .env manually:");
|
|
546
|
+
progress(" TD_API_KEY=your_api_key");
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// 11. Install dependencies (unless skipped)
|
|
550
|
+
if (!options.skipInstall) {
|
|
551
|
+
progress("\n📦 Installing dependencies...");
|
|
552
|
+
try {
|
|
553
|
+
await runInstall("npm", ["install", "-D", "vitest", "testdriverai"], targetDir, "vitest testdriverai");
|
|
554
|
+
progress("✓ Installed vitest, testdriverai");
|
|
555
|
+
await runInstall("npm", ["install", "dotenv"], targetDir, "dotenv");
|
|
556
|
+
progress("✓ Installed dotenv");
|
|
557
|
+
} catch (error) {
|
|
558
|
+
errors.push("Failed to install dependencies. Run manually:");
|
|
559
|
+
errors.push(" npm install -D vitest testdriverai");
|
|
560
|
+
errors.push(" npm install dotenv");
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
progress("\nℹ Skipped dependency installation. Run manually:");
|
|
564
|
+
progress(" npm install -D vitest testdriverai");
|
|
565
|
+
progress(" npm install dotenv");
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return { success: true, results, errors };
|
|
569
|
+
} catch (error) {
|
|
570
|
+
errors.push(`Initialization failed: ${error.message}`);
|
|
571
|
+
return { success: false, results, errors };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
module.exports = { initProject };
|