testdriverai 7.2.64 → 7.2.65
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/agent/index.js +4 -3
- package/agent/interface.js +11 -251
- package/agent/lib/debugger-server.js +2 -1
- package/agent/lib/logger.js +56 -0
- package/agent/lib/sandbox.js +6 -7
- package/ai/agents/test-writer.md +457 -0
- package/{docs/v7/ai.mdx → ai/skills/testdriver:ai/SKILL.md} +3 -5
- package/{docs/v7/assert.mdx → ai/skills/testdriver:assert/SKILL.md} +3 -4
- package/{docs/v7/aws-setup.mdx → ai/skills/testdriver:aws-setup/SKILL.md} +3 -4
- package/{docs/v7/caching.mdx → ai/skills/testdriver:caching/SKILL.md} +3 -7
- package/{docs/v7/captcha.mdx → ai/skills/testdriver:captcha/SKILL.md} +4 -5
- package/{docs/v7/ci-cd.mdx → ai/skills/testdriver:ci-cd/SKILL.md} +3 -4
- package/{docs/v7/click.mdx → ai/skills/testdriver:click/SKILL.md} +3 -4
- package/{docs/v7/client.mdx → ai/skills/testdriver:client/SKILL.md} +11 -5
- package/{docs/v7/cloud.mdx → ai/skills/testdriver:cloud/SKILL.md} +3 -4
- package/{docs/v7/customizing-devices.mdx → ai/skills/testdriver:customizing-devices/SKILL.md} +36 -26
- package/{docs/v7/dashcam.mdx → ai/skills/testdriver:dashcam/SKILL.md} +3 -4
- package/{docs/v7/device-config.mdx → ai/skills/testdriver:device-config/SKILL.md} +3 -3
- package/{docs/v7/double-click.mdx → ai/skills/testdriver:double-click/SKILL.md} +3 -3
- package/{docs/v7/elements.mdx → ai/skills/testdriver:elements/SKILL.md} +3 -4
- package/{docs/v7/enterprise.mdx → ai/skills/testdriver:enterprise/SKILL.md} +3 -5
- package/ai/skills/testdriver:examples/SKILL.md +7 -0
- package/{docs/v7/exec.mdx → ai/skills/testdriver:exec/SKILL.md} +3 -4
- package/{docs/v7/find.mdx → ai/skills/testdriver:find/SKILL.md} +81 -4
- package/{docs/v7/focus-application.mdx → ai/skills/testdriver:focus-application/SKILL.md} +3 -4
- package/{docs/v7/generating-tests.mdx → ai/skills/testdriver:generating-tests/SKILL.md} +3 -3
- package/{docs/v7/hover.mdx → ai/skills/testdriver:hover/SKILL.md} +3 -4
- package/{docs/v7/locating-elements.mdx → ai/skills/testdriver:locating-elements/SKILL.md} +3 -3
- package/{docs/v7/making-assertions.mdx → ai/skills/testdriver:making-assertions/SKILL.md} +3 -3
- package/ai/skills/testdriver:mcp-workflow/SKILL.md +410 -0
- package/{docs/v7/mouse-down.mdx → ai/skills/testdriver:mouse-down/SKILL.md} +3 -3
- package/{docs/v7/mouse-up.mdx → ai/skills/testdriver:mouse-up/SKILL.md} +3 -3
- package/{docs/v7/performing-actions.mdx → ai/skills/testdriver:performing-actions/SKILL.md} +3 -3
- package/{docs/v7/press-keys.mdx → ai/skills/testdriver:press-keys/SKILL.md} +3 -4
- package/{docs/v7/quickstart.mdx → ai/skills/testdriver:quickstart/SKILL.md} +3 -4
- package/{docs/v7/reusable-code.mdx → ai/skills/testdriver:reusable-code/SKILL.md} +3 -3
- package/{docs/v7/right-click.mdx → ai/skills/testdriver:right-click/SKILL.md} +3 -3
- package/{docs/v7/running-tests.mdx → ai/skills/testdriver:running-tests/SKILL.md} +3 -3
- package/{docs/v7/screenshot.mdx → ai/skills/testdriver:screenshot/SKILL.md} +3 -4
- package/{docs/v7/scroll.mdx → ai/skills/testdriver:scroll/SKILL.md} +3 -4
- package/{docs/v7/secrets.mdx → ai/skills/testdriver:secrets/SKILL.md} +3 -3
- package/{docs/v7/self-hosted.mdx → ai/skills/testdriver:self-hosted/SKILL.md} +3 -4
- package/ai/skills/testdriver:testdriver/SKILL.md +31 -0
- package/{docs/v7/type.mdx → ai/skills/testdriver:type/SKILL.md} +3 -4
- package/{docs/v7/variables.mdx → ai/skills/testdriver:variables/SKILL.md} +3 -3
- package/{docs/v7/waiting-for-elements.mdx → ai/skills/testdriver:waiting-for-elements/SKILL.md} +3 -3
- package/{docs/v7/what-is-testdriver.mdx → ai/skills/testdriver:what-is-testdriver/SKILL.md} +3 -3
- package/interfaces/cli/commands/init.js +278 -1
- package/interfaces/cli/commands/setup.js +382 -0
- package/interfaces/vitest-plugin.mjs +190 -122
- package/lib/sentry.js +4 -3
- package/lib/vitest/hooks.mjs +70 -16
- package/package.json +29 -9
- package/sdk.d.ts +29 -2
- package/sdk.js +1 -0
- package/.env.example +0 -4
- package/.github/workflows/acceptance-linux-scheduled.yaml +0 -45
- package/.github/workflows/acceptance-windows-scheduled.yaml +0 -54
- package/.github/workflows/acceptance.yaml +0 -87
- package/.github/workflows/publish.yaml +0 -68
- package/.github/workflows/test-init.yml +0 -145
- package/.github/workflows/testdriver.yml +0 -170
- package/.github/workflows/windows-self-hosted.yaml +0 -82
- package/.prettierignore +0 -4
- package/.prettierrc +0 -1
- package/CHANGELOG.md +0 -34
- package/agents.md +0 -455
- package/debugger/bg.png +0 -0
- package/debugger/icon.png +0 -0
- package/debugger/index.html +0 -797
- package/debugger/td.png +0 -0
- package/debugger/tray-buffered.png +0 -0
- package/debugger/tray.png +0 -0
- package/docs/GITHUB_COMMENTS.md +0 -330
- package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +0 -167
- package/docs/QUICK-START-GITHUB-COMMENTS.md +0 -84
- package/docs/TEST-GITHUB-COMMENTS.md +0 -129
- package/docs/_scripts/link-replacer.js +0 -164
- package/docs/_scripts/upload-docs-to-openai.js +0 -284
- package/docs/docs.json +0 -393
- package/docs/github-integration-setup.md +0 -266
- package/docs/guide/best-practices-polling.mdx +0 -154
- 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 +0 -1
- package/docs/images/content/extension/vscode.svg +0 -57
- package/docs/images/content/extension/windsurf.svg +0 -3
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/side-by-side.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/docs/images/content/vscode/vscode-2-assert.png +0 -0
- package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
- package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
- package/docs/images/content/vscode/vscode-file-creation.png +0 -0
- package/docs/images/content/vscode/vscode-install.png +0 -0
- package/docs/images/content/vscode/vscode-overview.png +0 -0
- package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
- package/docs/images/content/vscode/vscode-stopchat.png +0 -0
- package/docs/images/content/vscode/vscode-stoptest.png +0 -0
- package/docs/images/content/vscode/vscode-tdservice.png +0 -0
- package/docs/images/content/vscode/vscode-test-output.png +0 -0
- package/docs/images/content/vscode/vscode-testhistory.png +0 -0
- package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
- package/docs/images/content/vscode/vscode-testpane.png +0 -0
- package/docs/images/template/dark.png +0 -0
- package/docs/images/template/icon.png +0 -0
- package/docs/images/template/light.png +0 -0
- package/docs/snippets/calendar-link.mdx +0 -4
- package/docs/snippets/gitignore-warning.mdx +0 -7
- package/docs/snippets/lifecycle-warning.mdx +0 -6
- package/docs/snippets/test-prereqs.mdx +0 -12
- package/docs/snippets/tests/assert-replay.mdx +0 -7
- package/docs/snippets/tests/assert-yaml.mdx +0 -8
- package/docs/snippets/tests/exec-js-replay.mdx +0 -7
- package/docs/snippets/tests/exec-js-yaml.mdx +0 -32
- package/docs/snippets/tests/exec-shell-replay.mdx +0 -7
- package/docs/snippets/tests/exec-shell-yaml.mdx +0 -15
- package/docs/snippets/tests/hover-image-replay.mdx +0 -7
- package/docs/snippets/tests/hover-image-yaml.mdx +0 -17
- package/docs/snippets/tests/hover-text-replay.mdx +0 -7
- package/docs/snippets/tests/hover-text-with-description-replay.mdx +0 -7
- package/docs/snippets/tests/hover-text-with-description-yaml.mdx +0 -24
- package/docs/snippets/tests/hover-text-yaml.mdx +0 -14
- package/docs/snippets/tests/match-image-replay.mdx +0 -7
- package/docs/snippets/tests/match-image-yaml.mdx +0 -17
- package/docs/snippets/tests/press-keys-replay.mdx +0 -7
- package/docs/snippets/tests/press-keys-yaml.mdx +0 -36
- package/docs/snippets/tests/remember-replay.mdx +0 -7
- package/docs/snippets/tests/remember-yaml.mdx +0 -28
- package/docs/snippets/tests/scroll-replay.mdx +0 -7
- package/docs/snippets/tests/scroll-until-image-replay.mdx +0 -7
- package/docs/snippets/tests/scroll-until-image-yaml.mdx +0 -14
- package/docs/snippets/tests/scroll-until-text-replay.mdx +0 -7
- package/docs/snippets/tests/scroll-until-text-yaml.mdx +0 -17
- package/docs/snippets/tests/scroll-yaml.mdx +0 -30
- package/docs/snippets/tests/type-repeated-replay.mdx +0 -7
- package/docs/snippets/tests/type-repeated-yaml.mdx +0 -22
- package/docs/snippets/tests/type-replay.mdx +0 -7
- package/docs/snippets/tests/type-yaml.mdx +0 -28
- package/docs/snippets/tests/wait-for-image-replay.mdx +0 -7
- package/docs/snippets/tests/wait-for-image-yaml.mdx +0 -18
- package/docs/snippets/tests/wait-for-text-replay.mdx +0 -7
- package/docs/snippets/tests/wait-for-text-yaml.mdx +0 -18
- package/docs/snippets/tests/wait-replay.mdx +0 -7
- package/docs/snippets/tests/wait-yaml.mdx +0 -13
- package/docs/styles.css +0 -65
- package/docs/v6/account/dashboard.mdx +0 -16
- package/docs/v6/account/enterprise.mdx +0 -110
- package/docs/v6/account/pricing.mdx +0 -33
- package/docs/v6/account/projects.mdx +0 -33
- package/docs/v6/account/team.mdx +0 -35
- package/docs/v6/action/ami.mdx +0 -109
- package/docs/v6/action/performance.mdx +0 -105
- package/docs/v6/action/secrets.mdx +0 -93
- package/docs/v6/apps/chrome-extensions.mdx +0 -48
- package/docs/v6/apps/desktop-apps.mdx +0 -93
- package/docs/v6/apps/mobile-apps.mdx +0 -26
- package/docs/v6/apps/static-websites.mdx +0 -54
- package/docs/v6/apps/tauri-apps.mdx +0 -361
- package/docs/v6/bugs/jira.mdx +0 -232
- package/docs/v6/cli/overview.mdx +0 -66
- package/docs/v6/commands/assert.mdx +0 -45
- package/docs/v6/commands/exec.mdx +0 -282
- package/docs/v6/commands/focus-application.mdx +0 -44
- package/docs/v6/commands/hover-image.mdx +0 -69
- package/docs/v6/commands/hover-text.mdx +0 -47
- package/docs/v6/commands/if.mdx +0 -53
- package/docs/v6/commands/match-image.mdx +0 -67
- package/docs/v6/commands/press-keys.mdx +0 -87
- package/docs/v6/commands/remember.mdx +0 -49
- package/docs/v6/commands/run.mdx +0 -44
- package/docs/v6/commands/scroll-until-image.mdx +0 -66
- package/docs/v6/commands/scroll-until-text.mdx +0 -60
- package/docs/v6/commands/scroll.mdx +0 -69
- package/docs/v6/commands/type.mdx +0 -45
- package/docs/v6/commands/wait-for-image.mdx +0 -54
- package/docs/v6/commands/wait-for-text.mdx +0 -48
- package/docs/v6/commands/wait.mdx +0 -45
- package/docs/v6/exporting/junit.mdx +0 -218
- package/docs/v6/exporting/playwright.mdx +0 -197
- package/docs/v6/features/auto-healing.mdx +0 -144
- package/docs/v6/features/generation.mdx +0 -116
- package/docs/v6/features/parallel-testing.mdx +0 -151
- package/docs/v6/features/reusable-snippets.mdx +0 -131
- package/docs/v6/features/selectorless.mdx +0 -80
- package/docs/v6/features/visual-assertions.mdx +0 -139
- package/docs/v6/getting-started/ci.mdx +0 -146
- package/docs/v6/getting-started/cli.mdx +0 -91
- package/docs/v6/getting-started/editing.mdx +0 -100
- package/docs/v6/getting-started/playwright.mdx +0 -342
- package/docs/v6/getting-started/running.mdx +0 -48
- package/docs/v6/getting-started/self-hosting.mdx +0 -408
- package/docs/v6/getting-started/vscode.mdx +0 -89
- package/docs/v6/guide/assertions.mdx +0 -189
- package/docs/v6/guide/authentication.mdx +0 -136
- package/docs/v6/guide/code.mdx +0 -65
- package/docs/v6/guide/dashcam.mdx +0 -118
- package/docs/v6/guide/environment-variables.mdx +0 -26
- package/docs/v6/guide/lifecycle.mdx +0 -242
- package/docs/v6/guide/locating.mdx +0 -141
- package/docs/v6/guide/protips.mdx +0 -43
- package/docs/v6/guide/variables.mdx +0 -143
- package/docs/v6/guide/waiting.mdx +0 -130
- package/docs/v6/importing/csv.mdx +0 -196
- package/docs/v6/importing/gherkin.mdx +0 -143
- package/docs/v6/importing/jira.mdx +0 -164
- package/docs/v6/importing/testrail.mdx +0 -162
- package/docs/v6/integrations/electron.mdx +0 -146
- package/docs/v6/integrations/netlify.mdx +0 -100
- package/docs/v6/integrations/vercel.mdx +0 -125
- package/docs/v6/interactive/explore.mdx +0 -99
- package/docs/v6/interactive/run.mdx +0 -52
- package/docs/v6/interactive/save.mdx +0 -63
- package/docs/v6/overview/comparison.mdx +0 -101
- package/docs/v6/overview/faq.mdx +0 -162
- package/docs/v6/overview/performance.mdx +0 -52
- package/docs/v6/overview/quickstart.mdx +0 -137
- package/docs/v6/overview/what-is-testdriver.mdx +0 -85
- package/docs/v6/scenarios/ai-chatbot.mdx +0 -28
- package/docs/v6/scenarios/cookie-banner.mdx +0 -32
- package/docs/v6/scenarios/file-upload.mdx +0 -33
- package/docs/v6/scenarios/form-filling.mdx +0 -32
- package/docs/v6/scenarios/log-in.mdx +0 -75
- package/docs/v6/scenarios/pdf-generation.mdx +0 -25
- package/docs/v6/scenarios/spell-check.mdx +0 -22
- package/docs/v6/security/action.mdx +0 -84
- package/docs/v6/security/agent.mdx +0 -73
- package/docs/v6/security/platform.mdx +0 -77
- package/docs/v6/tutorials/advanced-test.mdx +0 -81
- package/docs/v6/tutorials/basic-test.mdx +0 -45
- package/docs/v7/_drafts/agents.mdx +0 -852
- package/docs/v7/_drafts/architecture.mdx +0 -399
- package/docs/v7/_drafts/auto-cache-key.mdx +0 -167
- package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +0 -100
- package/docs/v7/_drafts/best-practices.mdx +0 -486
- package/docs/v7/_drafts/caching-ai.mdx +0 -215
- package/docs/v7/_drafts/caching-selectors.mdx +0 -424
- package/docs/v7/_drafts/caching.mdx +0 -366
- package/docs/v7/_drafts/cli-to-sdk-migration.mdx +0 -425
- package/docs/v7/_drafts/commands/assert.mdx +0 -45
- package/docs/v7/_drafts/commands/exec.mdx +0 -282
- package/docs/v7/_drafts/commands/focus-application.mdx +0 -44
- package/docs/v7/_drafts/commands/hover-image.mdx +0 -69
- package/docs/v7/_drafts/commands/hover-text.mdx +0 -47
- package/docs/v7/_drafts/commands/if.mdx +0 -53
- package/docs/v7/_drafts/commands/match-image.mdx +0 -67
- package/docs/v7/_drafts/commands/press-keys.mdx +0 -87
- package/docs/v7/_drafts/commands/remember.mdx +0 -49
- package/docs/v7/_drafts/commands/run.mdx +0 -44
- package/docs/v7/_drafts/commands/scroll-until-image.mdx +0 -66
- package/docs/v7/_drafts/commands/scroll-until-text.mdx +0 -60
- package/docs/v7/_drafts/commands/scroll.mdx +0 -69
- package/docs/v7/_drafts/commands/type.mdx +0 -45
- package/docs/v7/_drafts/commands/wait-for-image.mdx +0 -54
- package/docs/v7/_drafts/commands/wait-for-text.mdx +0 -48
- package/docs/v7/_drafts/commands/wait.mdx +0 -45
- package/docs/v7/_drafts/configuration.mdx +0 -378
- package/docs/v7/_drafts/contributing.mdx +0 -174
- package/docs/v7/_drafts/core.mdx +0 -458
- package/docs/v7/_drafts/dashcam-title-feature.mdx +0 -89
- package/docs/v7/_drafts/debugging.mdx +0 -349
- package/docs/v7/_drafts/error-handling.mdx +0 -501
- package/docs/v7/_drafts/faq.mdx +0 -393
- package/docs/v7/_drafts/hooks.mdx +0 -360
- package/docs/v7/_drafts/init-command.mdx +0 -95
- package/docs/v7/_drafts/installation.mdx +0 -420
- package/docs/v7/_drafts/migration.mdx +0 -562
- package/docs/v7/_drafts/observable.mdx +0 -604
- package/docs/v7/_drafts/playwright.mdx +0 -342
- package/docs/v7/_drafts/plugin-migration.mdx +0 -220
- package/docs/v7/_drafts/powerful.mdx +0 -419
- package/docs/v7/_drafts/presets.mdx +0 -210
- package/docs/v7/_drafts/progressive-disclosure.mdx +0 -230
- package/docs/v7/_drafts/prompt-cache.mdx +0 -200
- package/docs/v7/_drafts/provision.mdx +0 -390
- package/docs/v7/_drafts/quick-start-test-recording.mdx +0 -214
- package/docs/v7/_drafts/readme.mdx +0 -135
- package/docs/v7/_drafts/reports.mdx +0 -414
- package/docs/v7/_drafts/scalable.mdx +0 -754
- package/docs/v7/_drafts/screenshot.mdx +0 -155
- package/docs/v7/_drafts/sdk-awesome-logs.mdx +0 -468
- package/docs/v7/_drafts/sdk-browser-rendering.mdx +0 -167
- package/docs/v7/_drafts/sdk-migration.mdx +0 -474
- package/docs/v7/_drafts/sdk-v7-complete.mdx +0 -345
- package/docs/v7/_drafts/self-hosting.mdx +0 -369
- package/docs/v7/_drafts/test-recording.mdx +0 -382
- package/docs/v7/_drafts/troubleshooting.mdx +0 -526
- package/docs/v7/_drafts/vitest-plugin.mdx +0 -477
- package/docs/v7/_drafts/vitest.mdx +0 -535
- package/docs/v7/_drafts/writing-tests.mdx +0 -25
- package/docs/v7/examples.mdx +0 -5
- package/eslint.config.js +0 -67
- package/examples/ai.test.mjs +0 -30
- package/examples/assert.test.mjs +0 -46
- package/examples/captcha-api.test.mjs +0 -50
- package/examples/chrome-extension.test.mjs +0 -94
- package/examples/drag-and-drop.test.mjs +0 -58
- package/examples/element-not-found.test.mjs +0 -26
- package/examples/exec-output.test.mjs +0 -59
- package/examples/exec-pwsh.test.mjs +0 -57
- package/examples/focus-window.test.mjs +0 -36
- package/examples/formatted-logging.test.mjs +0 -26
- package/examples/hover-image.test.mjs +0 -52
- package/examples/hover-text-with-description.test.mjs +0 -56
- package/examples/hover-text.test.mjs +0 -27
- package/examples/installer.test.mjs +0 -49
- package/examples/launch-vscode-linux.test.mjs +0 -54
- package/examples/match-image.test.mjs +0 -54
- package/examples/no-provision.test.mjs +0 -23
- package/examples/press-keys.test.mjs +0 -50
- package/examples/prompt.test.mjs +0 -33
- package/examples/scroll-keyboard.test.mjs +0 -37
- package/examples/scroll-until-image.test.mjs +0 -39
- package/examples/scroll-until-text.test.mjs +0 -67
- package/examples/scroll.test.mjs +0 -41
- package/examples/type.test.mjs +0 -45
- package/examples/windows-installer.test.mjs +0 -53
- package/interfaces/cli/commands/edit.js +0 -3
- package/interfaces/cli/commands/generate.js +0 -3
- package/interfaces/cli/commands/run.js +0 -3
- package/interfaces/cli/utils/factory.js +0 -71
- package/jsconfig.json +0 -26
- package/manual/test-init-command.js +0 -223
- package/sdk-log-formatter.js +0 -930
- package/setup/aws/cloudformation.yaml +0 -470
- package/setup/aws/spawn-runner.sh +0 -190
- package/test/api-resilience.test.mjs +0 -0
- package/test/captcha-solver.test.mjs +0 -70
- package/test/chrome-remote-debugging.test.mjs +0 -66
- package/test/manual/debug-locate-response.js +0 -82
- package/test/manual/reconnect-provision.test.mjs +0 -49
- package/test/manual/test-console-logs.test.mjs +0 -42
- package/test/manual/test-find-api.js +0 -73
- package/test/manual/test-init.sh +0 -54
- package/test/manual/test-prompt-cache.js +0 -96
- package/test/manual/test-provision-auth.mjs +0 -22
- package/test/manual/test-sandbox-render.js +0 -28
- package/test/manual/test-sdk-methods.js +0 -15
- package/test/manual/test-sdk-refactor.js +0 -53
- package/test/manual/test-stack-trace.mjs +0 -57
- package/test/manual/verify-element-api.js +0 -89
- package/test/manual/verify-types.js +0 -0
- package/test/manual-unawaited-promise.test.mjs +0 -31
- package/testdriver-plugin/skills/actions/SKILL.md +0 -93
- package/testdriver-plugin/skills/assertions/SKILL.md +0 -77
- package/testdriver-plugin/skills/caching/SKILL.md +0 -66
- package/testdriver-plugin/skills/creating-tests/SKILL.md +0 -104
- package/testdriver-plugin/skills/finding-elements/SKILL.md +0 -77
- package/testdriver-plugin/skills/github-actions/SKILL.md +0 -100
- package/testdriver-plugin/skills/running-tests/SKILL.md +0 -77
- package/testdriver-plugin/skills/secrets/SKILL.md +0 -87
- package/testdriver-plugin/skills/self-hosting/SKILL.md +0 -89
- package/testdriver-plugin/skills/setup/SKILL.md +0 -76
- package/testdriver-plugin/skills/variables/SKILL.md +0 -88
- package/testdriver-plugin/skills/waiting/SKILL.md +0 -72
- package/vitest.config.mjs +0 -29
|
@@ -19,21 +19,21 @@ const MINIMUM_VITEST_VERSION = 4;
|
|
|
19
19
|
*/
|
|
20
20
|
function checkVitestVersion() {
|
|
21
21
|
try {
|
|
22
|
-
const vitestPkg = require(
|
|
22
|
+
const vitestPkg = require("vitest/package.json");
|
|
23
23
|
const version = vitestPkg.version;
|
|
24
|
-
const major = parseInt(version.split(
|
|
25
|
-
|
|
24
|
+
const major = parseInt(version.split(".")[0], 10);
|
|
25
|
+
|
|
26
26
|
if (major < MINIMUM_VITEST_VERSION) {
|
|
27
27
|
throw new Error(
|
|
28
28
|
`TestDriver requires Vitest >= ${MINIMUM_VITEST_VERSION}.0.0, but found ${version}. ` +
|
|
29
|
-
|
|
29
|
+
`Please upgrade Vitest: npm install vitest@latest`,
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
32
|
} catch (err) {
|
|
33
|
-
if (err.code ===
|
|
33
|
+
if (err.code === "MODULE_NOT_FOUND") {
|
|
34
34
|
throw new Error(
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
"TestDriver requires Vitest to be installed. " +
|
|
36
|
+
"Please install it: npm install vitest@latest",
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
throw err;
|
|
@@ -50,7 +50,8 @@ checkVitestVersion();
|
|
|
50
50
|
* Set TD_LOG_LEVEL=debug for verbose output
|
|
51
51
|
*/
|
|
52
52
|
const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
53
|
-
const currentLogLevel =
|
|
53
|
+
const currentLogLevel =
|
|
54
|
+
LOG_LEVELS[process.env.TD_LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
|
|
54
55
|
|
|
55
56
|
const logger = {
|
|
56
57
|
debug: (...args) => {
|
|
@@ -187,7 +188,7 @@ export function getPluginState() {
|
|
|
187
188
|
export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
188
189
|
if (!apiKey) {
|
|
189
190
|
const error = new Error(
|
|
190
|
-
"TD_API_KEY is not configured. Get your API key at https://console.testdriver.ai/team"
|
|
191
|
+
"TD_API_KEY is not configured. Get your API key at https://console.testdriver.ai/team",
|
|
191
192
|
);
|
|
192
193
|
error.code = "MISSING_API_KEY";
|
|
193
194
|
error.isAuthError = true;
|
|
@@ -213,7 +214,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
|
213
214
|
// Network-level error (fetch failed entirely)
|
|
214
215
|
const networkError = new Error(
|
|
215
216
|
`Unable to reach TestDriver API at ${apiRoot}. ` +
|
|
216
|
-
|
|
217
|
+
"Check your internet connection and try again.",
|
|
217
218
|
);
|
|
218
219
|
networkError.code = "NETWORK_ERROR";
|
|
219
220
|
networkError.isNetworkError = true;
|
|
@@ -233,8 +234,8 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
|
233
234
|
if (response.status === 401) {
|
|
234
235
|
const authError = new Error(
|
|
235
236
|
data.message ||
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
"Invalid API key. Please check your TD_API_KEY and try again. " +
|
|
238
|
+
"Get your API key at https://console.testdriver.ai/team",
|
|
238
239
|
);
|
|
239
240
|
authError.code = data.error || "INVALID_API_KEY";
|
|
240
241
|
authError.isAuthError = true;
|
|
@@ -245,7 +246,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
|
245
246
|
if (response.status >= 500) {
|
|
246
247
|
const serverError = new Error(
|
|
247
248
|
data.message ||
|
|
248
|
-
|
|
249
|
+
`TestDriver API is currently unavailable (HTTP ${response.status}). Please try again later.`,
|
|
249
250
|
);
|
|
250
251
|
serverError.code = data.error || "API_UNAVAILABLE";
|
|
251
252
|
serverError.isServerError = true;
|
|
@@ -255,7 +256,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
|
255
256
|
// Rate limiting (429)
|
|
256
257
|
if (response.status === 429) {
|
|
257
258
|
const rateLimitError = new Error(
|
|
258
|
-
"Too many requests to TestDriver API. Please wait a moment and try again."
|
|
259
|
+
"Too many requests to TestDriver API. Please wait a moment and try again.",
|
|
259
260
|
);
|
|
260
261
|
rateLimitError.code = "RATE_LIMITED";
|
|
261
262
|
rateLimitError.isRateLimitError = true;
|
|
@@ -265,7 +266,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
|
|
|
265
266
|
// Other HTTP errors
|
|
266
267
|
throw new Error(
|
|
267
268
|
`Authentication failed: ${response.status} ${response.statusText}` +
|
|
268
|
-
|
|
269
|
+
(data.message ? ` - ${data.message}` : ""),
|
|
269
270
|
);
|
|
270
271
|
}
|
|
271
272
|
|
|
@@ -324,17 +325,17 @@ export async function recordTestCaseDirect(token, apiRoot, testCaseData) {
|
|
|
324
325
|
}
|
|
325
326
|
|
|
326
327
|
// Import TestDriverSDK using require to avoid esbuild transformation issues
|
|
327
|
-
const TestDriverSDK = require(
|
|
328
|
+
const TestDriverSDK = require("../sdk.js");
|
|
328
329
|
|
|
329
330
|
/**
|
|
330
331
|
* Create a TestDriver client for use in beforeAll/beforeEach hooks
|
|
331
332
|
* This is for the shared instance pattern where one driver is used across multiple tests
|
|
332
|
-
*
|
|
333
|
+
*
|
|
333
334
|
* @param {object} options - TestDriver options
|
|
334
335
|
* @param {string} [options.apiKey] - TestDriver API key (defaults to process.env.TD_API_KEY)
|
|
335
336
|
* @param {boolean} [options.headless] - Run sandbox in headless mode
|
|
336
337
|
* @returns {Promise<TestDriver>} Connected TestDriver client instance
|
|
337
|
-
*
|
|
338
|
+
*
|
|
338
339
|
* @example
|
|
339
340
|
* let testdriver;
|
|
340
341
|
* beforeAll(async () => {
|
|
@@ -344,45 +345,46 @@ const TestDriverSDK = require('../sdk.js');
|
|
|
344
345
|
*/
|
|
345
346
|
export async function createTestDriver(options = {}) {
|
|
346
347
|
// Get global plugin options if available
|
|
347
|
-
const pluginOptions =
|
|
348
|
-
|
|
348
|
+
const pluginOptions =
|
|
349
|
+
globalThis.__testdriverPlugin?.state?.testDriverOptions || {};
|
|
350
|
+
|
|
349
351
|
// Merge options: plugin global options < test-specific options
|
|
350
352
|
const mergedOptions = { ...pluginOptions, ...options };
|
|
351
|
-
|
|
353
|
+
|
|
352
354
|
// Support TD_OS environment variable for specifying target OS (linux, mac, windows)
|
|
353
355
|
// Priority: test options > plugin options > environment variable > default (linux)
|
|
354
356
|
if (!mergedOptions.os && process.env.TD_OS) {
|
|
355
357
|
mergedOptions.os = process.env.TD_OS;
|
|
356
358
|
}
|
|
357
|
-
|
|
359
|
+
|
|
358
360
|
// Extract TestDriver-specific options
|
|
359
361
|
const apiKey = mergedOptions.apiKey || process.env.TD_API_KEY;
|
|
360
|
-
|
|
362
|
+
|
|
361
363
|
// Build config for TestDriverSDK constructor
|
|
362
364
|
const config = { ...mergedOptions };
|
|
363
365
|
delete config.apiKey;
|
|
364
|
-
|
|
366
|
+
|
|
365
367
|
// Use TD_API_ROOT from environment if not provided in config
|
|
366
368
|
if (!config.apiRoot && process.env.TD_API_ROOT) {
|
|
367
369
|
config.apiRoot = process.env.TD_API_ROOT;
|
|
368
370
|
}
|
|
369
|
-
|
|
371
|
+
|
|
370
372
|
const testdriver = new TestDriverSDK(apiKey, config);
|
|
371
|
-
|
|
373
|
+
|
|
372
374
|
// Connect to sandbox
|
|
373
375
|
await testdriver.auth();
|
|
374
376
|
await testdriver.connect();
|
|
375
|
-
|
|
377
|
+
|
|
376
378
|
return testdriver;
|
|
377
379
|
}
|
|
378
380
|
|
|
379
381
|
/**
|
|
380
382
|
* Register a test with a shared TestDriver instance
|
|
381
383
|
* Call this at the start of each test to associate the test context with the driver
|
|
382
|
-
*
|
|
384
|
+
*
|
|
383
385
|
* @param {TestDriver} testdriver - TestDriver client instance from createTestDriver
|
|
384
386
|
* @param {object} context - Vitest test context (from async (context) => {})
|
|
385
|
-
*
|
|
387
|
+
*
|
|
386
388
|
* @example
|
|
387
389
|
* it("step01: verify login", async (context) => {
|
|
388
390
|
* registerTest(testdriver, context);
|
|
@@ -391,12 +393,14 @@ export async function createTestDriver(options = {}) {
|
|
|
391
393
|
*/
|
|
392
394
|
export function registerTest(testdriver, context) {
|
|
393
395
|
if (!testdriver) {
|
|
394
|
-
throw new Error(
|
|
396
|
+
throw new Error("registerTest() requires a TestDriver instance");
|
|
395
397
|
}
|
|
396
398
|
if (!context || !context.task) {
|
|
397
|
-
throw new Error(
|
|
399
|
+
throw new Error(
|
|
400
|
+
"registerTest() requires Vitest context. Pass the context parameter from your test function.",
|
|
401
|
+
);
|
|
398
402
|
}
|
|
399
|
-
|
|
403
|
+
|
|
400
404
|
testdriver.__vitestContext = context.task;
|
|
401
405
|
logger.debug(`Registered test: ${context.task.name}`);
|
|
402
406
|
}
|
|
@@ -404,9 +408,9 @@ export function registerTest(testdriver, context) {
|
|
|
404
408
|
/**
|
|
405
409
|
* Clean up a TestDriver client created with createTestDriver
|
|
406
410
|
* Call this in afterAll to properly disconnect and stop recordings
|
|
407
|
-
*
|
|
411
|
+
*
|
|
408
412
|
* @param {TestDriver} testdriver - TestDriver client instance
|
|
409
|
-
*
|
|
413
|
+
*
|
|
410
414
|
* @example
|
|
411
415
|
* afterAll(async () => {
|
|
412
416
|
* await cleanupTestDriver(testdriver);
|
|
@@ -416,31 +420,44 @@ export async function cleanupTestDriver(testdriver) {
|
|
|
416
420
|
if (!testdriver) {
|
|
417
421
|
return;
|
|
418
422
|
}
|
|
419
|
-
|
|
423
|
+
|
|
420
424
|
try {
|
|
421
425
|
// Stop dashcam if it was started
|
|
422
426
|
if (testdriver._dashcam && testdriver._dashcam.recording) {
|
|
423
427
|
try {
|
|
424
428
|
const dashcamUrl = await testdriver.dashcam.stop();
|
|
425
|
-
|
|
426
|
-
|
|
429
|
+
const debugMode =
|
|
430
|
+
process.env.VERBOSE || process.env.DEBUG || process.env.TD_DEBUG;
|
|
431
|
+
if (debugMode) {
|
|
432
|
+
console.log("🎥 Dashcam URL:", dashcamUrl);
|
|
433
|
+
}
|
|
434
|
+
|
|
427
435
|
// Register dashcam URL in memory for the reporter
|
|
428
436
|
if (dashcamUrl && globalThis.__testdriverPlugin?.registerDashcamUrl) {
|
|
429
|
-
const testId = testdriver.__vitestContext?.id ||
|
|
430
|
-
const platform = testdriver.os ||
|
|
431
|
-
globalThis.__testdriverPlugin.registerDashcamUrl(
|
|
437
|
+
const testId = testdriver.__vitestContext?.id || "unknown";
|
|
438
|
+
const platform = testdriver.os || "linux";
|
|
439
|
+
globalThis.__testdriverPlugin.registerDashcamUrl(
|
|
440
|
+
testId,
|
|
441
|
+
dashcamUrl,
|
|
442
|
+
platform,
|
|
443
|
+
);
|
|
432
444
|
}
|
|
433
445
|
} catch (error) {
|
|
434
|
-
console.error(
|
|
435
|
-
if (
|
|
436
|
-
|
|
446
|
+
console.error("❌ Failed to stop dashcam:", error.message);
|
|
447
|
+
if (
|
|
448
|
+
error.name === "NotFoundError" ||
|
|
449
|
+
error.responseData?.error === "NotFoundError"
|
|
450
|
+
) {
|
|
451
|
+
console.log(
|
|
452
|
+
" ℹ️ Sandbox session already terminated - dashcam stop skipped",
|
|
453
|
+
);
|
|
437
454
|
}
|
|
438
455
|
}
|
|
439
456
|
}
|
|
440
|
-
|
|
457
|
+
|
|
441
458
|
await testdriver.disconnect();
|
|
442
459
|
} catch (error) {
|
|
443
|
-
console.error(
|
|
460
|
+
console.error("Error disconnecting client:", error);
|
|
444
461
|
}
|
|
445
462
|
}
|
|
446
463
|
|
|
@@ -452,7 +469,7 @@ async function handleProcessExit() {
|
|
|
452
469
|
logger.debug("testRun:", !!pluginState.testRun);
|
|
453
470
|
logger.debug("testRunId:", pluginState.testRunId);
|
|
454
471
|
logger.debug("testRunCompleted:", pluginState.testRunCompleted);
|
|
455
|
-
|
|
472
|
+
|
|
456
473
|
if (!pluginState.testRun || !pluginState.testRunId) {
|
|
457
474
|
logger.debug("No test run to cancel - skipping cleanup");
|
|
458
475
|
return;
|
|
@@ -517,20 +534,22 @@ function registerExitHandlers() {
|
|
|
517
534
|
}
|
|
518
535
|
isExiting = true;
|
|
519
536
|
isCancelling = true; // Mark that we're cancelling
|
|
520
|
-
|
|
537
|
+
|
|
521
538
|
// Temporarily override process.exit to prevent Vitest from exiting before we're done
|
|
522
539
|
const originalExit = process.exit;
|
|
523
540
|
let exitCalled = false;
|
|
524
541
|
let exitCode = 130;
|
|
525
|
-
|
|
542
|
+
|
|
526
543
|
process.exit = (code) => {
|
|
527
544
|
if (!exitCalled) {
|
|
528
545
|
exitCalled = true;
|
|
529
546
|
exitCode = code ?? 130;
|
|
530
|
-
logger.debug(
|
|
547
|
+
logger.debug(
|
|
548
|
+
`process.exit(${exitCode}) called, waiting for cleanup...`,
|
|
549
|
+
);
|
|
531
550
|
}
|
|
532
551
|
};
|
|
533
|
-
|
|
552
|
+
|
|
534
553
|
handleProcessExit()
|
|
535
554
|
.then(() => {
|
|
536
555
|
logger.debug("Cleanup completed successfully");
|
|
@@ -552,14 +571,14 @@ function registerExitHandlers() {
|
|
|
552
571
|
if (isExiting) return;
|
|
553
572
|
isExiting = true;
|
|
554
573
|
isCancelling = true;
|
|
555
|
-
|
|
574
|
+
|
|
556
575
|
const originalExit = process.exit;
|
|
557
576
|
let exitCode = 143;
|
|
558
|
-
|
|
577
|
+
|
|
559
578
|
process.exit = (code) => {
|
|
560
579
|
exitCode = code ?? 143;
|
|
561
580
|
};
|
|
562
|
-
|
|
581
|
+
|
|
563
582
|
handleProcessExit()
|
|
564
583
|
.then(() => {
|
|
565
584
|
logger.debug("Cleanup completed successfully");
|
|
@@ -573,7 +592,6 @@ function registerExitHandlers() {
|
|
|
573
592
|
process.exit(exitCode);
|
|
574
593
|
});
|
|
575
594
|
});
|
|
576
|
-
|
|
577
595
|
}
|
|
578
596
|
|
|
579
597
|
/**
|
|
@@ -584,7 +602,9 @@ export default function testDriverPlugin(options = {}) {
|
|
|
584
602
|
// Store options but don't read env vars yet - they may not be loaded
|
|
585
603
|
// Environment variables will be read in onInit after setupFiles run
|
|
586
604
|
pluginState.apiRoot =
|
|
587
|
-
options.apiRoot ||
|
|
605
|
+
options.apiRoot ||
|
|
606
|
+
process.env.TD_API_ROOT ||
|
|
607
|
+
"https://testdriver-api.onrender.com";
|
|
588
608
|
pluginState.ciProvider = detectCI();
|
|
589
609
|
pluginState.gitInfo = getGitInfo();
|
|
590
610
|
|
|
@@ -607,10 +627,10 @@ export default function testDriverPlugin(options = {}) {
|
|
|
607
627
|
|
|
608
628
|
// Create reporter instance
|
|
609
629
|
const reporter = new TestDriverReporter(options);
|
|
610
|
-
|
|
630
|
+
|
|
611
631
|
// Add name property for Vitest
|
|
612
|
-
reporter.name =
|
|
613
|
-
|
|
632
|
+
reporter.name = "testdriver";
|
|
633
|
+
|
|
614
634
|
return reporter;
|
|
615
635
|
}
|
|
616
636
|
|
|
@@ -621,7 +641,10 @@ export default function testDriverPlugin(options = {}) {
|
|
|
621
641
|
class TestDriverReporter {
|
|
622
642
|
constructor(options = {}) {
|
|
623
643
|
this.options = options;
|
|
624
|
-
logger.debug("Reporter created with options:", {
|
|
644
|
+
logger.debug("Reporter created with options:", {
|
|
645
|
+
hasApiKey: !!options.apiKey,
|
|
646
|
+
hasApiRoot: !!options.apiRoot,
|
|
647
|
+
});
|
|
625
648
|
}
|
|
626
649
|
|
|
627
650
|
async onInit(ctx) {
|
|
@@ -634,7 +657,10 @@ class TestDriverReporter {
|
|
|
634
657
|
|
|
635
658
|
// NOW read the API key and API root (after setupFiles have run, including dotenv/config)
|
|
636
659
|
pluginState.apiKey = this.options.apiKey || process.env.TD_API_KEY;
|
|
637
|
-
pluginState.apiRoot =
|
|
660
|
+
pluginState.apiRoot =
|
|
661
|
+
this.options.apiRoot ||
|
|
662
|
+
process.env.TD_API_ROOT ||
|
|
663
|
+
"https://testdriver-api.onrender.com";
|
|
638
664
|
logger.debug("API key from options:", !!this.options.apiKey);
|
|
639
665
|
logger.debug("API key from env (at onInit):", !!process.env.TD_API_KEY);
|
|
640
666
|
logger.debug("API root from options:", this.options.apiRoot);
|
|
@@ -654,7 +680,12 @@ class TestDriverReporter {
|
|
|
654
680
|
// Check if we should enable the reporter
|
|
655
681
|
if (!pluginState.apiKey) {
|
|
656
682
|
logger.warn("No API key provided, skipping test recording");
|
|
657
|
-
logger.debug(
|
|
683
|
+
logger.debug(
|
|
684
|
+
"API key sources - options:",
|
|
685
|
+
!!this.options.apiKey,
|
|
686
|
+
"env:",
|
|
687
|
+
!!process.env.TD_API_KEY,
|
|
688
|
+
);
|
|
658
689
|
return;
|
|
659
690
|
}
|
|
660
691
|
|
|
@@ -705,7 +736,6 @@ class TestDriverReporter {
|
|
|
705
736
|
apiRoot: pluginState.apiRoot,
|
|
706
737
|
startTime: pluginState.startTime,
|
|
707
738
|
});
|
|
708
|
-
|
|
709
739
|
} catch (error) {
|
|
710
740
|
logger.error("Failed to initialize:", error.message);
|
|
711
741
|
pluginState.apiKey = null;
|
|
@@ -723,7 +753,9 @@ class TestDriverReporter {
|
|
|
723
753
|
|
|
724
754
|
// If we're cancelling due to SIGINT/SIGTERM, skip - handleProcessExit will handle it
|
|
725
755
|
if (isCancelling) {
|
|
726
|
-
logger.debug(
|
|
756
|
+
logger.debug(
|
|
757
|
+
"Cancellation in progress via signal handler, skipping onTestRunEnd",
|
|
758
|
+
);
|
|
727
759
|
return;
|
|
728
760
|
}
|
|
729
761
|
|
|
@@ -734,12 +766,16 @@ class TestDriverReporter {
|
|
|
734
766
|
}
|
|
735
767
|
|
|
736
768
|
if (!pluginState.apiKey) {
|
|
737
|
-
logger.warn(
|
|
769
|
+
logger.warn(
|
|
770
|
+
"Skipping completion - no API key (was it cleared after init failure?)",
|
|
771
|
+
);
|
|
738
772
|
return;
|
|
739
773
|
}
|
|
740
774
|
|
|
741
775
|
if (!pluginState.testRun) {
|
|
742
|
-
logger.warn(
|
|
776
|
+
logger.warn(
|
|
777
|
+
"Skipping completion - no test run created (check initialization logs)",
|
|
778
|
+
);
|
|
743
779
|
return;
|
|
744
780
|
}
|
|
745
781
|
|
|
@@ -765,7 +801,9 @@ class TestDriverReporter {
|
|
|
765
801
|
}
|
|
766
802
|
|
|
767
803
|
// Complete test run via API
|
|
768
|
-
logger.debug(
|
|
804
|
+
logger.debug(
|
|
805
|
+
`Completing test run ${pluginState.testRunId} with status: ${status}`,
|
|
806
|
+
);
|
|
769
807
|
|
|
770
808
|
const completeData = {
|
|
771
809
|
runId: pluginState.testRunId,
|
|
@@ -779,17 +817,23 @@ class TestDriverReporter {
|
|
|
779
817
|
|
|
780
818
|
// Update platform if detected from test results
|
|
781
819
|
const platform = getPlatform();
|
|
782
|
-
logger.debug(
|
|
820
|
+
logger.debug(
|
|
821
|
+
`Platform detection result: ${platform}, detectedPlatform in state: ${pluginState.detectedPlatform}`,
|
|
822
|
+
);
|
|
783
823
|
if (platform) {
|
|
784
824
|
completeData.platform = platform;
|
|
785
825
|
logger.debug(`Updating test run with platform: ${platform}`);
|
|
786
826
|
} else {
|
|
787
|
-
logger.warn(
|
|
827
|
+
logger.warn(
|
|
828
|
+
`No platform detected, test run will keep default platform`,
|
|
829
|
+
);
|
|
788
830
|
}
|
|
789
831
|
|
|
790
832
|
// Wait for any pending operations (shouldn't be any, but just in case)
|
|
791
833
|
if (pluginState.pendingTestCaseRecords.size > 0) {
|
|
792
|
-
logger.debug(
|
|
834
|
+
logger.debug(
|
|
835
|
+
`Waiting for ${pluginState.pendingTestCaseRecords.size} pending operations...`,
|
|
836
|
+
);
|
|
793
837
|
await Promise.all(Array.from(pluginState.pendingTestCaseRecords));
|
|
794
838
|
}
|
|
795
839
|
|
|
@@ -811,12 +855,14 @@ class TestDriverReporter {
|
|
|
811
855
|
logger.info(`View test run: ${testRunUrl}`);
|
|
812
856
|
// Output in a parseable format for CI
|
|
813
857
|
console.log(`TESTDRIVER_RUN_URL=${testRunUrl}`);
|
|
814
|
-
|
|
858
|
+
|
|
815
859
|
// Post GitHub comment if in CI environment
|
|
816
860
|
await postGitHubCommentIfEnabled(testRunUrl, stats, completeData);
|
|
817
861
|
}
|
|
818
862
|
|
|
819
|
-
logger.info(
|
|
863
|
+
logger.info(
|
|
864
|
+
`Test run completed: ${stats.passedTests}/${stats.totalTests} passed`,
|
|
865
|
+
);
|
|
820
866
|
} catch (error) {
|
|
821
867
|
logger.error("Failed to complete test run:", error.message);
|
|
822
868
|
logger.debug("Error stack:", error.stack);
|
|
@@ -848,8 +894,10 @@ class TestDriverReporter {
|
|
|
848
894
|
// Calculate duration from tracked start time
|
|
849
895
|
const testCase = pluginState.testCases.get(test.id);
|
|
850
896
|
const duration = testCase ? Date.now() - testCase.startTime : 0;
|
|
851
|
-
|
|
852
|
-
logger.debug(
|
|
897
|
+
|
|
898
|
+
logger.debug(
|
|
899
|
+
`Calculated duration: ${duration}ms (startTime: ${testCase?.startTime}, now: ${Date.now()})`,
|
|
900
|
+
);
|
|
853
901
|
|
|
854
902
|
// Read test metadata from Vitest's task.meta (set in test hooks)
|
|
855
903
|
const meta = test.meta();
|
|
@@ -874,9 +922,10 @@ class TestDriverReporter {
|
|
|
874
922
|
test.suite?.file?.name ||
|
|
875
923
|
test.location?.file ||
|
|
876
924
|
"unknown";
|
|
877
|
-
testFile =
|
|
878
|
-
|
|
879
|
-
|
|
925
|
+
testFile =
|
|
926
|
+
pluginState.projectRoot && absolutePath !== "unknown"
|
|
927
|
+
? path.relative(pluginState.projectRoot, absolutePath)
|
|
928
|
+
: absolutePath;
|
|
880
929
|
logger.debug(`Resolved testFile from fallback: ${testFile}`);
|
|
881
930
|
}
|
|
882
931
|
|
|
@@ -890,7 +939,9 @@ class TestDriverReporter {
|
|
|
890
939
|
const token = process.env.TD_TEST_RUN_TOKEN;
|
|
891
940
|
|
|
892
941
|
if (!testRunId || !token) {
|
|
893
|
-
logger.warn(
|
|
942
|
+
logger.warn(
|
|
943
|
+
`Test run not initialized, skipping test case recording for: ${test.name}`,
|
|
944
|
+
);
|
|
894
945
|
return;
|
|
895
946
|
}
|
|
896
947
|
|
|
@@ -938,7 +989,9 @@ class TestDriverReporter {
|
|
|
938
989
|
if (errorMessage) testCaseData.errorMessage = errorMessage;
|
|
939
990
|
if (errorStack) testCaseData.errorStack = errorStack;
|
|
940
991
|
|
|
941
|
-
logger.debug(
|
|
992
|
+
logger.debug(
|
|
993
|
+
`Recording test case: ${test.name} (${status}) with testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms, replay: ${dashcamUrl ? "yes" : "no"}`,
|
|
994
|
+
);
|
|
942
995
|
|
|
943
996
|
const testCaseResponse = await recordTestCaseDirect(
|
|
944
997
|
token,
|
|
@@ -955,8 +1008,10 @@ class TestDriverReporter {
|
|
|
955
1008
|
id: testCaseDbId,
|
|
956
1009
|
});
|
|
957
1010
|
|
|
958
|
-
console.log(
|
|
959
|
-
console.log(
|
|
1011
|
+
console.log("");
|
|
1012
|
+
console.log(
|
|
1013
|
+
`🔗 Test Report: ${getConsoleUrl(pluginState.apiRoot)}/runs/${testRunDbId}/${testCaseDbId}`,
|
|
1014
|
+
);
|
|
960
1015
|
} catch (error) {
|
|
961
1016
|
logger.error("Failed to report test case:", error.message);
|
|
962
1017
|
}
|
|
@@ -970,24 +1025,23 @@ class TestDriverReporter {
|
|
|
970
1025
|
/**
|
|
971
1026
|
* Maps an API root URL to its corresponding web console URL.
|
|
972
1027
|
* The API and web console are served from different domains/ports.
|
|
973
|
-
*
|
|
1028
|
+
*
|
|
974
1029
|
* @param {string} apiRoot - The API root URL (e.g., https://testdriver-api.onrender.com)
|
|
975
1030
|
* @returns {string} The corresponding web console URL
|
|
976
1031
|
*/
|
|
977
1032
|
function getConsoleUrl(apiRoot) {
|
|
1033
|
+
if (!apiRoot) return "https://console.testdriver.ai";
|
|
978
1034
|
|
|
979
|
-
if (!apiRoot) return 'https://console.testdriver.ai';
|
|
980
|
-
|
|
981
1035
|
// Production: API on render.com -> Console on testdriver.ai
|
|
982
|
-
if (apiRoot.includes(
|
|
983
|
-
return
|
|
1036
|
+
if (apiRoot.includes("testdriver-api.onrender.com")) {
|
|
1037
|
+
return "https://console.testdriver.ai";
|
|
984
1038
|
}
|
|
985
|
-
|
|
1039
|
+
|
|
986
1040
|
// Local development: API on localhost:1337 -> Web on localhost:3001
|
|
987
|
-
if (apiRoot.includes(
|
|
1041
|
+
if (apiRoot.includes("ngrok.io")) {
|
|
988
1042
|
return `http://localhost:3001`;
|
|
989
1043
|
}
|
|
990
|
-
|
|
1044
|
+
|
|
991
1045
|
// Ngrok or other tunnels: assume same host, different path structure
|
|
992
1046
|
// For ngrok, the API and web might be on same domain or user needs to configure
|
|
993
1047
|
// Return as-is since we can't reliably determine the mapping
|
|
@@ -1005,14 +1059,18 @@ function getSuiteName() {
|
|
|
1005
1059
|
function getPlatform() {
|
|
1006
1060
|
// First try to get platform from SDK client detected during test execution
|
|
1007
1061
|
if (pluginState.detectedPlatform) {
|
|
1008
|
-
logger.debug(
|
|
1062
|
+
logger.debug(
|
|
1063
|
+
`Using platform from SDK client: ${pluginState.detectedPlatform}`,
|
|
1064
|
+
);
|
|
1009
1065
|
return pluginState.detectedPlatform;
|
|
1010
1066
|
}
|
|
1011
1067
|
|
|
1012
1068
|
// Try to get platform from dashcam URLs (registered during test cleanup)
|
|
1013
1069
|
for (const [, data] of pluginState.dashcamUrls) {
|
|
1014
1070
|
if (data.platform) {
|
|
1015
|
-
logger.debug(
|
|
1071
|
+
logger.debug(
|
|
1072
|
+
`Using platform from dashcam URL registration: ${data.platform}`,
|
|
1073
|
+
);
|
|
1016
1074
|
return data.platform;
|
|
1017
1075
|
}
|
|
1018
1076
|
}
|
|
@@ -1083,7 +1141,7 @@ function getGitInfo() {
|
|
|
1083
1141
|
try {
|
|
1084
1142
|
info.commit = execSync("git rev-parse HEAD", {
|
|
1085
1143
|
encoding: "utf8",
|
|
1086
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
1144
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
1087
1145
|
}).trim();
|
|
1088
1146
|
logger.debug("Git commit from local:", info.commit);
|
|
1089
1147
|
} catch (e) {
|
|
@@ -1095,7 +1153,7 @@ function getGitInfo() {
|
|
|
1095
1153
|
try {
|
|
1096
1154
|
info.branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
1097
1155
|
encoding: "utf8",
|
|
1098
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
1156
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
1099
1157
|
}).trim();
|
|
1100
1158
|
logger.debug("Git branch from local:", info.branch);
|
|
1101
1159
|
} catch (e) {
|
|
@@ -1107,7 +1165,7 @@ function getGitInfo() {
|
|
|
1107
1165
|
try {
|
|
1108
1166
|
info.author = execSync("git config user.name", {
|
|
1109
1167
|
encoding: "utf8",
|
|
1110
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
1168
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
1111
1169
|
}).trim();
|
|
1112
1170
|
logger.debug("Git author from local:", info.author);
|
|
1113
1171
|
} catch (e) {
|
|
@@ -1119,7 +1177,7 @@ function getGitInfo() {
|
|
|
1119
1177
|
try {
|
|
1120
1178
|
const remoteUrl = execSync("git config --get remote.origin.url", {
|
|
1121
1179
|
encoding: "utf8",
|
|
1122
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
1180
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
1123
1181
|
}).trim();
|
|
1124
1182
|
|
|
1125
1183
|
// Extract repo from git URL (supports both SSH and HTTPS)
|
|
@@ -1153,42 +1211,46 @@ function getGitInfo() {
|
|
|
1153
1211
|
async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
|
|
1154
1212
|
try {
|
|
1155
1213
|
// Check if GitHub comments are explicitly disabled
|
|
1156
|
-
if (process.env.TESTDRIVER_SKIP_GITHUB_COMMENT ===
|
|
1157
|
-
logger.debug(
|
|
1214
|
+
if (process.env.TESTDRIVER_SKIP_GITHUB_COMMENT === "true") {
|
|
1215
|
+
logger.debug(
|
|
1216
|
+
"GitHub comments disabled via TESTDRIVER_SKIP_GITHUB_COMMENT",
|
|
1217
|
+
);
|
|
1158
1218
|
return;
|
|
1159
1219
|
}
|
|
1160
|
-
|
|
1220
|
+
|
|
1161
1221
|
// Check if GitHub comment posting is enabled
|
|
1162
1222
|
const githubToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
1163
1223
|
const prNumber = process.env.GITHUB_PR_NUMBER;
|
|
1164
1224
|
const commitSha = process.env.GITHUB_SHA || pluginState.gitInfo.commit;
|
|
1165
|
-
|
|
1225
|
+
|
|
1166
1226
|
// Only post if we have a token and either a PR number or commit SHA
|
|
1167
1227
|
if (!githubToken) {
|
|
1168
|
-
logger.debug(
|
|
1228
|
+
logger.debug("GitHub token not found, skipping comment posting");
|
|
1169
1229
|
return;
|
|
1170
1230
|
}
|
|
1171
|
-
|
|
1231
|
+
|
|
1172
1232
|
if (!prNumber && !commitSha) {
|
|
1173
|
-
logger.debug(
|
|
1233
|
+
logger.debug(
|
|
1234
|
+
"Neither PR number nor commit SHA found, skipping comment posting",
|
|
1235
|
+
);
|
|
1174
1236
|
return;
|
|
1175
1237
|
}
|
|
1176
|
-
|
|
1238
|
+
|
|
1177
1239
|
// Extract owner/repo from git info
|
|
1178
1240
|
const repo = pluginState.gitInfo.repo;
|
|
1179
1241
|
if (!repo) {
|
|
1180
|
-
logger.warn(
|
|
1242
|
+
logger.warn("Repository info not available, skipping GitHub comment");
|
|
1181
1243
|
return;
|
|
1182
1244
|
}
|
|
1183
|
-
|
|
1184
|
-
const [owner, repoName] = repo.split(
|
|
1245
|
+
|
|
1246
|
+
const [owner, repoName] = repo.split("/");
|
|
1185
1247
|
if (!owner || !repoName) {
|
|
1186
|
-
logger.warn(
|
|
1248
|
+
logger.warn("Invalid repository format, expected owner/repo");
|
|
1187
1249
|
return;
|
|
1188
1250
|
}
|
|
1189
|
-
|
|
1190
|
-
logger.debug(
|
|
1191
|
-
|
|
1251
|
+
|
|
1252
|
+
logger.debug("Preparing GitHub comment...");
|
|
1253
|
+
|
|
1192
1254
|
// Prepare test run data for comment
|
|
1193
1255
|
const testRunData = {
|
|
1194
1256
|
runId: pluginState.testRunId,
|
|
@@ -1199,16 +1261,19 @@ async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
|
|
|
1199
1261
|
skippedTests: stats.skippedTests,
|
|
1200
1262
|
duration: completeData.duration,
|
|
1201
1263
|
testRunUrl,
|
|
1202
|
-
platform:
|
|
1203
|
-
|
|
1204
|
-
|
|
1264
|
+
platform:
|
|
1265
|
+
completeData.platform || pluginState.detectedPlatform || "unknown",
|
|
1266
|
+
branch: pluginState.gitInfo.branch || "unknown",
|
|
1267
|
+
commit: commitSha || "unknown",
|
|
1205
1268
|
};
|
|
1206
|
-
|
|
1269
|
+
|
|
1207
1270
|
// Use recorded test cases from pluginState
|
|
1208
1271
|
const testCases = pluginState.recordedTestCases || [];
|
|
1209
|
-
|
|
1210
|
-
logger.info(
|
|
1211
|
-
|
|
1272
|
+
|
|
1273
|
+
logger.info(
|
|
1274
|
+
`Posting GitHub comment with ${testCases.length} test cases...`,
|
|
1275
|
+
);
|
|
1276
|
+
|
|
1212
1277
|
// Post or update GitHub comment
|
|
1213
1278
|
const githubOptions = {
|
|
1214
1279
|
token: githubToken,
|
|
@@ -1217,14 +1282,17 @@ async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
|
|
|
1217
1282
|
prNumber: prNumber ? parseInt(prNumber, 10) : undefined,
|
|
1218
1283
|
commitSha: commitSha,
|
|
1219
1284
|
};
|
|
1220
|
-
|
|
1221
|
-
const comment = await postOrUpdateTestResults(
|
|
1285
|
+
|
|
1286
|
+
const comment = await postOrUpdateTestResults(
|
|
1287
|
+
testRunData,
|
|
1288
|
+
testCases,
|
|
1289
|
+
githubOptions,
|
|
1290
|
+
);
|
|
1222
1291
|
logger.info(`✅ GitHub comment posted: ${comment.html_url}`);
|
|
1223
1292
|
console.log(`\n🔗 GitHub Comment: ${comment.html_url}\n`);
|
|
1224
|
-
|
|
1225
1293
|
} catch (error) {
|
|
1226
|
-
logger.warn(
|
|
1227
|
-
logger.debug(
|
|
1294
|
+
logger.warn("Failed to post GitHub comment:", error.message);
|
|
1295
|
+
logger.debug("GitHub comment error stack:", error.stack);
|
|
1228
1296
|
}
|
|
1229
1297
|
}
|
|
1230
1298
|
|
|
@@ -1286,7 +1354,7 @@ async function createTestRun(data) {
|
|
|
1286
1354
|
async function completeTestRun(data) {
|
|
1287
1355
|
const url = `${pluginState.apiRoot}/api/v1/testdriver/test-run-complete`;
|
|
1288
1356
|
logger.debug(`completeTestRun: POSTing to ${url}`);
|
|
1289
|
-
|
|
1357
|
+
|
|
1290
1358
|
try {
|
|
1291
1359
|
const response = await withTimeout(
|
|
1292
1360
|
fetch(url, {
|