@testdriverai/agent 7.8.0-test.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -0
- package/.env.example +4 -0
- package/.prettierignore +4 -0
- package/.prettierrc +1 -0
- package/CHANGELOG.md +953 -0
- package/README.md +81 -0
- package/agent/events.js +135 -0
- package/agent/index.js +2450 -0
- package/agent/interface.js +35 -0
- package/agent/lib/analytics.js +22 -0
- package/agent/lib/censorship.js +75 -0
- package/agent/lib/commander.js +246 -0
- package/agent/lib/commands.js +1684 -0
- package/agent/lib/config.js +60 -0
- package/agent/lib/generator.js +91 -0
- package/agent/lib/http.js +144 -0
- package/agent/lib/logger.js +56 -0
- package/agent/lib/outputs.js +29 -0
- package/agent/lib/parser.js +209 -0
- package/agent/lib/redraw.js +386 -0
- package/agent/lib/resources/cursor-2.png +0 -0
- package/agent/lib/sandbox.js +1104 -0
- package/agent/lib/sdk.js +633 -0
- package/agent/lib/session.js +25 -0
- package/agent/lib/source-mapper.js +342 -0
- package/agent/lib/subimage/index.js +77 -0
- package/agent/lib/subimage/opencv.js +69 -0
- package/agent/lib/system.js +204 -0
- package/agent/lib/theme.js +14 -0
- package/agent/lib/valid-version.js +21 -0
- package/agent/lib/validation.js +169 -0
- package/ai/.claude-plugin/plugin.json +9 -0
- package/ai/agents/testdriver.md +638 -0
- package/ai/skills/testdriver-ai/SKILL.md +204 -0
- package/ai/skills/testdriver-assert/SKILL.md +315 -0
- package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
- package/ai/skills/testdriver-cache/SKILL.md +221 -0
- package/ai/skills/testdriver-caching/SKILL.md +124 -0
- package/ai/skills/testdriver-captcha/SKILL.md +158 -0
- package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
- package/ai/skills/testdriver-click/SKILL.md +286 -0
- package/ai/skills/testdriver-client/SKILL.md +477 -0
- package/ai/skills/testdriver-cloud/SKILL.md +119 -0
- package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
- package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
- package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
- package/ai/skills/testdriver-device-config/SKILL.md +317 -0
- package/ai/skills/testdriver-double-click/SKILL.md +102 -0
- package/ai/skills/testdriver-elements/SKILL.md +605 -0
- package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
- package/ai/skills/testdriver-errors/SKILL.md +246 -0
- package/ai/skills/testdriver-events/SKILL.md +356 -0
- package/ai/skills/testdriver-examples/SKILL.md +7 -0
- package/ai/skills/testdriver-exec/SKILL.md +317 -0
- package/ai/skills/testdriver-find/SKILL.md +829 -0
- package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
- package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
- package/ai/skills/testdriver-hover/SKILL.md +278 -0
- package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
- package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
- package/ai/skills/testdriver-mcp/SKILL.md +7 -0
- package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
- package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
- package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
- package/ai/skills/testdriver-parse/SKILL.md +236 -0
- package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
- package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
- package/ai/skills/testdriver-provision/SKILL.md +331 -0
- package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
- package/ai/skills/testdriver-redraw/SKILL.md +214 -0
- package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
- package/ai/skills/testdriver-right-click/SKILL.md +123 -0
- package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
- package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
- package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
- package/ai/skills/testdriver-scroll/SKILL.md +335 -0
- package/ai/skills/testdriver-secrets/SKILL.md +115 -0
- package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
- package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
- package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
- package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
- package/ai/skills/testdriver-type/SKILL.md +357 -0
- package/ai/skills/testdriver-variables/SKILL.md +111 -0
- package/ai/skills/testdriver-wait/SKILL.md +50 -0
- package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
- package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
- package/bin/testdriverai.js +22 -0
- package/debugger/bg.png +0 -0
- package/debugger/icon.png +0 -0
- package/debugger/index.html +469 -0
- package/debugger/td.png +0 -0
- package/debugger/tray-buffered.png +0 -0
- package/debugger/tray.png +0 -0
- package/docs/GITHUB_COMMENTS.md +330 -0
- package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
- package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
- package/docs/TEST-GITHUB-COMMENTS.md +129 -0
- package/docs/_data/examples-manifest.json +177 -0
- package/docs/_data/examples-manifest.schema.json +41 -0
- package/docs/_scripts/extract-example-urls.js +165 -0
- package/docs/_scripts/generate-examples.js +560 -0
- package/docs/_scripts/generate-skills.js +154 -0
- package/docs/_scripts/link-replacer.js +164 -0
- package/docs/_scripts/upload-docs-to-openai.js +284 -0
- package/docs/changelog.mdx +161 -0
- package/docs/claude-mcp-plugin.mdx +160 -0
- package/docs/docs.json +442 -0
- package/docs/github-integration-setup.md +266 -0
- package/docs/guide/best-practices-polling.mdx +174 -0
- package/docs/images/content/account/newprojectsettings.png +0 -0
- package/docs/images/content/account/projectpage.png +0 -0
- package/docs/images/content/account/projectreplays.png +0 -0
- package/docs/images/content/account/team-manage.png +0 -0
- package/docs/images/content/account/teampage.png +0 -0
- package/docs/images/content/extension/cursor.svg +1 -0
- package/docs/images/content/extension/vscode.svg +57 -0
- package/docs/images/content/extension/windsurf.svg +3 -0
- package/docs/images/content/parse/output.png +0 -0
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/side-by-side.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/docs/images/content/vscode/v7-chat.png +0 -0
- package/docs/images/content/vscode/v7-choose-agent.png +0 -0
- package/docs/images/content/vscode/v7-full.png +0 -0
- package/docs/images/content/vscode/v7-onboarding.png +0 -0
- package/docs/images/content/vscode/vscode-2-assert.png +0 -0
- package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
- package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
- package/docs/images/content/vscode/vscode-file-creation.png +0 -0
- package/docs/images/content/vscode/vscode-install.png +0 -0
- package/docs/images/content/vscode/vscode-overview.png +0 -0
- package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
- package/docs/images/content/vscode/vscode-stopchat.png +0 -0
- package/docs/images/content/vscode/vscode-stoptest.png +0 -0
- package/docs/images/content/vscode/vscode-tdservice.png +0 -0
- package/docs/images/content/vscode/vscode-test-output.png +0 -0
- package/docs/images/content/vscode/vscode-testhistory.png +0 -0
- package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
- package/docs/images/content/vscode/vscode-testpane.png +0 -0
- package/docs/images/template/dark.png +0 -0
- package/docs/images/template/icon.png +0 -0
- package/docs/images/template/light.png +0 -0
- package/docs/snippets/calendar-link.mdx +4 -0
- package/docs/snippets/gitignore-warning.mdx +7 -0
- package/docs/snippets/lifecycle-warning.mdx +6 -0
- package/docs/snippets/test-prereqs.mdx +12 -0
- package/docs/snippets/tests/assert-replay.mdx +7 -0
- package/docs/snippets/tests/assert-yaml.mdx +8 -0
- package/docs/snippets/tests/exec-js-replay.mdx +7 -0
- package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
- package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
- package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
- package/docs/snippets/tests/hover-image-replay.mdx +7 -0
- package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
- package/docs/snippets/tests/hover-text-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
- package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
- package/docs/snippets/tests/match-image-replay.mdx +7 -0
- package/docs/snippets/tests/match-image-yaml.mdx +17 -0
- package/docs/snippets/tests/press-keys-replay.mdx +7 -0
- package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
- package/docs/snippets/tests/remember-replay.mdx +7 -0
- package/docs/snippets/tests/remember-yaml.mdx +28 -0
- package/docs/snippets/tests/scroll-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
- package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
- package/docs/snippets/tests/scroll-yaml.mdx +30 -0
- package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
- package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
- package/docs/snippets/tests/type-replay.mdx +7 -0
- package/docs/snippets/tests/type-yaml.mdx +28 -0
- package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-replay.mdx +7 -0
- package/docs/snippets/tests/wait-yaml.mdx +13 -0
- package/docs/styles.css +65 -0
- package/docs/v6/account/dashboard.mdx +16 -0
- package/docs/v6/account/enterprise.mdx +110 -0
- package/docs/v6/account/pricing.mdx +33 -0
- package/docs/v6/account/projects.mdx +33 -0
- package/docs/v6/account/team.mdx +35 -0
- package/docs/v6/action/ami.mdx +109 -0
- package/docs/v6/action/performance.mdx +105 -0
- package/docs/v6/action/secrets.mdx +93 -0
- package/docs/v6/apps/chrome-extensions.mdx +48 -0
- package/docs/v6/apps/desktop-apps.mdx +93 -0
- package/docs/v6/apps/mobile-apps.mdx +26 -0
- package/docs/v6/apps/static-websites.mdx +54 -0
- package/docs/v6/apps/tauri-apps.mdx +361 -0
- package/docs/v6/bugs/jira.mdx +232 -0
- package/docs/v6/cli/overview.mdx +66 -0
- package/docs/v6/commands/assert.mdx +45 -0
- package/docs/v6/commands/exec.mdx +276 -0
- package/docs/v6/commands/focus-application.mdx +44 -0
- package/docs/v6/commands/hover-image.mdx +69 -0
- package/docs/v6/commands/hover-text.mdx +47 -0
- package/docs/v6/commands/if.mdx +53 -0
- package/docs/v6/commands/match-image.mdx +67 -0
- package/docs/v6/commands/press-keys.mdx +87 -0
- package/docs/v6/commands/remember.mdx +49 -0
- package/docs/v6/commands/run.mdx +44 -0
- package/docs/v6/commands/scroll-until-image.mdx +66 -0
- package/docs/v6/commands/scroll-until-text.mdx +60 -0
- package/docs/v6/commands/scroll.mdx +69 -0
- package/docs/v6/commands/type.mdx +45 -0
- package/docs/v6/commands/wait-for-image.mdx +54 -0
- package/docs/v6/commands/wait-for-text.mdx +48 -0
- package/docs/v6/commands/wait.mdx +45 -0
- package/docs/v6/exporting/junit.mdx +218 -0
- package/docs/v6/exporting/playwright.mdx +197 -0
- package/docs/v6/features/auto-healing.mdx +144 -0
- package/docs/v6/features/generation.mdx +116 -0
- package/docs/v6/features/parallel-testing.mdx +151 -0
- package/docs/v6/features/reusable-snippets.mdx +131 -0
- package/docs/v6/features/selectorless.mdx +80 -0
- package/docs/v6/features/visual-assertions.mdx +139 -0
- package/docs/v6/getting-started/ci.mdx +146 -0
- package/docs/v6/getting-started/cli.mdx +91 -0
- package/docs/v6/getting-started/editing.mdx +100 -0
- package/docs/v6/getting-started/playwright.mdx +342 -0
- package/docs/v6/getting-started/running.mdx +48 -0
- package/docs/v6/getting-started/self-hosting.mdx +408 -0
- package/docs/v6/getting-started/vscode.mdx +88 -0
- package/docs/v6/guide/assertions.mdx +189 -0
- package/docs/v6/guide/authentication.mdx +136 -0
- package/docs/v6/guide/code.mdx +65 -0
- package/docs/v6/guide/dashcam.mdx +118 -0
- package/docs/v6/guide/environment-variables.mdx +26 -0
- package/docs/v6/guide/lifecycle.mdx +242 -0
- package/docs/v6/guide/locating.mdx +141 -0
- package/docs/v6/guide/protips.mdx +43 -0
- package/docs/v6/guide/variables.mdx +143 -0
- package/docs/v6/guide/waiting.mdx +130 -0
- package/docs/v6/importing/csv.mdx +196 -0
- package/docs/v6/importing/gherkin.mdx +143 -0
- package/docs/v6/importing/jira.mdx +164 -0
- package/docs/v6/importing/testrail.mdx +162 -0
- package/docs/v6/integrations/electron.mdx +146 -0
- package/docs/v6/integrations/netlify.mdx +100 -0
- package/docs/v6/integrations/vercel.mdx +125 -0
- package/docs/v6/interactive/explore.mdx +99 -0
- package/docs/v6/interactive/run.mdx +52 -0
- package/docs/v6/interactive/save.mdx +63 -0
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v6/overview/faq.mdx +162 -0
- package/docs/v6/overview/performance.mdx +52 -0
- package/docs/v6/overview/quickstart.mdx +137 -0
- package/docs/v6/overview/what-is-testdriver.mdx +85 -0
- package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
- package/docs/v6/scenarios/cookie-banner.mdx +32 -0
- package/docs/v6/scenarios/file-upload.mdx +33 -0
- package/docs/v6/scenarios/form-filling.mdx +32 -0
- package/docs/v6/scenarios/log-in.mdx +75 -0
- package/docs/v6/scenarios/pdf-generation.mdx +25 -0
- package/docs/v6/scenarios/spell-check.mdx +22 -0
- package/docs/v6/security/action.mdx +84 -0
- package/docs/v6/security/agent.mdx +73 -0
- package/docs/v6/security/platform.mdx +77 -0
- package/docs/v6/tutorials/advanced-test.mdx +81 -0
- package/docs/v6/tutorials/basic-test.mdx +45 -0
- package/docs/v7/_drafts/agents.mdx +843 -0
- package/docs/v7/_drafts/architecture.mdx +399 -0
- package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
- package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
- package/docs/v7/_drafts/best-practices.mdx +486 -0
- package/docs/v7/_drafts/caching-ai.mdx +215 -0
- package/docs/v7/_drafts/caching-selectors.mdx +424 -0
- package/docs/v7/_drafts/caching.mdx +366 -0
- package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
- package/docs/v7/_drafts/commands/assert.mdx +45 -0
- package/docs/v7/_drafts/commands/exec.mdx +276 -0
- package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
- package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
- package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
- package/docs/v7/_drafts/commands/if.mdx +53 -0
- package/docs/v7/_drafts/commands/match-image.mdx +67 -0
- package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
- package/docs/v7/_drafts/commands/remember.mdx +49 -0
- package/docs/v7/_drafts/commands/run.mdx +44 -0
- package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/_drafts/commands/scroll.mdx +69 -0
- package/docs/v7/_drafts/commands/type.mdx +45 -0
- package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
- package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
- package/docs/v7/_drafts/commands/wait.mdx +45 -0
- package/docs/v7/_drafts/configuration.mdx +378 -0
- package/docs/v7/_drafts/contributing.mdx +174 -0
- package/docs/v7/_drafts/core.mdx +458 -0
- package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
- package/docs/v7/_drafts/debugging.mdx +349 -0
- package/docs/v7/_drafts/error-handling.mdx +501 -0
- package/docs/v7/_drafts/faq.mdx +393 -0
- package/docs/v7/_drafts/hooks.mdx +360 -0
- package/docs/v7/_drafts/init-command.mdx +95 -0
- package/docs/v7/_drafts/installation.mdx +420 -0
- package/docs/v7/_drafts/migration.mdx +562 -0
- package/docs/v7/_drafts/observable.mdx +604 -0
- package/docs/v7/_drafts/playwright.mdx +342 -0
- package/docs/v7/_drafts/plugin-migration.mdx +220 -0
- package/docs/v7/_drafts/powerful.mdx +419 -0
- package/docs/v7/_drafts/presets.mdx +210 -0
- package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
- package/docs/v7/_drafts/prompt-cache.mdx +200 -0
- package/docs/v7/_drafts/provision.mdx +390 -0
- package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
- package/docs/v7/_drafts/readme.mdx +135 -0
- package/docs/v7/_drafts/reports.mdx +414 -0
- package/docs/v7/_drafts/scalable.mdx +763 -0
- package/docs/v7/_drafts/screenshot.mdx +155 -0
- package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
- package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
- package/docs/v7/_drafts/sdk-migration.mdx +474 -0
- package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
- package/docs/v7/_drafts/self-hosting.mdx +369 -0
- package/docs/v7/_drafts/test-recording.mdx +382 -0
- package/docs/v7/_drafts/troubleshooting.mdx +526 -0
- package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
- package/docs/v7/_drafts/vitest.mdx +535 -0
- package/docs/v7/_drafts/writing-tests.mdx +25 -0
- package/docs/v7/ai.mdx +205 -0
- package/docs/v7/assert.mdx +316 -0
- package/docs/v7/aws-setup.mdx +449 -0
- package/docs/v7/cache.mdx +223 -0
- package/docs/v7/caching.mdx +128 -0
- package/docs/v7/captcha.mdx +159 -0
- package/docs/v7/ci-cd.mdx +603 -0
- package/docs/v7/click.mdx +287 -0
- package/docs/v7/client.mdx +478 -0
- package/docs/v7/copilot/auto-healing.mdx +265 -0
- package/docs/v7/copilot/creating-tests.mdx +156 -0
- package/docs/v7/copilot/github.mdx +143 -0
- package/docs/v7/copilot/running-tests.mdx +149 -0
- package/docs/v7/copilot/setup.mdx +143 -0
- package/docs/v7/customizing-devices.mdx +319 -0
- package/docs/v7/dashcam.mdx +419 -0
- package/docs/v7/debugging-with-screenshots.mdx +402 -0
- package/docs/v7/device-config.mdx +317 -0
- package/docs/v7/double-click.mdx +102 -0
- package/docs/v7/elements.mdx +606 -0
- package/docs/v7/enterprise.mdx +9 -0
- package/docs/v7/errors.mdx +248 -0
- package/docs/v7/events.mdx +358 -0
- package/docs/v7/examples/ai.mdx +72 -0
- package/docs/v7/examples/assert.mdx +72 -0
- package/docs/v7/examples/captcha-api.mdx +92 -0
- package/docs/v7/examples/chrome-extension.mdx +132 -0
- package/docs/v7/examples/drag-and-drop.mdx +100 -0
- package/docs/v7/examples/element-not-found.mdx +67 -0
- package/docs/v7/examples/exec-output.mdx +85 -0
- package/docs/v7/examples/exec-pwsh.mdx +83 -0
- package/docs/v7/examples/focus-window.mdx +62 -0
- package/docs/v7/examples/hover-image.mdx +94 -0
- package/docs/v7/examples/hover-text.mdx +69 -0
- package/docs/v7/examples/installer.mdx +91 -0
- package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
- package/docs/v7/examples/match-image.mdx +96 -0
- package/docs/v7/examples/press-keys.mdx +92 -0
- package/docs/v7/examples/scroll-keyboard.mdx +79 -0
- package/docs/v7/examples/scroll-until-image.mdx +81 -0
- package/docs/v7/examples/scroll-until-text.mdx +109 -0
- package/docs/v7/examples/scroll.mdx +81 -0
- package/docs/v7/examples/type.mdx +92 -0
- package/docs/v7/examples/windows-installer.mdx +89 -0
- package/docs/v7/exec.mdx +318 -0
- package/docs/v7/find.mdx +830 -0
- package/docs/v7/focus-application.mdx +294 -0
- package/docs/v7/generating-tests.mdx +36 -0
- package/docs/v7/hosted.mdx +158 -0
- package/docs/v7/hover.mdx +279 -0
- package/docs/v7/locating-elements.mdx +71 -0
- package/docs/v7/making-assertions.mdx +32 -0
- package/docs/v7/mcp.mdx +9 -0
- package/docs/v7/mouse-down.mdx +161 -0
- package/docs/v7/mouse-up.mdx +164 -0
- package/docs/v7/parse.mdx +237 -0
- package/docs/v7/performing-actions.mdx +54 -0
- package/docs/v7/press-keys.mdx +349 -0
- package/docs/v7/provision.mdx +333 -0
- package/docs/v7/quickstart.mdx +173 -0
- package/docs/v7/redraw.mdx +216 -0
- package/docs/v7/reusable-code.mdx +249 -0
- package/docs/v7/right-click.mdx +123 -0
- package/docs/v7/running-tests.mdx +185 -0
- package/docs/v7/screenshot.mdx +249 -0
- package/docs/v7/screenshots.mdx +186 -0
- package/docs/v7/scroll.mdx +336 -0
- package/docs/v7/secrets.mdx +115 -0
- package/docs/v7/self-hosted.mdx +149 -0
- package/docs/v7/type.mdx +358 -0
- package/docs/v7/variables.mdx +111 -0
- package/docs/v7/wait.mdx +52 -0
- package/docs/v7/waiting-for-elements.mdx +90 -0
- package/docs/v7/what-is-testdriver.mdx +54 -0
- package/eslint.config.js +67 -0
- package/examples/ai.test.mjs +31 -0
- package/examples/assert.test.mjs +47 -0
- package/examples/chrome-extension.test.mjs +97 -0
- package/examples/config.mjs +5 -0
- package/examples/element-not-found.test.mjs +27 -0
- package/examples/exec-output.test.mjs +60 -0
- package/examples/exec-pwsh.test.mjs +58 -0
- package/examples/findall-coffee-icons.test.mjs +42 -0
- package/examples/focus-window.test.mjs +37 -0
- package/examples/formatted-logging.test.mjs +27 -0
- package/examples/hover-image.test.mjs +53 -0
- package/examples/hover-text-with-description.test.mjs +57 -0
- package/examples/hover-text.test.mjs +28 -0
- package/examples/installer.test.mjs +50 -0
- package/examples/launch-vscode-linux.test.mjs +55 -0
- package/examples/match-image.test.mjs +55 -0
- package/examples/parse.test.mjs +19 -0
- package/examples/press-keys.test.mjs +44 -0
- package/examples/prompt.test.mjs +34 -0
- package/examples/scroll-keyboard.test.mjs +38 -0
- package/examples/scroll-until-image.test.mjs +40 -0
- package/examples/scroll.test.mjs +42 -0
- package/examples/type.test.mjs +46 -0
- package/examples/windows-installer.test.mjs +54 -0
- package/index.js +2 -0
- package/interfaces/cli/commands/init.js +438 -0
- package/interfaces/cli/commands/setup.js +382 -0
- package/interfaces/cli/lib/base.js +285 -0
- package/interfaces/cli.js +20 -0
- package/interfaces/junit-reporter.js +290 -0
- package/interfaces/logger.js +388 -0
- package/interfaces/readline.js +234 -0
- package/interfaces/shared-test-state.mjs +64 -0
- package/interfaces/vitest-plugin.d.ts +115 -0
- package/interfaces/vitest-plugin.mjs +1698 -0
- package/lib/captcha/solver.js +358 -0
- package/lib/core/Dashcam.js +533 -0
- package/lib/core/index.d.ts +172 -0
- package/lib/core/index.js +12 -0
- package/lib/environments.json +18 -0
- package/lib/github-comment-formatter.js +263 -0
- package/lib/github-comment.mjs +452 -0
- package/lib/init-project.js +575 -0
- package/lib/presets/index.mjs +331 -0
- package/lib/resolve-channel.js +46 -0
- package/lib/sentry.js +417 -0
- package/lib/vitest/hooks.d.ts +57 -0
- package/lib/vitest/hooks.mjs +674 -0
- package/lib/vitest/setup-aws.mjs +247 -0
- package/lib/vitest/setup-self-hosted.mjs +151 -0
- package/lib/vitest/setup.mjs +46 -0
- package/manual/captcha-api.test.mjs +51 -0
- package/manual/drag-and-drop.test.mjs +59 -0
- package/manual/flake-diffthreshold-001.test.mjs +9 -0
- package/manual/flake-diffthreshold-01.test.mjs +9 -0
- package/manual/flake-diffthreshold-05.test.mjs +9 -0
- package/manual/flake-noredraw-cache.test.mjs +9 -0
- package/manual/flake-noredraw-nocache.test.mjs +9 -0
- package/manual/flake-redraw-cache.test.mjs +9 -0
- package/manual/flake-redraw-nocache.test.mjs +9 -0
- package/manual/flake-rocket-match.test.mjs +30 -0
- package/manual/flake-shared.mjs +51 -0
- package/manual/no-provision.test.mjs +31 -0
- package/manual/packer-hover-image.test.mjs +176 -0
- package/manual/scroll-until-text.test.mjs +68 -0
- package/manual/test-init-command.js +223 -0
- package/mcp-server/README.md +322 -0
- package/mcp-server/dist/codegen.d.ts +9 -0
- package/mcp-server/dist/codegen.js +165 -0
- package/mcp-server/dist/mcp-app.html +114 -0
- package/mcp-server/dist/package.json +1 -0
- package/mcp-server/dist/provision-types.d.ts +290 -0
- package/mcp-server/dist/provision-types.js +174 -0
- package/mcp-server/dist/server.d.ts +6 -0
- package/mcp-server/dist/server.mjs +1925 -0
- package/mcp-server/dist/session.d.ts +85 -0
- package/mcp-server/dist/session.js +152 -0
- package/mcp-server/mcp-app.html +28 -0
- package/mcp-server/mcp-config.example.json +19 -0
- package/mcp-server/package-lock.json +4027 -0
- package/mcp-server/package.json +31 -0
- package/mcp-server/src/codegen.ts +189 -0
- package/mcp-server/src/mcp-app.css +360 -0
- package/mcp-server/src/mcp-app.ts +547 -0
- package/mcp-server/src/provision-types.ts +209 -0
- package/mcp-server/src/server.ts +2391 -0
- package/mcp-server/src/session.ts +194 -0
- package/mcp-server/tsconfig.json +16 -0
- package/mcp-server/vite.config.ts +23 -0
- package/package.json +158 -0
- package/schema.json +1046 -0
- package/scripts/generate-skills.js +94 -0
- package/sdk-log-formatter.js +1157 -0
- package/sdk.d.ts +1486 -0
- package/sdk.js +4336 -0
- package/setup/aws/cloudformation.yaml +463 -0
- package/setup/aws/disable-defender.sh +42 -0
- package/setup/aws/install-dev-runner.sh +79 -0
- package/setup/aws/spawn-runner.sh +289 -0
- package/test/captcha-solver.test.mjs +152 -0
- package/test/chrome-remote-debugging.test.mjs +66 -0
- package/test/duckduckgo/experiment.test.mjs +28 -0
- package/test/duckduckgo/setup.test.mjs +29 -0
- package/test/manual/debug-locate-response.js +82 -0
- package/test/manual/reconnect-provision.test.mjs +49 -0
- package/test/manual/test-console-logs.test.mjs +42 -0
- package/test/manual/test-find-api.js +73 -0
- package/test/manual/test-init.sh +54 -0
- package/test/manual/test-prompt-cache.js +97 -0
- package/test/manual/test-provision-auth.mjs +22 -0
- package/test/manual/test-sandbox-render.js +29 -0
- package/test/manual/test-sdk-methods.js +15 -0
- package/test/manual/test-sdk-refactor.js +53 -0
- package/test/manual/test-stack-trace.mjs +57 -0
- package/test/manual/verify-element-api.js +89 -0
- package/test/manual/verify-types.js +0 -0
- package/test/manual-unawaited-promise.test.mjs +31 -0
- package/vitest.config.mjs +58 -0
- package/vitest.runner.config.mjs +33 -0
- package/vscode-extension/.vscodeignore +12 -0
- package/vscode-extension/README.md +94 -0
- package/vscode-extension/media/icon.png +0 -0
- package/vscode-extension/package-lock.json +4126 -0
- package/vscode-extension/package.json +86 -0
- package/vscode-extension/src/extension.ts +829 -0
- package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
- package/vscode-extension/tsconfig.json +16 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Comment Generator for TestDriver Test Results
|
|
3
|
+
*
|
|
4
|
+
* Creates beautifully formatted GitHub comments with:
|
|
5
|
+
* - Test results summary
|
|
6
|
+
* - Dashcam GIF replays
|
|
7
|
+
* - Exception details
|
|
8
|
+
* - Links to test runs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Octokit } from '@octokit/rest';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format test duration in human-readable format
|
|
15
|
+
* @param {number} ms - Duration in milliseconds
|
|
16
|
+
* @returns {string} Formatted duration
|
|
17
|
+
*/
|
|
18
|
+
function formatDuration(ms) {
|
|
19
|
+
if (ms < 1000) return `${ms}ms`;
|
|
20
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
|
|
21
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get status emoji for test result
|
|
26
|
+
* @param {string} status - Test status (passed, failed, skipped)
|
|
27
|
+
* @returns {string} Emoji
|
|
28
|
+
*/
|
|
29
|
+
function getStatusEmoji(status) {
|
|
30
|
+
switch (status) {
|
|
31
|
+
case 'passed': return '✅';
|
|
32
|
+
case 'failed': return '❌';
|
|
33
|
+
case 'skipped': return '⏭️';
|
|
34
|
+
case 'cancelled': return '🚫';
|
|
35
|
+
default: return '❓';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate markdown for test results table
|
|
41
|
+
* @param {Array} testCases - Array of test case objects
|
|
42
|
+
* @param {string} testRunUrl - Base URL for test run
|
|
43
|
+
* @returns {string} Markdown table
|
|
44
|
+
*/
|
|
45
|
+
function generateTestResultsTable(testCases, testRunUrl) {
|
|
46
|
+
if (!testCases || testCases.length === 0) {
|
|
47
|
+
return '_No test cases recorded_';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Filter out skipped tests
|
|
51
|
+
const nonSkippedTests = testCases.filter(test => test.status !== 'skipped');
|
|
52
|
+
|
|
53
|
+
if (nonSkippedTests.length === 0) {
|
|
54
|
+
return '_No test cases to display (all tests were skipped)_';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let table = '| Status | Test | File | Duration | Replay |\n';
|
|
58
|
+
table += '|--------|------|------|----------|--------|\n';
|
|
59
|
+
|
|
60
|
+
for (const test of nonSkippedTests) {
|
|
61
|
+
const status = getStatusEmoji(test.status);
|
|
62
|
+
const name = test.testName || 'Unknown';
|
|
63
|
+
const file = test.testFile || 'unknown';
|
|
64
|
+
const duration = formatDuration(test.duration || 0);
|
|
65
|
+
|
|
66
|
+
// Use test run context URL instead of direct replay URL
|
|
67
|
+
let replay = '-';
|
|
68
|
+
if (test.replayUrl) {
|
|
69
|
+
const linkUrl = (test.id && testRunUrl) ? `${testRunUrl}/${test.id}` : test.replayUrl;
|
|
70
|
+
|
|
71
|
+
// Extract replay ID and generate GIF URL
|
|
72
|
+
const replayId = extractReplayId(test.replayUrl);
|
|
73
|
+
if (replayId) {
|
|
74
|
+
const gifUrl = getReplayGifUrl(test.replayUrl, replayId);
|
|
75
|
+
// Embed GIF with link using HTML for height control
|
|
76
|
+
replay = `<a href="${linkUrl}"><img src="${gifUrl}" height="100" alt="Test replay" /></a>`;
|
|
77
|
+
} else {
|
|
78
|
+
// Fallback to text link if no GIF available
|
|
79
|
+
replay = `[🎥 View](${linkUrl})`;
|
|
80
|
+
}
|
|
81
|
+
} else if (test.id && testRunUrl) {
|
|
82
|
+
// Link without replay URL
|
|
83
|
+
replay = `[🎥 View](${testRunUrl}/${test.id})`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
table += `| ${status} | ${name} | \`${file}\` | ${duration} | ${replay} |\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return table;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generate markdown for exceptions/errors
|
|
94
|
+
* @param {Array} testCases - Array of test case objects with errors
|
|
95
|
+
* @param {string} testRunUrl - Base URL for test run
|
|
96
|
+
* @returns {string} Markdown with error details
|
|
97
|
+
*/
|
|
98
|
+
function generateExceptionsSection(testCases, testRunUrl) {
|
|
99
|
+
const failedTests = testCases.filter(t => t.status === 'failed' && t.errorMessage);
|
|
100
|
+
|
|
101
|
+
if (failedTests.length === 0) {
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let section = '\n## 🔴 Failures\n\n';
|
|
106
|
+
|
|
107
|
+
for (const test of failedTests) {
|
|
108
|
+
section += `### ${test.testName}\n\n`;
|
|
109
|
+
section += `**File:** \`${test.testFile}\`\n\n`;
|
|
110
|
+
|
|
111
|
+
// Use test run context URL instead of direct replay URL
|
|
112
|
+
if (test.id && testRunUrl) {
|
|
113
|
+
section += `**📹 [Watch Replay](${testRunUrl}/${test.id})**\n\n`;
|
|
114
|
+
} else if (test.replayUrl) {
|
|
115
|
+
section += `**📹 [Watch Replay](${test.replayUrl})**\n\n`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
section += '```\n';
|
|
119
|
+
section += test.errorMessage || 'Unknown error';
|
|
120
|
+
section += '\n```\n\n';
|
|
121
|
+
|
|
122
|
+
if (test.errorStack) {
|
|
123
|
+
section += '<details>\n';
|
|
124
|
+
section += '<summary>Stack Trace</summary>\n\n';
|
|
125
|
+
section += '```\n';
|
|
126
|
+
section += test.errorStack;
|
|
127
|
+
section += '\n```\n';
|
|
128
|
+
section += '</details>\n\n';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return section;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Generate markdown for dashcam replays section
|
|
137
|
+
* @param {Array} testCases - Array of test case objects
|
|
138
|
+
* @param {string} testRunUrl - Base URL for test run
|
|
139
|
+
* @returns {string} Markdown with replay embeds
|
|
140
|
+
*/
|
|
141
|
+
function generateReplaySection(testCases, testRunUrl) {
|
|
142
|
+
const testsWithReplays = testCases.filter(t => t.replayUrl);
|
|
143
|
+
|
|
144
|
+
if (testsWithReplays.length === 0) {
|
|
145
|
+
return '';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let section = '\n## 🎥 Dashcam Replays\n\n';
|
|
149
|
+
|
|
150
|
+
for (const test of testsWithReplays) {
|
|
151
|
+
section += `### ${test.testName}\n\n`;
|
|
152
|
+
|
|
153
|
+
// Determine the link URL - prefer test run context
|
|
154
|
+
let linkUrl = test.replayUrl;
|
|
155
|
+
if (test.id && testRunUrl) {
|
|
156
|
+
linkUrl = `${testRunUrl}/${test.id}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Extract replay ID from URL for GIF embed
|
|
160
|
+
const replayId = extractReplayId(test.replayUrl);
|
|
161
|
+
if (replayId) {
|
|
162
|
+
const gifUrl = getReplayGifUrl(test.replayUrl, replayId);
|
|
163
|
+
section += `[](${linkUrl})\n\n`;
|
|
164
|
+
section += `[🎬 View Full Replay](${linkUrl})\n\n`;
|
|
165
|
+
} else {
|
|
166
|
+
section += `[🎬 View Replay](${linkUrl})\n\n`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return section;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Extract replay ID from dashcam URL
|
|
175
|
+
* @param {string} url - Dashcam replay URL
|
|
176
|
+
* @returns {string|null} Replay ID or null
|
|
177
|
+
*/
|
|
178
|
+
function extractReplayId(url) {
|
|
179
|
+
if (!url) return null;
|
|
180
|
+
|
|
181
|
+
// Match pattern: /replay/{id} or /replay/{id}?params
|
|
182
|
+
const match = url.match(/\/replay\/([^?/#]+)/);
|
|
183
|
+
return match ? match[1] : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Extract share key from dashcam URL
|
|
188
|
+
* @param {string} url - Dashcam replay URL
|
|
189
|
+
* @returns {string|null} Share key or null
|
|
190
|
+
*/
|
|
191
|
+
function extractShareKey(url) {
|
|
192
|
+
if (!url) return null;
|
|
193
|
+
|
|
194
|
+
// Match pattern: ?share=KEY or &share=KEY
|
|
195
|
+
const match = url.match(/[?&]share=([^&#]+)/);
|
|
196
|
+
return match ? match[1] : null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get GIF URL for replay
|
|
201
|
+
* @param {string} replayUrl - Full replay URL
|
|
202
|
+
* @param {string} replayId - Replay ID
|
|
203
|
+
* @returns {string} GIF URL
|
|
204
|
+
*/
|
|
205
|
+
function getReplayGifUrl(replayUrl, replayId) {
|
|
206
|
+
// Determine the API base URL based on the replay URL
|
|
207
|
+
// Replay URLs use console domains; GIF endpoints live on the corresponding API domain
|
|
208
|
+
let apiBaseUrl;
|
|
209
|
+
|
|
210
|
+
if (replayUrl.includes('app.dashcam.io')) {
|
|
211
|
+
// Production dashcam uses Heroku API
|
|
212
|
+
apiBaseUrl = 'https://testdriverai-v6-c96fc597be11.herokuapp.com';
|
|
213
|
+
} else if (replayUrl.includes('localhost')) {
|
|
214
|
+
// Local development
|
|
215
|
+
apiBaseUrl = 'http://localhost:1337';
|
|
216
|
+
} else {
|
|
217
|
+
// Map console URLs → API URLs for all environments
|
|
218
|
+
// console-test.testdriver.ai → api-test.testdriver.ai
|
|
219
|
+
// console-canary.testdriver.ai → api-canary.testdriver.ai
|
|
220
|
+
// console.testdriver.ai → api.testdriver.ai
|
|
221
|
+
const consoleEnvMatch = replayUrl.match(/https:\/\/console-(test|canary)\.testdriver\.ai/);
|
|
222
|
+
if (consoleEnvMatch) {
|
|
223
|
+
apiBaseUrl = `https://api-${consoleEnvMatch[1]}.testdriver.ai`;
|
|
224
|
+
} else if (replayUrl.includes('console.testdriver.ai')) {
|
|
225
|
+
apiBaseUrl = 'https://api.testdriver.ai';
|
|
226
|
+
}
|
|
227
|
+
// Fly.io: map web app → API app
|
|
228
|
+
// pr-123-web.fly.dev → pr-123-api.fly.dev
|
|
229
|
+
// td-test-web.fly.dev → td-test-api.fly.dev
|
|
230
|
+
else {
|
|
231
|
+
const flyWebMatch = replayUrl.match(/https:\/\/([\w-]+)-web\.fly\.dev/);
|
|
232
|
+
if (flyWebMatch) {
|
|
233
|
+
apiBaseUrl = `https://${flyWebMatch[1]}-api.fly.dev`;
|
|
234
|
+
} else {
|
|
235
|
+
// Fallback: extract base URL from replay URL as-is
|
|
236
|
+
const urlObj = new URL(replayUrl);
|
|
237
|
+
apiBaseUrl = `${urlObj.protocol}//${urlObj.host}`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Extract share key if present
|
|
243
|
+
const shareKey = extractShareKey(replayUrl);
|
|
244
|
+
|
|
245
|
+
// Build GIF URL with shareKey parameter
|
|
246
|
+
let gifUrl = `${apiBaseUrl}/replay/${replayId}/gif`;
|
|
247
|
+
if (shareKey) {
|
|
248
|
+
gifUrl += `?shareKey=${shareKey}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return gifUrl;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Generate complete GitHub comment markdown
|
|
256
|
+
* @param {Object} testRunData - Test run data
|
|
257
|
+
* @param {Array} testCases - Array of test case objects
|
|
258
|
+
* @returns {string} Complete markdown comment
|
|
259
|
+
*/
|
|
260
|
+
export function generateGitHubComment(testRunData, testCases = []) {
|
|
261
|
+
const {
|
|
262
|
+
runId,
|
|
263
|
+
status,
|
|
264
|
+
totalTests = 0,
|
|
265
|
+
passedTests = 0,
|
|
266
|
+
failedTests = 0,
|
|
267
|
+
skippedTests = 0,
|
|
268
|
+
duration = 0,
|
|
269
|
+
testRunUrl,
|
|
270
|
+
platform = 'unknown',
|
|
271
|
+
branch = 'unknown',
|
|
272
|
+
commit = 'unknown',
|
|
273
|
+
} = testRunData;
|
|
274
|
+
|
|
275
|
+
// Header with overall status
|
|
276
|
+
const statusEmoji = getStatusEmoji(status);
|
|
277
|
+
const statusColor = status === 'passed' ? '🟢' : status === 'failed' ? '🔴' : '🟡';
|
|
278
|
+
|
|
279
|
+
let comment = `# ${statusColor} TestDriver Test Results\n\n`;
|
|
280
|
+
|
|
281
|
+
// Compact summary line
|
|
282
|
+
comment += `**Status:** ${statusEmoji} ${status.toUpperCase()}`;
|
|
283
|
+
comment += ` • **Duration:** ${formatDuration(duration)}`;
|
|
284
|
+
comment += ` • ${passedTests} passed`;
|
|
285
|
+
if (failedTests > 0) comment += `, ${failedTests} failed`;
|
|
286
|
+
// Only show skipped count if there are no passed or failed tests
|
|
287
|
+
if (skippedTests > 0 && passedTests === 0 && failedTests === 0) {
|
|
288
|
+
comment += `, ${skippedTests} skipped`;
|
|
289
|
+
}
|
|
290
|
+
comment += `\n\n`;
|
|
291
|
+
|
|
292
|
+
// Exceptions section (only if there are failures) - show first
|
|
293
|
+
comment += generateExceptionsSection(testCases, testRunUrl);
|
|
294
|
+
|
|
295
|
+
// Test results table (now includes embedded GIFs)
|
|
296
|
+
comment += '## 📝 Test Results\n\n';
|
|
297
|
+
comment += generateTestResultsTable(testCases, testRunUrl);
|
|
298
|
+
|
|
299
|
+
// Link to full test run (below table)
|
|
300
|
+
if (testRunUrl) {
|
|
301
|
+
comment += `\n[📋 View Full Test Run](${testRunUrl})\n`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Footer
|
|
305
|
+
comment += '\n---\n';
|
|
306
|
+
comment += `<sub>Generated by [TestDriver](https://testdriver.ai) • Run ID: \`${runId}\`</sub>\n`;
|
|
307
|
+
|
|
308
|
+
return comment;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Post comment to GitHub PR or commit
|
|
313
|
+
* @param {Object} options - Options
|
|
314
|
+
* @param {string} options.token - GitHub token
|
|
315
|
+
* @param {string} options.owner - Repository owner
|
|
316
|
+
* @param {string} options.repo - Repository name
|
|
317
|
+
* @param {number} [options.prNumber] - Pull request number (if commenting on PR)
|
|
318
|
+
* @param {string} [options.commitSha] - Commit SHA (if commenting on commit)
|
|
319
|
+
* @param {string} options.body - Comment body (markdown)
|
|
320
|
+
* @returns {Promise<Object>} GitHub API response
|
|
321
|
+
*/
|
|
322
|
+
export async function postGitHubComment(options) {
|
|
323
|
+
const { token, owner, repo, prNumber, commitSha, body } = options;
|
|
324
|
+
|
|
325
|
+
if (!token) {
|
|
326
|
+
throw new Error('GitHub token is required');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!owner || !repo) {
|
|
330
|
+
throw new Error('Repository owner and name are required');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!prNumber && !commitSha) {
|
|
334
|
+
throw new Error('Either prNumber or commitSha must be provided');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const octokit = new Octokit({ auth: token });
|
|
338
|
+
|
|
339
|
+
if (prNumber) {
|
|
340
|
+
// Comment on PR
|
|
341
|
+
const response = await octokit.rest.issues.createComment({
|
|
342
|
+
owner,
|
|
343
|
+
repo,
|
|
344
|
+
issue_number: prNumber,
|
|
345
|
+
body,
|
|
346
|
+
});
|
|
347
|
+
return response.data;
|
|
348
|
+
} else if (commitSha) {
|
|
349
|
+
// Comment on commit
|
|
350
|
+
const response = await octokit.rest.repos.createCommitComment({
|
|
351
|
+
owner,
|
|
352
|
+
repo,
|
|
353
|
+
commit_sha: commitSha,
|
|
354
|
+
body,
|
|
355
|
+
});
|
|
356
|
+
return response.data;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Update existing GitHub comment
|
|
362
|
+
* @param {Object} options - Options
|
|
363
|
+
* @param {string} options.token - GitHub token
|
|
364
|
+
* @param {string} options.owner - Repository owner
|
|
365
|
+
* @param {string} options.repo - Repository name
|
|
366
|
+
* @param {number} options.commentId - Comment ID to update
|
|
367
|
+
* @param {string} options.body - Updated comment body (markdown)
|
|
368
|
+
* @returns {Promise<Object>} GitHub API response
|
|
369
|
+
*/
|
|
370
|
+
export async function updateGitHubComment(options) {
|
|
371
|
+
const { token, owner, repo, commentId, body } = options;
|
|
372
|
+
|
|
373
|
+
if (!token || !owner || !repo || !commentId) {
|
|
374
|
+
throw new Error('Token, owner, repo, and commentId are required');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const octokit = new Octokit({ auth: token });
|
|
378
|
+
|
|
379
|
+
const response = await octokit.rest.issues.updateComment({
|
|
380
|
+
owner,
|
|
381
|
+
repo,
|
|
382
|
+
comment_id: commentId,
|
|
383
|
+
body,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
return response.data;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Find existing TestDriver comment on PR
|
|
391
|
+
* @param {Object} options - Options
|
|
392
|
+
* @param {string} options.token - GitHub token
|
|
393
|
+
* @param {string} options.owner - Repository owner
|
|
394
|
+
* @param {string} options.repo - Repository name
|
|
395
|
+
* @param {number} options.prNumber - Pull request number
|
|
396
|
+
* @returns {Promise<Object|null>} Existing comment or null
|
|
397
|
+
*/
|
|
398
|
+
export async function findExistingComment(options) {
|
|
399
|
+
const { token, owner, repo, prNumber } = options;
|
|
400
|
+
|
|
401
|
+
if (!token || !owner || !repo || !prNumber) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const octokit = new Octokit({ auth: token });
|
|
406
|
+
|
|
407
|
+
const comments = await octokit.rest.issues.listComments({
|
|
408
|
+
owner,
|
|
409
|
+
repo,
|
|
410
|
+
issue_number: prNumber,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Find comment with TestDriver signature
|
|
414
|
+
const existingComment = comments.data.find(comment =>
|
|
415
|
+
comment.body && comment.body.includes('Generated by [TestDriver]')
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
return existingComment || null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Post or update GitHub comment with test results
|
|
423
|
+
* Always keeps the comment at the end of the thread by deleting and recreating
|
|
424
|
+
* @param {Object} testRunData - Test run data
|
|
425
|
+
* @param {Array} testCases - Array of test case objects
|
|
426
|
+
* @param {Object} githubOptions - GitHub API options
|
|
427
|
+
* @returns {Promise<Object>} GitHub API response
|
|
428
|
+
*/
|
|
429
|
+
export async function postOrUpdateTestResults(testRunData, testCases, githubOptions) {
|
|
430
|
+
const commentBody = generateGitHubComment(testRunData, testCases);
|
|
431
|
+
|
|
432
|
+
// Try to find and delete existing comment to keep it at the end
|
|
433
|
+
if (githubOptions.prNumber) {
|
|
434
|
+
const existingComment = await findExistingComment(githubOptions);
|
|
435
|
+
|
|
436
|
+
if (existingComment) {
|
|
437
|
+
// Delete the old comment
|
|
438
|
+
const octokit = new Octokit({ auth: githubOptions.token });
|
|
439
|
+
await octokit.rest.issues.deleteComment({
|
|
440
|
+
owner: githubOptions.owner,
|
|
441
|
+
repo: githubOptions.repo,
|
|
442
|
+
comment_id: existingComment.id,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Always create a new comment (will be at the end of the thread)
|
|
448
|
+
return await postGitHubComment({
|
|
449
|
+
...githubOptions,
|
|
450
|
+
body: commentBody,
|
|
451
|
+
});
|
|
452
|
+
}
|