@testdriverai/agent 7.8.0-canary.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -0
- package/.env.example +4 -0
- package/.prettierignore +4 -0
- package/.prettierrc +1 -0
- package/CHANGELOG.md +953 -0
- package/README.md +81 -0
- package/agent/events.js +135 -0
- package/agent/index.js +2450 -0
- package/agent/interface.js +35 -0
- package/agent/lib/analytics.js +22 -0
- package/agent/lib/censorship.js +75 -0
- package/agent/lib/commander.js +246 -0
- package/agent/lib/commands.js +1684 -0
- package/agent/lib/config.js +60 -0
- package/agent/lib/generator.js +91 -0
- package/agent/lib/http.js +144 -0
- package/agent/lib/logger.js +56 -0
- package/agent/lib/outputs.js +29 -0
- package/agent/lib/parser.js +209 -0
- package/agent/lib/redraw.js +386 -0
- package/agent/lib/resources/cursor-2.png +0 -0
- package/agent/lib/sandbox.js +1104 -0
- package/agent/lib/sdk.js +633 -0
- package/agent/lib/session.js +25 -0
- package/agent/lib/source-mapper.js +342 -0
- package/agent/lib/subimage/index.js +77 -0
- package/agent/lib/subimage/opencv.js +69 -0
- package/agent/lib/system.js +204 -0
- package/agent/lib/theme.js +14 -0
- package/agent/lib/valid-version.js +21 -0
- package/agent/lib/validation.js +169 -0
- package/ai/.claude-plugin/plugin.json +9 -0
- package/ai/agents/testdriver.md +638 -0
- package/ai/skills/testdriver-ai/SKILL.md +204 -0
- package/ai/skills/testdriver-assert/SKILL.md +315 -0
- package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
- package/ai/skills/testdriver-cache/SKILL.md +221 -0
- package/ai/skills/testdriver-caching/SKILL.md +124 -0
- package/ai/skills/testdriver-captcha/SKILL.md +158 -0
- package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
- package/ai/skills/testdriver-click/SKILL.md +286 -0
- package/ai/skills/testdriver-client/SKILL.md +477 -0
- package/ai/skills/testdriver-cloud/SKILL.md +119 -0
- package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
- package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
- package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
- package/ai/skills/testdriver-device-config/SKILL.md +317 -0
- package/ai/skills/testdriver-double-click/SKILL.md +102 -0
- package/ai/skills/testdriver-elements/SKILL.md +605 -0
- package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
- package/ai/skills/testdriver-errors/SKILL.md +246 -0
- package/ai/skills/testdriver-events/SKILL.md +356 -0
- package/ai/skills/testdriver-examples/SKILL.md +7 -0
- package/ai/skills/testdriver-exec/SKILL.md +317 -0
- package/ai/skills/testdriver-find/SKILL.md +829 -0
- package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
- package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
- package/ai/skills/testdriver-hover/SKILL.md +278 -0
- package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
- package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
- package/ai/skills/testdriver-mcp/SKILL.md +7 -0
- package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
- package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
- package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
- package/ai/skills/testdriver-parse/SKILL.md +236 -0
- package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
- package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
- package/ai/skills/testdriver-provision/SKILL.md +331 -0
- package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
- package/ai/skills/testdriver-redraw/SKILL.md +214 -0
- package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
- package/ai/skills/testdriver-right-click/SKILL.md +123 -0
- package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
- package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
- package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
- package/ai/skills/testdriver-scroll/SKILL.md +335 -0
- package/ai/skills/testdriver-secrets/SKILL.md +115 -0
- package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
- package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
- package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
- package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
- package/ai/skills/testdriver-type/SKILL.md +357 -0
- package/ai/skills/testdriver-variables/SKILL.md +111 -0
- package/ai/skills/testdriver-wait/SKILL.md +50 -0
- package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
- package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
- package/bin/testdriverai.js +22 -0
- package/debugger/bg.png +0 -0
- package/debugger/icon.png +0 -0
- package/debugger/index.html +469 -0
- package/debugger/td.png +0 -0
- package/debugger/tray-buffered.png +0 -0
- package/debugger/tray.png +0 -0
- package/docs/GITHUB_COMMENTS.md +330 -0
- package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
- package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
- package/docs/TEST-GITHUB-COMMENTS.md +129 -0
- package/docs/_data/examples-manifest.json +177 -0
- package/docs/_data/examples-manifest.schema.json +41 -0
- package/docs/_scripts/extract-example-urls.js +165 -0
- package/docs/_scripts/generate-examples.js +560 -0
- package/docs/_scripts/generate-skills.js +154 -0
- package/docs/_scripts/link-replacer.js +164 -0
- package/docs/_scripts/upload-docs-to-openai.js +284 -0
- package/docs/changelog.mdx +161 -0
- package/docs/claude-mcp-plugin.mdx +160 -0
- package/docs/docs.json +442 -0
- package/docs/github-integration-setup.md +266 -0
- package/docs/guide/best-practices-polling.mdx +174 -0
- package/docs/images/content/account/newprojectsettings.png +0 -0
- package/docs/images/content/account/projectpage.png +0 -0
- package/docs/images/content/account/projectreplays.png +0 -0
- package/docs/images/content/account/team-manage.png +0 -0
- package/docs/images/content/account/teampage.png +0 -0
- package/docs/images/content/extension/cursor.svg +1 -0
- package/docs/images/content/extension/vscode.svg +57 -0
- package/docs/images/content/extension/windsurf.svg +3 -0
- package/docs/images/content/parse/output.png +0 -0
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/side-by-side.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/docs/images/content/vscode/v7-chat.png +0 -0
- package/docs/images/content/vscode/v7-choose-agent.png +0 -0
- package/docs/images/content/vscode/v7-full.png +0 -0
- package/docs/images/content/vscode/v7-onboarding.png +0 -0
- package/docs/images/content/vscode/vscode-2-assert.png +0 -0
- package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
- package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
- package/docs/images/content/vscode/vscode-file-creation.png +0 -0
- package/docs/images/content/vscode/vscode-install.png +0 -0
- package/docs/images/content/vscode/vscode-overview.png +0 -0
- package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
- package/docs/images/content/vscode/vscode-stopchat.png +0 -0
- package/docs/images/content/vscode/vscode-stoptest.png +0 -0
- package/docs/images/content/vscode/vscode-tdservice.png +0 -0
- package/docs/images/content/vscode/vscode-test-output.png +0 -0
- package/docs/images/content/vscode/vscode-testhistory.png +0 -0
- package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
- package/docs/images/content/vscode/vscode-testpane.png +0 -0
- package/docs/images/template/dark.png +0 -0
- package/docs/images/template/icon.png +0 -0
- package/docs/images/template/light.png +0 -0
- package/docs/snippets/calendar-link.mdx +4 -0
- package/docs/snippets/gitignore-warning.mdx +7 -0
- package/docs/snippets/lifecycle-warning.mdx +6 -0
- package/docs/snippets/test-prereqs.mdx +12 -0
- package/docs/snippets/tests/assert-replay.mdx +7 -0
- package/docs/snippets/tests/assert-yaml.mdx +8 -0
- package/docs/snippets/tests/exec-js-replay.mdx +7 -0
- package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
- package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
- package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
- package/docs/snippets/tests/hover-image-replay.mdx +7 -0
- package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
- package/docs/snippets/tests/hover-text-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
- package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
- package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
- package/docs/snippets/tests/match-image-replay.mdx +7 -0
- package/docs/snippets/tests/match-image-yaml.mdx +17 -0
- package/docs/snippets/tests/press-keys-replay.mdx +7 -0
- package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
- package/docs/snippets/tests/remember-replay.mdx +7 -0
- package/docs/snippets/tests/remember-yaml.mdx +28 -0
- package/docs/snippets/tests/scroll-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
- package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
- package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
- package/docs/snippets/tests/scroll-yaml.mdx +30 -0
- package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
- package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
- package/docs/snippets/tests/type-replay.mdx +7 -0
- package/docs/snippets/tests/type-yaml.mdx +28 -0
- package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
- package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
- package/docs/snippets/tests/wait-replay.mdx +7 -0
- package/docs/snippets/tests/wait-yaml.mdx +13 -0
- package/docs/styles.css +65 -0
- package/docs/v6/account/dashboard.mdx +16 -0
- package/docs/v6/account/enterprise.mdx +110 -0
- package/docs/v6/account/pricing.mdx +33 -0
- package/docs/v6/account/projects.mdx +33 -0
- package/docs/v6/account/team.mdx +35 -0
- package/docs/v6/action/ami.mdx +109 -0
- package/docs/v6/action/performance.mdx +105 -0
- package/docs/v6/action/secrets.mdx +93 -0
- package/docs/v6/apps/chrome-extensions.mdx +48 -0
- package/docs/v6/apps/desktop-apps.mdx +93 -0
- package/docs/v6/apps/mobile-apps.mdx +26 -0
- package/docs/v6/apps/static-websites.mdx +54 -0
- package/docs/v6/apps/tauri-apps.mdx +361 -0
- package/docs/v6/bugs/jira.mdx +232 -0
- package/docs/v6/cli/overview.mdx +66 -0
- package/docs/v6/commands/assert.mdx +45 -0
- package/docs/v6/commands/exec.mdx +276 -0
- package/docs/v6/commands/focus-application.mdx +44 -0
- package/docs/v6/commands/hover-image.mdx +69 -0
- package/docs/v6/commands/hover-text.mdx +47 -0
- package/docs/v6/commands/if.mdx +53 -0
- package/docs/v6/commands/match-image.mdx +67 -0
- package/docs/v6/commands/press-keys.mdx +87 -0
- package/docs/v6/commands/remember.mdx +49 -0
- package/docs/v6/commands/run.mdx +44 -0
- package/docs/v6/commands/scroll-until-image.mdx +66 -0
- package/docs/v6/commands/scroll-until-text.mdx +60 -0
- package/docs/v6/commands/scroll.mdx +69 -0
- package/docs/v6/commands/type.mdx +45 -0
- package/docs/v6/commands/wait-for-image.mdx +54 -0
- package/docs/v6/commands/wait-for-text.mdx +48 -0
- package/docs/v6/commands/wait.mdx +45 -0
- package/docs/v6/exporting/junit.mdx +218 -0
- package/docs/v6/exporting/playwright.mdx +197 -0
- package/docs/v6/features/auto-healing.mdx +144 -0
- package/docs/v6/features/generation.mdx +116 -0
- package/docs/v6/features/parallel-testing.mdx +151 -0
- package/docs/v6/features/reusable-snippets.mdx +131 -0
- package/docs/v6/features/selectorless.mdx +80 -0
- package/docs/v6/features/visual-assertions.mdx +139 -0
- package/docs/v6/getting-started/ci.mdx +146 -0
- package/docs/v6/getting-started/cli.mdx +91 -0
- package/docs/v6/getting-started/editing.mdx +100 -0
- package/docs/v6/getting-started/playwright.mdx +342 -0
- package/docs/v6/getting-started/running.mdx +48 -0
- package/docs/v6/getting-started/self-hosting.mdx +408 -0
- package/docs/v6/getting-started/vscode.mdx +88 -0
- package/docs/v6/guide/assertions.mdx +189 -0
- package/docs/v6/guide/authentication.mdx +136 -0
- package/docs/v6/guide/code.mdx +65 -0
- package/docs/v6/guide/dashcam.mdx +118 -0
- package/docs/v6/guide/environment-variables.mdx +26 -0
- package/docs/v6/guide/lifecycle.mdx +242 -0
- package/docs/v6/guide/locating.mdx +141 -0
- package/docs/v6/guide/protips.mdx +43 -0
- package/docs/v6/guide/variables.mdx +143 -0
- package/docs/v6/guide/waiting.mdx +130 -0
- package/docs/v6/importing/csv.mdx +196 -0
- package/docs/v6/importing/gherkin.mdx +143 -0
- package/docs/v6/importing/jira.mdx +164 -0
- package/docs/v6/importing/testrail.mdx +162 -0
- package/docs/v6/integrations/electron.mdx +146 -0
- package/docs/v6/integrations/netlify.mdx +100 -0
- package/docs/v6/integrations/vercel.mdx +125 -0
- package/docs/v6/interactive/explore.mdx +99 -0
- package/docs/v6/interactive/run.mdx +52 -0
- package/docs/v6/interactive/save.mdx +63 -0
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v6/overview/faq.mdx +162 -0
- package/docs/v6/overview/performance.mdx +52 -0
- package/docs/v6/overview/quickstart.mdx +137 -0
- package/docs/v6/overview/what-is-testdriver.mdx +85 -0
- package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
- package/docs/v6/scenarios/cookie-banner.mdx +32 -0
- package/docs/v6/scenarios/file-upload.mdx +33 -0
- package/docs/v6/scenarios/form-filling.mdx +32 -0
- package/docs/v6/scenarios/log-in.mdx +75 -0
- package/docs/v6/scenarios/pdf-generation.mdx +25 -0
- package/docs/v6/scenarios/spell-check.mdx +22 -0
- package/docs/v6/security/action.mdx +84 -0
- package/docs/v6/security/agent.mdx +73 -0
- package/docs/v6/security/platform.mdx +77 -0
- package/docs/v6/tutorials/advanced-test.mdx +81 -0
- package/docs/v6/tutorials/basic-test.mdx +45 -0
- package/docs/v7/_drafts/agents.mdx +843 -0
- package/docs/v7/_drafts/architecture.mdx +399 -0
- package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
- package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
- package/docs/v7/_drafts/best-practices.mdx +486 -0
- package/docs/v7/_drafts/caching-ai.mdx +215 -0
- package/docs/v7/_drafts/caching-selectors.mdx +424 -0
- package/docs/v7/_drafts/caching.mdx +366 -0
- package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
- package/docs/v7/_drafts/commands/assert.mdx +45 -0
- package/docs/v7/_drafts/commands/exec.mdx +276 -0
- package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
- package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
- package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
- package/docs/v7/_drafts/commands/if.mdx +53 -0
- package/docs/v7/_drafts/commands/match-image.mdx +67 -0
- package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
- package/docs/v7/_drafts/commands/remember.mdx +49 -0
- package/docs/v7/_drafts/commands/run.mdx +44 -0
- package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/_drafts/commands/scroll.mdx +69 -0
- package/docs/v7/_drafts/commands/type.mdx +45 -0
- package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
- package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
- package/docs/v7/_drafts/commands/wait.mdx +45 -0
- package/docs/v7/_drafts/configuration.mdx +378 -0
- package/docs/v7/_drafts/contributing.mdx +174 -0
- package/docs/v7/_drafts/core.mdx +458 -0
- package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
- package/docs/v7/_drafts/debugging.mdx +349 -0
- package/docs/v7/_drafts/error-handling.mdx +501 -0
- package/docs/v7/_drafts/faq.mdx +393 -0
- package/docs/v7/_drafts/hooks.mdx +360 -0
- package/docs/v7/_drafts/init-command.mdx +95 -0
- package/docs/v7/_drafts/installation.mdx +420 -0
- package/docs/v7/_drafts/migration.mdx +562 -0
- package/docs/v7/_drafts/observable.mdx +604 -0
- package/docs/v7/_drafts/playwright.mdx +342 -0
- package/docs/v7/_drafts/plugin-migration.mdx +220 -0
- package/docs/v7/_drafts/powerful.mdx +419 -0
- package/docs/v7/_drafts/presets.mdx +210 -0
- package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
- package/docs/v7/_drafts/prompt-cache.mdx +200 -0
- package/docs/v7/_drafts/provision.mdx +390 -0
- package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
- package/docs/v7/_drafts/readme.mdx +135 -0
- package/docs/v7/_drafts/reports.mdx +414 -0
- package/docs/v7/_drafts/scalable.mdx +763 -0
- package/docs/v7/_drafts/screenshot.mdx +155 -0
- package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
- package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
- package/docs/v7/_drafts/sdk-migration.mdx +474 -0
- package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
- package/docs/v7/_drafts/self-hosting.mdx +369 -0
- package/docs/v7/_drafts/test-recording.mdx +382 -0
- package/docs/v7/_drafts/troubleshooting.mdx +526 -0
- package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
- package/docs/v7/_drafts/vitest.mdx +535 -0
- package/docs/v7/_drafts/writing-tests.mdx +25 -0
- package/docs/v7/ai.mdx +205 -0
- package/docs/v7/assert.mdx +316 -0
- package/docs/v7/aws-setup.mdx +449 -0
- package/docs/v7/cache.mdx +223 -0
- package/docs/v7/caching.mdx +128 -0
- package/docs/v7/captcha.mdx +159 -0
- package/docs/v7/ci-cd.mdx +603 -0
- package/docs/v7/click.mdx +287 -0
- package/docs/v7/client.mdx +478 -0
- package/docs/v7/copilot/auto-healing.mdx +265 -0
- package/docs/v7/copilot/creating-tests.mdx +156 -0
- package/docs/v7/copilot/github.mdx +143 -0
- package/docs/v7/copilot/running-tests.mdx +149 -0
- package/docs/v7/copilot/setup.mdx +143 -0
- package/docs/v7/customizing-devices.mdx +319 -0
- package/docs/v7/dashcam.mdx +419 -0
- package/docs/v7/debugging-with-screenshots.mdx +402 -0
- package/docs/v7/device-config.mdx +317 -0
- package/docs/v7/double-click.mdx +102 -0
- package/docs/v7/elements.mdx +606 -0
- package/docs/v7/enterprise.mdx +9 -0
- package/docs/v7/errors.mdx +248 -0
- package/docs/v7/events.mdx +358 -0
- package/docs/v7/examples/ai.mdx +72 -0
- package/docs/v7/examples/assert.mdx +72 -0
- package/docs/v7/examples/captcha-api.mdx +92 -0
- package/docs/v7/examples/chrome-extension.mdx +132 -0
- package/docs/v7/examples/drag-and-drop.mdx +100 -0
- package/docs/v7/examples/element-not-found.mdx +67 -0
- package/docs/v7/examples/exec-output.mdx +85 -0
- package/docs/v7/examples/exec-pwsh.mdx +83 -0
- package/docs/v7/examples/focus-window.mdx +62 -0
- package/docs/v7/examples/hover-image.mdx +94 -0
- package/docs/v7/examples/hover-text.mdx +69 -0
- package/docs/v7/examples/installer.mdx +91 -0
- package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
- package/docs/v7/examples/match-image.mdx +96 -0
- package/docs/v7/examples/press-keys.mdx +92 -0
- package/docs/v7/examples/scroll-keyboard.mdx +79 -0
- package/docs/v7/examples/scroll-until-image.mdx +81 -0
- package/docs/v7/examples/scroll-until-text.mdx +109 -0
- package/docs/v7/examples/scroll.mdx +81 -0
- package/docs/v7/examples/type.mdx +92 -0
- package/docs/v7/examples/windows-installer.mdx +89 -0
- package/docs/v7/exec.mdx +318 -0
- package/docs/v7/find.mdx +830 -0
- package/docs/v7/focus-application.mdx +294 -0
- package/docs/v7/generating-tests.mdx +36 -0
- package/docs/v7/hosted.mdx +158 -0
- package/docs/v7/hover.mdx +279 -0
- package/docs/v7/locating-elements.mdx +71 -0
- package/docs/v7/making-assertions.mdx +32 -0
- package/docs/v7/mcp.mdx +9 -0
- package/docs/v7/mouse-down.mdx +161 -0
- package/docs/v7/mouse-up.mdx +164 -0
- package/docs/v7/parse.mdx +237 -0
- package/docs/v7/performing-actions.mdx +54 -0
- package/docs/v7/press-keys.mdx +349 -0
- package/docs/v7/provision.mdx +333 -0
- package/docs/v7/quickstart.mdx +173 -0
- package/docs/v7/redraw.mdx +216 -0
- package/docs/v7/reusable-code.mdx +249 -0
- package/docs/v7/right-click.mdx +123 -0
- package/docs/v7/running-tests.mdx +185 -0
- package/docs/v7/screenshot.mdx +249 -0
- package/docs/v7/screenshots.mdx +186 -0
- package/docs/v7/scroll.mdx +336 -0
- package/docs/v7/secrets.mdx +115 -0
- package/docs/v7/self-hosted.mdx +149 -0
- package/docs/v7/type.mdx +358 -0
- package/docs/v7/variables.mdx +111 -0
- package/docs/v7/wait.mdx +52 -0
- package/docs/v7/waiting-for-elements.mdx +90 -0
- package/docs/v7/what-is-testdriver.mdx +54 -0
- package/eslint.config.js +67 -0
- package/examples/ai.test.mjs +31 -0
- package/examples/assert.test.mjs +47 -0
- package/examples/chrome-extension.test.mjs +97 -0
- package/examples/config.mjs +5 -0
- package/examples/element-not-found.test.mjs +27 -0
- package/examples/exec-output.test.mjs +60 -0
- package/examples/exec-pwsh.test.mjs +58 -0
- package/examples/findall-coffee-icons.test.mjs +42 -0
- package/examples/focus-window.test.mjs +37 -0
- package/examples/formatted-logging.test.mjs +27 -0
- package/examples/hover-image.test.mjs +53 -0
- package/examples/hover-text-with-description.test.mjs +57 -0
- package/examples/hover-text.test.mjs +28 -0
- package/examples/installer.test.mjs +50 -0
- package/examples/launch-vscode-linux.test.mjs +55 -0
- package/examples/match-image.test.mjs +55 -0
- package/examples/parse.test.mjs +19 -0
- package/examples/press-keys.test.mjs +44 -0
- package/examples/prompt.test.mjs +34 -0
- package/examples/scroll-keyboard.test.mjs +38 -0
- package/examples/scroll-until-image.test.mjs +40 -0
- package/examples/scroll.test.mjs +42 -0
- package/examples/type.test.mjs +46 -0
- package/examples/windows-installer.test.mjs +54 -0
- package/index.js +2 -0
- package/interfaces/cli/commands/init.js +438 -0
- package/interfaces/cli/commands/setup.js +382 -0
- package/interfaces/cli/lib/base.js +285 -0
- package/interfaces/cli.js +20 -0
- package/interfaces/junit-reporter.js +290 -0
- package/interfaces/logger.js +388 -0
- package/interfaces/readline.js +234 -0
- package/interfaces/shared-test-state.mjs +64 -0
- package/interfaces/vitest-plugin.d.ts +115 -0
- package/interfaces/vitest-plugin.mjs +1698 -0
- package/lib/captcha/solver.js +358 -0
- package/lib/core/Dashcam.js +533 -0
- package/lib/core/index.d.ts +172 -0
- package/lib/core/index.js +12 -0
- package/lib/environments.json +18 -0
- package/lib/github-comment-formatter.js +263 -0
- package/lib/github-comment.mjs +452 -0
- package/lib/init-project.js +575 -0
- package/lib/presets/index.mjs +331 -0
- package/lib/resolve-channel.js +46 -0
- package/lib/sentry.js +417 -0
- package/lib/vitest/hooks.d.ts +57 -0
- package/lib/vitest/hooks.mjs +674 -0
- package/lib/vitest/setup-aws.mjs +247 -0
- package/lib/vitest/setup-self-hosted.mjs +151 -0
- package/lib/vitest/setup.mjs +46 -0
- package/manual/captcha-api.test.mjs +51 -0
- package/manual/drag-and-drop.test.mjs +59 -0
- package/manual/flake-diffthreshold-001.test.mjs +9 -0
- package/manual/flake-diffthreshold-01.test.mjs +9 -0
- package/manual/flake-diffthreshold-05.test.mjs +9 -0
- package/manual/flake-noredraw-cache.test.mjs +9 -0
- package/manual/flake-noredraw-nocache.test.mjs +9 -0
- package/manual/flake-redraw-cache.test.mjs +9 -0
- package/manual/flake-redraw-nocache.test.mjs +9 -0
- package/manual/flake-rocket-match.test.mjs +30 -0
- package/manual/flake-shared.mjs +51 -0
- package/manual/no-provision.test.mjs +31 -0
- package/manual/packer-hover-image.test.mjs +176 -0
- package/manual/scroll-until-text.test.mjs +68 -0
- package/manual/test-init-command.js +223 -0
- package/mcp-server/README.md +322 -0
- package/mcp-server/dist/codegen.d.ts +9 -0
- package/mcp-server/dist/codegen.js +165 -0
- package/mcp-server/dist/mcp-app.html +114 -0
- package/mcp-server/dist/package.json +1 -0
- package/mcp-server/dist/provision-types.d.ts +290 -0
- package/mcp-server/dist/provision-types.js +174 -0
- package/mcp-server/dist/server.d.ts +6 -0
- package/mcp-server/dist/server.mjs +1925 -0
- package/mcp-server/dist/session.d.ts +85 -0
- package/mcp-server/dist/session.js +152 -0
- package/mcp-server/mcp-app.html +28 -0
- package/mcp-server/mcp-config.example.json +19 -0
- package/mcp-server/package-lock.json +4027 -0
- package/mcp-server/package.json +31 -0
- package/mcp-server/src/codegen.ts +189 -0
- package/mcp-server/src/mcp-app.css +360 -0
- package/mcp-server/src/mcp-app.ts +547 -0
- package/mcp-server/src/provision-types.ts +209 -0
- package/mcp-server/src/server.ts +2391 -0
- package/mcp-server/src/session.ts +194 -0
- package/mcp-server/tsconfig.json +16 -0
- package/mcp-server/vite.config.ts +23 -0
- package/package.json +158 -0
- package/schema.json +1046 -0
- package/scripts/generate-skills.js +94 -0
- package/sdk-log-formatter.js +1157 -0
- package/sdk.d.ts +1486 -0
- package/sdk.js +4336 -0
- package/setup/aws/cloudformation.yaml +463 -0
- package/setup/aws/disable-defender.sh +42 -0
- package/setup/aws/install-dev-runner.sh +79 -0
- package/setup/aws/spawn-runner.sh +289 -0
- package/test/captcha-solver.test.mjs +152 -0
- package/test/chrome-remote-debugging.test.mjs +66 -0
- package/test/duckduckgo/experiment.test.mjs +28 -0
- package/test/duckduckgo/setup.test.mjs +29 -0
- package/test/manual/debug-locate-response.js +82 -0
- package/test/manual/reconnect-provision.test.mjs +49 -0
- package/test/manual/test-console-logs.test.mjs +42 -0
- package/test/manual/test-find-api.js +73 -0
- package/test/manual/test-init.sh +54 -0
- package/test/manual/test-prompt-cache.js +97 -0
- package/test/manual/test-provision-auth.mjs +22 -0
- package/test/manual/test-sandbox-render.js +29 -0
- package/test/manual/test-sdk-methods.js +15 -0
- package/test/manual/test-sdk-refactor.js +53 -0
- package/test/manual/test-stack-trace.mjs +57 -0
- package/test/manual/verify-element-api.js +89 -0
- package/test/manual/verify-types.js +0 -0
- package/test/manual-unawaited-promise.test.mjs +31 -0
- package/vitest.config.mjs +58 -0
- package/vitest.runner.config.mjs +33 -0
- package/vscode-extension/.vscodeignore +12 -0
- package/vscode-extension/README.md +94 -0
- package/vscode-extension/media/icon.png +0 -0
- package/vscode-extension/package-lock.json +4126 -0
- package/vscode-extension/package.json +86 -0
- package/vscode-extension/src/extension.ts +829 -0
- package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
- package/vscode-extension/tsconfig.json +16 -0
|
@@ -0,0 +1,1157 @@
|
|
|
1
|
+
const chalk = require("chalk");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AWESOME Log formatter for TestDriver SDK ๐จ
|
|
5
|
+
* Provides beautiful, emoji-rich formatting with great DX for logs sent to dashcam
|
|
6
|
+
* ANSI codes are preserved through the log pipeline: SDK โ sandbox โ /tmp/testdriver.log โ dashcam
|
|
7
|
+
*
|
|
8
|
+
* Now with full UTF-8 and emoji support! ๐
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Duration threshold configurations for different contexts
|
|
12
|
+
const DURATION_THRESHOLDS = {
|
|
13
|
+
default: { fast: 3000, medium: 10000 },
|
|
14
|
+
action: { fast: 100, medium: 500 },
|
|
15
|
+
redraw: { fast: 5000, medium: 10000 },
|
|
16
|
+
quickAction: { fast: 50, medium: 200 },
|
|
17
|
+
test: { fast: 1000, medium: 5000 },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class SDKLogFormatter {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.testContext = {
|
|
23
|
+
currentTest: null,
|
|
24
|
+
currentFile: null,
|
|
25
|
+
startTime: null,
|
|
26
|
+
};
|
|
27
|
+
this.eventCount = 0;
|
|
28
|
+
this.useColors = options.colors !== false;
|
|
29
|
+
this.useEmojis = options.emojis !== false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Set the current test context from Vitest
|
|
34
|
+
* @param {Object} context - Test context with file, test name, etc.
|
|
35
|
+
*/
|
|
36
|
+
setTestContext(context) {
|
|
37
|
+
if (context.file) this.testContext.currentFile = context.file;
|
|
38
|
+
if (context.test) this.testContext.currentTest = context.test;
|
|
39
|
+
if (context.startTime) this.testContext.startTime = context.startTime;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get elapsed time since test start
|
|
44
|
+
* @returns {string} Formatted elapsed time
|
|
45
|
+
*/
|
|
46
|
+
getElapsedTime() {
|
|
47
|
+
if (!this.testContext.startTime) return "";
|
|
48
|
+
const elapsed = Date.now() - this.testContext.startTime;
|
|
49
|
+
const seconds = (elapsed / 1000).toFixed(2);
|
|
50
|
+
return `[${seconds}s]`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Add elapsed time to parts array if available
|
|
55
|
+
* @param {Array} parts - Array to push time string to
|
|
56
|
+
* @param {boolean} dim - Whether to dim the time string
|
|
57
|
+
*/
|
|
58
|
+
addTimestamp(parts, dim = true) {
|
|
59
|
+
const timeStr = this.getElapsedTime();
|
|
60
|
+
if (timeStr) {
|
|
61
|
+
parts.push(dim ? chalk.dim(timeStr) : timeStr);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get color function based on duration and thresholds
|
|
67
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
68
|
+
* @param {string} thresholdKey - Key from DURATION_THRESHOLDS
|
|
69
|
+
* @returns {Function} Chalk color function
|
|
70
|
+
*/
|
|
71
|
+
getDurationColor(durationMs, thresholdKey = "default") {
|
|
72
|
+
const thresholds = DURATION_THRESHOLDS[thresholdKey] || DURATION_THRESHOLDS.default;
|
|
73
|
+
if (durationMs < thresholds.fast) return chalk.green;
|
|
74
|
+
if (durationMs < thresholds.medium) return chalk.yellow;
|
|
75
|
+
return chalk.red;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Format duration with appropriate color
|
|
80
|
+
* @param {number|string} duration - Duration in ms
|
|
81
|
+
* @param {string} thresholdKey - Key from DURATION_THRESHOLDS
|
|
82
|
+
* @param {boolean} showSeconds - Show as seconds (true) or raw (false)
|
|
83
|
+
* @returns {string} Formatted duration string
|
|
84
|
+
*/
|
|
85
|
+
formatDurationColored(duration, thresholdKey = "default", showSeconds = true) {
|
|
86
|
+
const durationMs = parseInt(duration);
|
|
87
|
+
const color = this.getDurationColor(durationMs, thresholdKey);
|
|
88
|
+
const display = showSeconds ? `(${(durationMs / 1000).toFixed(1)}s)` : `(${duration})`;
|
|
89
|
+
return color(display);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Join metadata parts with separator
|
|
94
|
+
* @param {Array} metaParts - Array of metadata strings
|
|
95
|
+
* @returns {string} Joined metadata string with separators
|
|
96
|
+
*/
|
|
97
|
+
joinMetaParts(metaParts) {
|
|
98
|
+
if (metaParts.length === 0) return "";
|
|
99
|
+
return chalk.dim("ยท") + " " + metaParts.join(chalk.dim(" ยท "));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create an indented result line prefix (for child results)
|
|
104
|
+
* @returns {string} Indented arrow prefix
|
|
105
|
+
*/
|
|
106
|
+
getResultPrefix() {
|
|
107
|
+
return " " + chalk.dim("โ");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Format a nested action result line (scrolled, clicked, typed, pressed keys, etc.)
|
|
112
|
+
* @param {string} message - The action message (e.g., "scrolled down 300px", "pressed keys: tab")
|
|
113
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
114
|
+
* @returns {string} Formatted nested result line
|
|
115
|
+
*/
|
|
116
|
+
formatNestedAction(message, durationMs) {
|
|
117
|
+
return this.getResultPrefix() + " " + chalk.dim(message) + " " + this.formatDurationColored(durationMs);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Format a redraw/idle wait completion line
|
|
122
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
123
|
+
* @returns {string} Formatted redraw complete line
|
|
124
|
+
*/
|
|
125
|
+
formatRedrawComplete(durationMs) {
|
|
126
|
+
return this.formatNestedAction("flake protection", durationMs);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Format a scroll action result
|
|
131
|
+
* @param {string} direction - Scroll direction (up, down, left, right)
|
|
132
|
+
* @param {number} amount - Scroll amount in pixels
|
|
133
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
134
|
+
* @returns {string} Formatted scroll result line
|
|
135
|
+
*/
|
|
136
|
+
formatScrollResult(direction, amount, durationMs) {
|
|
137
|
+
return this.formatNestedAction(`scrolled ${direction} ${amount}px`, durationMs);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Format a click action result
|
|
142
|
+
* @param {string} button - Button type (left, right, middle)
|
|
143
|
+
* @param {number} x - X coordinate
|
|
144
|
+
* @param {number} y - Y coordinate
|
|
145
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
146
|
+
* @returns {string} Formatted click result line
|
|
147
|
+
*/
|
|
148
|
+
formatClickResult(button, x, y, durationMs) {
|
|
149
|
+
return this.formatNestedAction(`click ${button} clicking at ${x}, ${y}`, durationMs);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Format a type action result
|
|
154
|
+
* @param {string} text - Text that was typed (or "****" for secrets)
|
|
155
|
+
* @param {boolean} isSecret - Whether the text is a secret
|
|
156
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
157
|
+
* @returns {string} Formatted type result line
|
|
158
|
+
*/
|
|
159
|
+
formatTypeResult(text, isSecret, durationMs) {
|
|
160
|
+
const displayText = isSecret ? "secret ****" : `"${text}"`;
|
|
161
|
+
return this.formatNestedAction(`typed ${displayText}`, durationMs);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Format a press keys action result
|
|
166
|
+
* @param {string} keysDisplay - Keys that were pressed (comma-separated)
|
|
167
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
168
|
+
* @returns {string} Formatted press keys result line
|
|
169
|
+
*/
|
|
170
|
+
formatPressKeysResult(keysDisplay, durationMs) {
|
|
171
|
+
return this.formatNestedAction(`pressed keys: ${keysDisplay}`, durationMs);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Format a nested code display line (for exec commands)
|
|
176
|
+
* @param {string} codeDisplay - The code to display
|
|
177
|
+
* @returns {string} Formatted code line
|
|
178
|
+
*/
|
|
179
|
+
formatCodeLine(codeDisplay) {
|
|
180
|
+
return this.getResultPrefix() + " " + chalk.dim(codeDisplay);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Format an exec complete result
|
|
185
|
+
* @param {number} exitCode - The exit code
|
|
186
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
187
|
+
* @returns {string} Formatted exec result line
|
|
188
|
+
*/
|
|
189
|
+
formatExecComplete(exitCode, durationMs) {
|
|
190
|
+
const statusText = exitCode !== 0
|
|
191
|
+
? `failed (exit code ${exitCode})`
|
|
192
|
+
: `complete (exit code 0)`;
|
|
193
|
+
const statusColor = exitCode !== 0 ? chalk.red : chalk.green;
|
|
194
|
+
|
|
195
|
+
return this.formatResultLine(
|
|
196
|
+
statusText,
|
|
197
|
+
statusColor,
|
|
198
|
+
{ duration: durationMs },
|
|
199
|
+
"action"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Format a log message in Vitest style
|
|
205
|
+
* @param {string} type - Log type (info, success, error, action, debug)
|
|
206
|
+
* @param {string} message - The message to format
|
|
207
|
+
* @param {Object} meta - Additional metadata
|
|
208
|
+
* @returns {string} Formatted log message
|
|
209
|
+
*/
|
|
210
|
+
format(type, message, meta = {}) {
|
|
211
|
+
this.eventCount++;
|
|
212
|
+
|
|
213
|
+
const parts = [];
|
|
214
|
+
|
|
215
|
+
// Add timestamp/elapsed time
|
|
216
|
+
this.addTimestamp(parts, false);
|
|
217
|
+
|
|
218
|
+
// Add type prefix with color
|
|
219
|
+
const prefix = this.getPrefix(type);
|
|
220
|
+
if (prefix) parts.push(prefix);
|
|
221
|
+
|
|
222
|
+
// Add message
|
|
223
|
+
parts.push(this.formatMessage(type, message));
|
|
224
|
+
|
|
225
|
+
// Add metadata if present
|
|
226
|
+
if (meta.duration) {
|
|
227
|
+
parts.push(chalk.dim(`(${meta.duration})`));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return parts.join(" ");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get prefix for log type with AWESOME colors and emojis ๐จ
|
|
235
|
+
* @param {string} type - Log type
|
|
236
|
+
* @returns {string} Colored prefix with emoji
|
|
237
|
+
*/
|
|
238
|
+
getPrefix(type) {
|
|
239
|
+
if (!this.useEmojis) {
|
|
240
|
+
// Fallback to simple symbols without emojis
|
|
241
|
+
const simplePrefixes = {
|
|
242
|
+
info: chalk.blue("โน"),
|
|
243
|
+
success: chalk.green("โ"),
|
|
244
|
+
error: chalk.red("โ"),
|
|
245
|
+
action: chalk.cyan("โ"),
|
|
246
|
+
debug: chalk.gray("โ"),
|
|
247
|
+
find: chalk.magenta("โ"),
|
|
248
|
+
click: chalk.cyan("โธ"),
|
|
249
|
+
type: chalk.yellow("โจ"),
|
|
250
|
+
assert: chalk.green("โ"),
|
|
251
|
+
scroll: chalk.blue("โ"),
|
|
252
|
+
hover: chalk.cyan("โ"),
|
|
253
|
+
wait: chalk.yellow("โฑ"),
|
|
254
|
+
connect: chalk.green("โก"),
|
|
255
|
+
disconnect: chalk.red("โน"),
|
|
256
|
+
};
|
|
257
|
+
return simplePrefixes[type] || chalk.gray("โข");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const prefixes = {
|
|
261
|
+
// Core actions - hand gestures
|
|
262
|
+
info: chalk.blue("โน๏ธ"),
|
|
263
|
+
success: chalk.green("โ
"),
|
|
264
|
+
error: chalk.red("โ"),
|
|
265
|
+
warning: chalk.yellow("โ ๏ธ"),
|
|
266
|
+
|
|
267
|
+
// Finding elements
|
|
268
|
+
find: chalk.magenta("๐"),
|
|
269
|
+
findAll: chalk.magenta("๐"),
|
|
270
|
+
|
|
271
|
+
// Mouse actions
|
|
272
|
+
click: chalk.cyan("๐"),
|
|
273
|
+
doubleClick: chalk.cyan("๐๐"),
|
|
274
|
+
rightClick: chalk.cyan("๐ฑ๏ธ"),
|
|
275
|
+
hover: chalk.cyan("๐"),
|
|
276
|
+
drag: chalk.cyan("โ"),
|
|
277
|
+
|
|
278
|
+
// Keyboard actions
|
|
279
|
+
type: chalk.yellow("โจ๏ธ "),
|
|
280
|
+
pressKeys: chalk.yellow("๐น"),
|
|
281
|
+
|
|
282
|
+
// Navigation
|
|
283
|
+
scroll: chalk.blue("๐"),
|
|
284
|
+
scrollUp: chalk.blue("โฌ๏ธ"),
|
|
285
|
+
scrollDown: chalk.blue("โฌ๏ธ"),
|
|
286
|
+
navigate: chalk.blue("๐งญ"),
|
|
287
|
+
|
|
288
|
+
// Validation
|
|
289
|
+
assert: chalk.green("โ
"),
|
|
290
|
+
verify: chalk.green("๐"),
|
|
291
|
+
extract: chalk.blue("๐ง "),
|
|
292
|
+
|
|
293
|
+
// System
|
|
294
|
+
connect: chalk.green("๐"),
|
|
295
|
+
disconnect: chalk.red("๐"),
|
|
296
|
+
screenshot: chalk.blue("๐ธ"),
|
|
297
|
+
wait: chalk.yellow("โณ"),
|
|
298
|
+
|
|
299
|
+
// Focus & Windows
|
|
300
|
+
focusApplication: chalk.cyan("๐ฏ"),
|
|
301
|
+
|
|
302
|
+
// Cache
|
|
303
|
+
cacheHit: chalk.yellow("โก"),
|
|
304
|
+
cacheMiss: chalk.gray("๐ค"),
|
|
305
|
+
|
|
306
|
+
// Debug
|
|
307
|
+
debug: chalk.gray("๐ง"),
|
|
308
|
+
|
|
309
|
+
// Default
|
|
310
|
+
action: chalk.cyan("โถ๏ธ "),
|
|
311
|
+
};
|
|
312
|
+
return prefixes[type] || chalk.gray("โข");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Format the message content with appropriate styling
|
|
317
|
+
* @param {string} type - Log type
|
|
318
|
+
* @param {string} message - Raw message
|
|
319
|
+
* @returns {string} Formatted message
|
|
320
|
+
*/
|
|
321
|
+
formatMessage(type, message) {
|
|
322
|
+
if (!this.useColors) return message;
|
|
323
|
+
|
|
324
|
+
const formatters = {
|
|
325
|
+
success: (msg) => chalk.green(msg),
|
|
326
|
+
error: (msg) => chalk.red(msg),
|
|
327
|
+
debug: (msg) => chalk.dim(msg),
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return formatters[type] ? formatters[type](message) : message;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Format a "finding" style message (when search starts) ๐
|
|
335
|
+
* @param {string} prefixType - Prefix type for getPrefix
|
|
336
|
+
* @param {string} label - Action label (e.g., "Finding", "Finding All", "Asserting")
|
|
337
|
+
* @param {string} description - Element/assertion description
|
|
338
|
+
* @returns {string} Formatted message
|
|
339
|
+
*/
|
|
340
|
+
formatFindingStyle(prefixType, label, description) {
|
|
341
|
+
const parts = [];
|
|
342
|
+
this.addTimestamp(parts);
|
|
343
|
+
parts.push(this.getPrefix(prefixType));
|
|
344
|
+
parts.push(chalk.bold.cyan(label));
|
|
345
|
+
parts.push(chalk.cyan(`"${description}"`));
|
|
346
|
+
return parts.join(" ");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Format an element finding message (when search starts) ๐
|
|
351
|
+
* @param {string} description - Element description
|
|
352
|
+
* @returns {string} Formatted message
|
|
353
|
+
*/
|
|
354
|
+
formatElementFinding(description) {
|
|
355
|
+
return this.formatFindingStyle("find", "Finding", description);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Build common metadata parts for result messages
|
|
360
|
+
* @param {Object} meta - Metadata object
|
|
361
|
+
* @param {string} thresholdKey - Duration threshold key
|
|
362
|
+
* @returns {Array} Array of formatted metadata strings
|
|
363
|
+
*/
|
|
364
|
+
buildResultMetaParts(meta, thresholdKey = "default") {
|
|
365
|
+
const metaParts = [];
|
|
366
|
+
|
|
367
|
+
if (meta.x !== undefined && meta.y !== undefined) {
|
|
368
|
+
metaParts.push(chalk.dim.gray(`๐ (${meta.x}, ${meta.y})`));
|
|
369
|
+
}
|
|
370
|
+
if (meta.selectorId && meta.consoleUrl) {
|
|
371
|
+
const cacheUrl = `${meta.consoleUrl}/cache/${meta.selectorId}`;
|
|
372
|
+
metaParts.push(chalk.blue.underline(cacheUrl));
|
|
373
|
+
}
|
|
374
|
+
if (meta.error) {
|
|
375
|
+
metaParts.push(chalk.dim.red(meta.error));
|
|
376
|
+
}
|
|
377
|
+
if (meta.cacheHit) {
|
|
378
|
+
metaParts.push(chalk.bold.yellow("โก cached"));
|
|
379
|
+
if (meta.validated) {
|
|
380
|
+
const confStr = meta.validationConfidence !== null && meta.validationConfidence !== undefined
|
|
381
|
+
? ` ${(meta.validationConfidence * 100).toFixed(1)}%`
|
|
382
|
+
: '';
|
|
383
|
+
metaParts.push(chalk.green(`โ
validated${confStr}`));
|
|
384
|
+
if (meta.coordsUpdated) {
|
|
385
|
+
metaParts.push(chalk.dim.yellow(`โ coords shifted`));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (meta.confidence !== undefined && meta.confidence !== null) {
|
|
390
|
+
metaParts.push(chalk.dim.gray(`confidence: ${meta.confidence}`));
|
|
391
|
+
}
|
|
392
|
+
if (meta.reasoning) {
|
|
393
|
+
metaParts.push(chalk.dim.gray(`reasoning: ${meta.reasoning}`));
|
|
394
|
+
}
|
|
395
|
+
// Duration always last
|
|
396
|
+
if (meta.duration) {
|
|
397
|
+
metaParts.push(this.formatDurationColored(meta.duration, thresholdKey));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return metaParts;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Format a result line (indented child result)
|
|
405
|
+
* @param {string} statusText - Status text (e.g., "found", "not found")
|
|
406
|
+
* @param {Function} statusColor - Chalk color function for status
|
|
407
|
+
* @param {Object} meta - Metadata object
|
|
408
|
+
* @param {string} thresholdKey - Duration threshold key
|
|
409
|
+
* @returns {string} Formatted result line
|
|
410
|
+
*/
|
|
411
|
+
formatResultLine(statusText, statusColor, meta = {}, thresholdKey = "default") {
|
|
412
|
+
const parts = [];
|
|
413
|
+
this.addTimestamp(parts);
|
|
414
|
+
parts.push(this.getResultPrefix());
|
|
415
|
+
parts.push(statusColor(statusText));
|
|
416
|
+
|
|
417
|
+
const metaParts = this.buildResultMetaParts(meta, thresholdKey);
|
|
418
|
+
if (metaParts.length > 0) {
|
|
419
|
+
parts.push(this.joinMetaParts(metaParts));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return parts.join(" ");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Format an element found message with AWESOME styling ๐ฏ
|
|
427
|
+
* @param {string} description - Element description
|
|
428
|
+
* @param {Object} meta - Element metadata (coordinates, duration, cache hit)
|
|
429
|
+
* @returns {string} Formatted message
|
|
430
|
+
*/
|
|
431
|
+
formatElementFound(description, meta = {}) {
|
|
432
|
+
return this.formatResultLine("found", chalk.green, meta);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Format an element not found message with styling โ
|
|
437
|
+
* @param {string} description - Element description
|
|
438
|
+
* @param {Object} meta - Metadata (duration, error)
|
|
439
|
+
* @returns {string} Formatted message
|
|
440
|
+
*/
|
|
441
|
+
formatElementNotFound(description, meta = {}) {
|
|
442
|
+
return this.formatResultLine("not found", chalk.red, meta);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Format a finding all message (when search starts) ๐
|
|
447
|
+
* @param {string} description - Element description
|
|
448
|
+
* @returns {string} Formatted message
|
|
449
|
+
*/
|
|
450
|
+
formatElementsFinding(description) {
|
|
451
|
+
return this.formatFindingStyle("findAll", "Finding All", description);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Format a found all message with AWESOME styling ๐ฏ
|
|
456
|
+
* @param {string} description - Element description
|
|
457
|
+
* @param {number} count - Number of elements found
|
|
458
|
+
* @param {Object} meta - Metadata (duration, cache hit)
|
|
459
|
+
* @returns {string} Formatted message
|
|
460
|
+
*/
|
|
461
|
+
formatElementsFound(description, count, meta = {}) {
|
|
462
|
+
const parts = [];
|
|
463
|
+
this.addTimestamp(parts);
|
|
464
|
+
parts.push(this.getResultPrefix());
|
|
465
|
+
parts.push(chalk.green(`found ${count} elements`));
|
|
466
|
+
|
|
467
|
+
const metaParts = [];
|
|
468
|
+
if (meta.cacheHit) {
|
|
469
|
+
metaParts.push(chalk.bold.yellow("โก cached"));
|
|
470
|
+
}
|
|
471
|
+
if (meta.duration) {
|
|
472
|
+
metaParts.push(this.formatDurationColored(meta.duration));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (metaParts.length > 0) {
|
|
476
|
+
parts.push(this.joinMetaParts(metaParts));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return parts.join(" ");
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Format a single-line findAll message (combines finding + result) ๐
|
|
484
|
+
* @param {string} description - Element description
|
|
485
|
+
* @param {number} count - Number of elements found
|
|
486
|
+
* @param {Object} meta - Metadata (duration, cache hit)
|
|
487
|
+
* @returns {string} Formatted message
|
|
488
|
+
*/
|
|
489
|
+
formatFindAllSingleLine(description, count, meta = {}) {
|
|
490
|
+
const parts = [];
|
|
491
|
+
this.addTimestamp(parts);
|
|
492
|
+
parts.push(this.getPrefix("findAll"));
|
|
493
|
+
parts.push(chalk.bold.magenta("Finding All"));
|
|
494
|
+
parts.push(chalk.cyan(`"${description}"`));
|
|
495
|
+
|
|
496
|
+
const metaParts = [];
|
|
497
|
+
|
|
498
|
+
// Add count with appropriate coloring
|
|
499
|
+
if (count > 0) {
|
|
500
|
+
metaParts.push(chalk.green(`found ${count}`));
|
|
501
|
+
} else {
|
|
502
|
+
metaParts.push(chalk.yellow("found 0"));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Add cache hit indicator
|
|
506
|
+
if (meta.cacheHit) {
|
|
507
|
+
metaParts.push(chalk.bold.yellow("โก cached"));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Add duration
|
|
511
|
+
if (meta.duration) {
|
|
512
|
+
metaParts.push(this.formatDurationColored(meta.duration));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (metaParts.length > 0) {
|
|
516
|
+
parts.push(this.joinMetaParts(metaParts));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return parts.join(" ");
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Format an asserting message (when assertion starts) โ
|
|
524
|
+
* @param {string} assertion - What is being asserted
|
|
525
|
+
* @returns {string} Formatted message
|
|
526
|
+
*/
|
|
527
|
+
formatAsserting(assertion) {
|
|
528
|
+
return this.formatFindingStyle("assert", "Asserting", assertion);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Format the assertion result as a subtask line
|
|
532
|
+
* @param {boolean} passed - Whether assertion passed
|
|
533
|
+
* @param {string} response - The AI response message
|
|
534
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
535
|
+
* @param {boolean} cacheHit - Whether the result was from cache
|
|
536
|
+
* @returns {string} Formatted result line
|
|
537
|
+
*/
|
|
538
|
+
formatAssertResult(passed, response, durationMs, cacheHit = false) {
|
|
539
|
+
const parts = [];
|
|
540
|
+
this.addTimestamp(parts);
|
|
541
|
+
parts.push(this.getResultPrefix());
|
|
542
|
+
|
|
543
|
+
if (passed) {
|
|
544
|
+
parts.push(chalk.green("passed"));
|
|
545
|
+
} else {
|
|
546
|
+
parts.push(chalk.red("failed"));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Add cache hit indicator (like find does)
|
|
550
|
+
if (cacheHit) {
|
|
551
|
+
parts.push(chalk.dim("ยท"));
|
|
552
|
+
parts.push(chalk.bold.yellow("โก cached"));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Add the response message (trimmed)
|
|
556
|
+
if (response) {
|
|
557
|
+
const trimmedResponse = response.trim().split('\n')[0]; // First line only
|
|
558
|
+
parts.push(chalk.dim(trimmedResponse));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Add duration
|
|
562
|
+
if (durationMs) {
|
|
563
|
+
parts.push(this.formatDurationColored(durationMs, "action"));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return parts.join(" ");
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Action color mapping (shared between formatAction and formatActionComplete)
|
|
570
|
+
static ACTION_COLORS = {
|
|
571
|
+
click: chalk.bold.cyan,
|
|
572
|
+
hover: chalk.bold.blue,
|
|
573
|
+
type: chalk.bold.yellow,
|
|
574
|
+
scroll: chalk.bold.magenta,
|
|
575
|
+
assert: chalk.bold.green,
|
|
576
|
+
wait: chalk.bold.yellow,
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Build action message parts (shared logic for formatAction and formatActionComplete)
|
|
581
|
+
* @param {string} action - Action type
|
|
582
|
+
* @param {string} description - Description or target
|
|
583
|
+
* @returns {Array} Array of formatted parts
|
|
584
|
+
*/
|
|
585
|
+
buildActionParts(action, description) {
|
|
586
|
+
const parts = [];
|
|
587
|
+
this.addTimestamp(parts);
|
|
588
|
+
|
|
589
|
+
const actionKey = action.toLowerCase().replace(/\s+/g, "");
|
|
590
|
+
parts.push(this.getPrefix(actionKey));
|
|
591
|
+
|
|
592
|
+
const actionText = action.charAt(0).toUpperCase() + action.slice(1).toLowerCase();
|
|
593
|
+
const colorFn = SDKLogFormatter.ACTION_COLORS[actionKey] || chalk.bold.white;
|
|
594
|
+
parts.push(colorFn(actionText));
|
|
595
|
+
|
|
596
|
+
if (description) {
|
|
597
|
+
parts.push(chalk.cyan(`"${description}"`));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return { parts, actionKey };
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Format an action message with AWESOME emojis! ๐ฌ
|
|
605
|
+
* @param {string} action - Action type
|
|
606
|
+
* @param {string} description - Description or target
|
|
607
|
+
* @param {Object} meta - Action metadata
|
|
608
|
+
* @returns {string} Formatted message
|
|
609
|
+
*/
|
|
610
|
+
formatAction(action, description, meta = {}) {
|
|
611
|
+
const { parts } = this.buildActionParts(action, description);
|
|
612
|
+
|
|
613
|
+
const metaParts = [];
|
|
614
|
+
if (meta.text) {
|
|
615
|
+
metaParts.push(chalk.gray(`โ ${chalk.white(meta.text)}`));
|
|
616
|
+
}
|
|
617
|
+
if (meta.duration) {
|
|
618
|
+
metaParts.push(chalk.dim(`โฑ๏ธ ${this.formatDurationColored(meta.duration, "quickAction", false)}`));
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (metaParts.length > 0) {
|
|
622
|
+
parts.push(this.joinMetaParts(metaParts));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return parts.join(" ");
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Format an action complete message with separate action and redraw durations ๐ฌ
|
|
630
|
+
* @param {string} action - Action type
|
|
631
|
+
* @param {string} description - Description or target
|
|
632
|
+
* @param {Object} meta - Action metadata
|
|
633
|
+
* @param {number} meta.actionDuration - Duration of the action itself in ms
|
|
634
|
+
* @param {number} meta.redrawDuration - Duration of the redraw wait in ms
|
|
635
|
+
* @param {boolean} meta.cacheHit - Whether cache was hit
|
|
636
|
+
* @returns {string} Formatted message
|
|
637
|
+
*/
|
|
638
|
+
formatActionComplete(action, description, meta = {}) {
|
|
639
|
+
const { parts } = this.buildActionParts(action, description);
|
|
640
|
+
|
|
641
|
+
const metaParts = [];
|
|
642
|
+
|
|
643
|
+
if (meta.actionDuration !== undefined) {
|
|
644
|
+
const durationMs = parseInt(meta.actionDuration);
|
|
645
|
+
const durationSec = (durationMs / 1000).toFixed(1) + 's';
|
|
646
|
+
const color = this.getDurationColor(durationMs, "action");
|
|
647
|
+
metaParts.push(chalk.dim(`โก ${color(durationSec)}`));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (meta.redrawDuration !== undefined) {
|
|
651
|
+
const durationMs = parseInt(meta.redrawDuration);
|
|
652
|
+
const durationSec = (durationMs / 1000).toFixed(1) + 's';
|
|
653
|
+
const color = this.getDurationColor(durationMs, "redraw");
|
|
654
|
+
metaParts.push(chalk.dim(`๐ ${color(durationSec)}`));
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (meta.cacheHit) {
|
|
658
|
+
metaParts.push(chalk.bold.yellow("โก cached"));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (metaParts.length > 0) {
|
|
662
|
+
parts.push(this.joinMetaParts(metaParts));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return parts.join(" ");
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Format an assertion message with beautiful status indicators ๐ฏ
|
|
670
|
+
* @param {string} assertion - What is being asserted
|
|
671
|
+
* @param {boolean} passed - Whether assertion passed
|
|
672
|
+
* @param {Object} meta - Assertion metadata
|
|
673
|
+
* @returns {string} Formatted message
|
|
674
|
+
*/
|
|
675
|
+
formatAssertion(assertion, passed, meta = {}) {
|
|
676
|
+
const parts = [];
|
|
677
|
+
this.addTimestamp(parts);
|
|
678
|
+
|
|
679
|
+
if (passed) {
|
|
680
|
+
parts.push(this.getPrefix("success"));
|
|
681
|
+
parts.push(chalk.bold.green("Assert"));
|
|
682
|
+
parts.push(chalk.cyan(`"${assertion}"`));
|
|
683
|
+
parts.push(chalk.dim("ยท"));
|
|
684
|
+
parts.push(chalk.bold.green("โ PASSED"));
|
|
685
|
+
} else {
|
|
686
|
+
parts.push(this.getPrefix("error"));
|
|
687
|
+
parts.push(chalk.bold.red("Assert"));
|
|
688
|
+
parts.push(chalk.cyan(`"${assertion}"`));
|
|
689
|
+
parts.push(chalk.dim("ยท"));
|
|
690
|
+
parts.push(chalk.bold.red("โ FAILED"));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (meta.duration) {
|
|
694
|
+
parts.push(chalk.dim("ยท"));
|
|
695
|
+
parts.push(chalk.dim(`โฑ๏ธ ${this.formatDurationColored(meta.duration, "action", false)}`));
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return parts.join(" ");
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Format an error message with clear visual indicators ๐จ
|
|
703
|
+
* @param {string} message - Error message
|
|
704
|
+
* @param {Error} error - Error object
|
|
705
|
+
* @returns {string} Formatted error
|
|
706
|
+
*/
|
|
707
|
+
formatError(message, error) {
|
|
708
|
+
const parts = [];
|
|
709
|
+
this.addTimestamp(parts);
|
|
710
|
+
parts.push(this.getPrefix("error"));
|
|
711
|
+
parts.push(chalk.red.bold(message));
|
|
712
|
+
|
|
713
|
+
if (error && error.message) {
|
|
714
|
+
parts.push(chalk.dim("โ"));
|
|
715
|
+
parts.push(chalk.red(error.message));
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return parts.join(" ");
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Format a connection/disconnection message ๐
|
|
723
|
+
* @param {string} type - 'connect' or 'disconnect'
|
|
724
|
+
* @param {Object} meta - Connection metadata
|
|
725
|
+
* @returns {string} Formatted message
|
|
726
|
+
*/
|
|
727
|
+
formatConnection(type, meta = {}) {
|
|
728
|
+
const parts = [];
|
|
729
|
+
this.addTimestamp(parts);
|
|
730
|
+
parts.push(this.getPrefix(type));
|
|
731
|
+
|
|
732
|
+
if (type === "connect") {
|
|
733
|
+
parts.push(chalk.bold.green("Connected"));
|
|
734
|
+
if (meta.sandboxId) {
|
|
735
|
+
parts.push(chalk.dim("ยท"));
|
|
736
|
+
parts.push(chalk.cyan(`Sandbox: ${meta.sandboxId}`));
|
|
737
|
+
}
|
|
738
|
+
if (meta.os) {
|
|
739
|
+
parts.push(chalk.dim("ยท"));
|
|
740
|
+
parts.push(chalk.gray(`OS: ${meta.os}`));
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
parts.push(chalk.bold.yellow("Disconnected"));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return parts.join(" ");
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Format a screenshot message ๐ธ
|
|
751
|
+
* @param {Object} meta - Screenshot metadata
|
|
752
|
+
* @returns {string} Formatted message
|
|
753
|
+
*/
|
|
754
|
+
formatScreenshot(meta = {}) {
|
|
755
|
+
const parts = [];
|
|
756
|
+
this.addTimestamp(parts);
|
|
757
|
+
parts.push(this.getPrefix("screenshot"));
|
|
758
|
+
parts.push(chalk.bold.blue("Screenshot"));
|
|
759
|
+
|
|
760
|
+
if (meta.path) {
|
|
761
|
+
parts.push(chalk.dim("ยท"));
|
|
762
|
+
parts.push(chalk.cyan(meta.path));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (meta.size) {
|
|
766
|
+
parts.push(chalk.dim("ยท"));
|
|
767
|
+
parts.push(chalk.gray(`${meta.size}`));
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return parts.join(" ");
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Format a cache status message โก
|
|
775
|
+
* @param {boolean} hit - Whether it was a cache hit
|
|
776
|
+
* @param {Object} meta - Cache metadata
|
|
777
|
+
* @returns {string} Formatted message
|
|
778
|
+
*/
|
|
779
|
+
formatCacheStatus(hit, meta = {}) {
|
|
780
|
+
const parts = [];
|
|
781
|
+
parts.push(this.getPrefix(hit ? "cacheHit" : "cacheMiss"));
|
|
782
|
+
|
|
783
|
+
if (hit) {
|
|
784
|
+
parts.push(chalk.bold.yellow("Cache HIT"));
|
|
785
|
+
if (meta.similarity !== undefined) {
|
|
786
|
+
const similarity = (meta.similarity * 100).toFixed(1);
|
|
787
|
+
parts.push(chalk.dim("ยท"));
|
|
788
|
+
parts.push(chalk.green(`${similarity}% similar`));
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
parts.push(chalk.dim.gray("Cache MISS"));
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (meta.strategy) {
|
|
795
|
+
parts.push(chalk.dim("ยท"));
|
|
796
|
+
parts.push(chalk.gray(meta.strategy));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return parts.join(" ");
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Create a beautiful section header with box drawing ๐ฆ
|
|
804
|
+
* @param {string} title - Section title
|
|
805
|
+
* @param {string} emoji - Optional emoji to prefix
|
|
806
|
+
* @returns {string} Formatted header
|
|
807
|
+
*/
|
|
808
|
+
formatHeader(title, emoji = "โจ") {
|
|
809
|
+
const width = Math.min(60, Math.max(title.length + 4, 40));
|
|
810
|
+
const topLine = chalk.dim("โญ" + "โ".repeat(width - 2) + "โฎ");
|
|
811
|
+
const titleLine =
|
|
812
|
+
`${chalk.dim("โ")} ${emoji} ${chalk.bold.white(title)}`.padEnd(width + 20) + chalk.dim("โ");
|
|
813
|
+
const bottomLine = chalk.dim("โฐ" + "โ".repeat(width - 2) + "โฏ");
|
|
814
|
+
return `\n${topLine}\n${titleLine}\n${bottomLine}\n`;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Format a simple divider
|
|
819
|
+
* @param {string} char - Character to use for divider
|
|
820
|
+
* @returns {string} Formatted divider
|
|
821
|
+
*/
|
|
822
|
+
formatDivider(char = "โ") {
|
|
823
|
+
return chalk.dim(char.repeat(60));
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Format a beautiful summary line with stats ๐
|
|
828
|
+
* @param {Object} stats - Test statistics
|
|
829
|
+
* @returns {string} Formatted summary
|
|
830
|
+
*/
|
|
831
|
+
formatSummary(stats) {
|
|
832
|
+
const parts = [];
|
|
833
|
+
|
|
834
|
+
if (stats.passed > 0) {
|
|
835
|
+
parts.push(chalk.bold.green(`โ ${stats.passed} passed`));
|
|
836
|
+
}
|
|
837
|
+
if (stats.failed > 0) {
|
|
838
|
+
parts.push(chalk.bold.red(`โ ${stats.failed} failed`));
|
|
839
|
+
}
|
|
840
|
+
if (stats.skipped > 0) {
|
|
841
|
+
parts.push(chalk.yellow(`โ ${stats.skipped} skipped`));
|
|
842
|
+
}
|
|
843
|
+
if (stats.total > 0) {
|
|
844
|
+
parts.push(chalk.dim(`${stats.total} total`));
|
|
845
|
+
}
|
|
846
|
+
if (stats.duration) {
|
|
847
|
+
parts.push(chalk.dim(`โฑ๏ธ ${stats.duration}`));
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const divider = this.formatDivider();
|
|
851
|
+
const separator = chalk.dim(" โ ");
|
|
852
|
+
return `\n${divider}\n${parts.join(separator)}\n${divider}\n`;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Format a progress indicator ๐
|
|
857
|
+
* @param {number} current - Current step
|
|
858
|
+
* @param {number} total - Total steps
|
|
859
|
+
* @param {string} message - Progress message
|
|
860
|
+
* @returns {string} Formatted progress
|
|
861
|
+
*/
|
|
862
|
+
formatProgress(current, total, message = "") {
|
|
863
|
+
const percentage = Math.round((current / total) * 100);
|
|
864
|
+
const barWidth = 20;
|
|
865
|
+
const filled = Math.round((current / total) * barWidth);
|
|
866
|
+
const empty = barWidth - filled;
|
|
867
|
+
|
|
868
|
+
const bar = chalk.green("โ".repeat(filled)) + chalk.dim("โ".repeat(empty));
|
|
869
|
+
const stats = chalk.dim(`${current}/${total}`);
|
|
870
|
+
|
|
871
|
+
const parts = [
|
|
872
|
+
chalk.bold("Progress"),
|
|
873
|
+
bar,
|
|
874
|
+
chalk.cyan(`${percentage}%`),
|
|
875
|
+
stats,
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
if (message) {
|
|
879
|
+
parts.push(chalk.dim("ยท"));
|
|
880
|
+
parts.push(chalk.gray(message));
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return parts.join(" ");
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Format a waiting/loading message โณ
|
|
888
|
+
* @param {string} message - What we're waiting for
|
|
889
|
+
* @param {number} elapsed - Elapsed time in ms
|
|
890
|
+
* @returns {string} Formatted waiting message
|
|
891
|
+
*/
|
|
892
|
+
formatWaiting(message, elapsed) {
|
|
893
|
+
const parts = [];
|
|
894
|
+
parts.push(this.getPrefix("wait"));
|
|
895
|
+
parts.push(chalk.bold.yellow("Waiting"));
|
|
896
|
+
parts.push(chalk.cyan(message));
|
|
897
|
+
|
|
898
|
+
if (elapsed) {
|
|
899
|
+
const seconds = (elapsed / 1000).toFixed(1);
|
|
900
|
+
parts.push(chalk.dim("ยท"));
|
|
901
|
+
parts.push(chalk.gray(`${seconds}s`));
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return parts.join(" ");
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Format test start message ๐
|
|
909
|
+
* @param {string} testName - Name of the test
|
|
910
|
+
* @returns {string} Formatted test start
|
|
911
|
+
*/
|
|
912
|
+
formatTestStart(testName) {
|
|
913
|
+
return `\n${chalk.bold.cyan("โถ๏ธ Running:")} ${chalk.white(testName)}\n`;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Format test end message with result ๐
|
|
918
|
+
* @param {string} testName - Name of the test
|
|
919
|
+
* @param {boolean} passed - Whether test passed
|
|
920
|
+
* @param {number} duration - Test duration in ms
|
|
921
|
+
* @returns {string} Formatted test end
|
|
922
|
+
*/
|
|
923
|
+
formatTestEnd(testName, passed, duration) {
|
|
924
|
+
const parts = [];
|
|
925
|
+
|
|
926
|
+
if (passed) {
|
|
927
|
+
parts.push(chalk.bold.green("โ
PASSED"));
|
|
928
|
+
} else {
|
|
929
|
+
parts.push(chalk.bold.red("โ FAILED"));
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
parts.push(chalk.white(testName));
|
|
933
|
+
|
|
934
|
+
if (duration) {
|
|
935
|
+
const seconds = (duration / 1000).toFixed(2);
|
|
936
|
+
const color = this.getDurationColor(duration, "test");
|
|
937
|
+
parts.push(chalk.dim("ยท"));
|
|
938
|
+
parts.push(color(`${seconds}s`));
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return `\n${parts.join(" ")}\n`;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Format ai() start message - provides visual scope boundary
|
|
946
|
+
* @param {string} task - The task being executed
|
|
947
|
+
* @returns {string} Formatted ai start message
|
|
948
|
+
*/
|
|
949
|
+
formatAIStart(task) {
|
|
950
|
+
const parts = [];
|
|
951
|
+
this.addTimestamp(parts);
|
|
952
|
+
parts.push(this.getPrefix("action"));
|
|
953
|
+
parts.push(chalk.bold.cyan("AI"));
|
|
954
|
+
parts.push(chalk.cyan(`"${task}"`));
|
|
955
|
+
return parts.join(" ");
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Format ai() completion message - provides visual scope boundary
|
|
960
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
961
|
+
* @param {boolean} success - Whether the ai completed successfully
|
|
962
|
+
* @param {string} [error] - Error message if failed
|
|
963
|
+
* @returns {string} Formatted ai complete message
|
|
964
|
+
*/
|
|
965
|
+
formatAIComplete(durationMs, success, error = null) {
|
|
966
|
+
const parts = [];
|
|
967
|
+
this.addTimestamp(parts);
|
|
968
|
+
parts.push(this.getResultPrefix());
|
|
969
|
+
|
|
970
|
+
if (success) {
|
|
971
|
+
parts.push(chalk.green("complete"));
|
|
972
|
+
} else {
|
|
973
|
+
parts.push(chalk.red("failed"));
|
|
974
|
+
if (error) {
|
|
975
|
+
parts.push(chalk.dim("ยท"));
|
|
976
|
+
parts.push(chalk.red(error));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
parts.push(this.formatDurationColored(durationMs, "default"));
|
|
981
|
+
|
|
982
|
+
return parts.join(" ");
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Format act() start message - provides visual scope boundary
|
|
987
|
+
* @param {string} task - The task being executed
|
|
988
|
+
* @returns {string} Formatted act start message
|
|
989
|
+
*/
|
|
990
|
+
formatActStart(task) {
|
|
991
|
+
const parts = [];
|
|
992
|
+
this.addTimestamp(parts);
|
|
993
|
+
parts.push(this.getPrefix("action"));
|
|
994
|
+
parts.push(chalk.bold.cyan("Act"));
|
|
995
|
+
parts.push(chalk.cyan(`"${task}"`));
|
|
996
|
+
return parts.join(" ");
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Format act() completion message - provides visual scope boundary
|
|
1001
|
+
* @param {number} durationMs - Duration in milliseconds
|
|
1002
|
+
* @param {boolean} success - Whether the act completed successfully
|
|
1003
|
+
* @param {string} [error] - Error message if failed
|
|
1004
|
+
* @returns {string} Formatted act complete message
|
|
1005
|
+
*/
|
|
1006
|
+
formatActComplete(durationMs, success, error = null) {
|
|
1007
|
+
const parts = [];
|
|
1008
|
+
this.addTimestamp(parts);
|
|
1009
|
+
parts.push(this.getResultPrefix());
|
|
1010
|
+
|
|
1011
|
+
if (success) {
|
|
1012
|
+
parts.push(chalk.green("complete"));
|
|
1013
|
+
} else {
|
|
1014
|
+
parts.push(chalk.red("failed"));
|
|
1015
|
+
if (error) {
|
|
1016
|
+
parts.push(chalk.dim("ยท"));
|
|
1017
|
+
parts.push(chalk.red(error));
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
parts.push(this.formatDurationColored(durationMs, "default"));
|
|
1022
|
+
|
|
1023
|
+
return parts.join(" ");
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Format parse() elements as a formatted table for console output ๐
|
|
1028
|
+
* @param {Array} elements - Array of parsed elements from parse()
|
|
1029
|
+
* @param {Object} options - Formatting options
|
|
1030
|
+
* @param {number} options.maxContentLength - Max length for content column (default: 30)
|
|
1031
|
+
* @param {number} options.maxRows - Max number of rows to display (default: 50)
|
|
1032
|
+
* @returns {string} Formatted table string
|
|
1033
|
+
*/
|
|
1034
|
+
formatParseElements(elements, options = {}) {
|
|
1035
|
+
if (!elements || elements.length === 0) {
|
|
1036
|
+
return chalk.dim(" No elements found");
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const maxContentLength = options.maxContentLength || 30;
|
|
1040
|
+
const maxRows = options.maxRows || 50;
|
|
1041
|
+
|
|
1042
|
+
// Column widths
|
|
1043
|
+
const idxWidth = 5;
|
|
1044
|
+
const typeWidth = 10;
|
|
1045
|
+
const contentWidth = maxContentLength + 2;
|
|
1046
|
+
const interactWidth = 14;
|
|
1047
|
+
const posWidth = 18;
|
|
1048
|
+
|
|
1049
|
+
const lines = [];
|
|
1050
|
+
|
|
1051
|
+
// Header
|
|
1052
|
+
const headerLine = [
|
|
1053
|
+
chalk.bold.cyan(this._padRight("Idx", idxWidth)),
|
|
1054
|
+
chalk.bold.cyan(this._padRight("Type", typeWidth)),
|
|
1055
|
+
chalk.bold.cyan(this._padRight("Content", contentWidth)),
|
|
1056
|
+
chalk.bold.cyan(this._padRight("Interactive", interactWidth)),
|
|
1057
|
+
chalk.bold.cyan("Position"),
|
|
1058
|
+
].join(chalk.dim(" โ "));
|
|
1059
|
+
|
|
1060
|
+
lines.push(" " + headerLine);
|
|
1061
|
+
|
|
1062
|
+
// Separator line
|
|
1063
|
+
const separatorLine = [
|
|
1064
|
+
chalk.dim("โ".repeat(idxWidth)),
|
|
1065
|
+
chalk.dim("โ".repeat(typeWidth)),
|
|
1066
|
+
chalk.dim("โ".repeat(contentWidth)),
|
|
1067
|
+
chalk.dim("โ".repeat(interactWidth)),
|
|
1068
|
+
chalk.dim("โ".repeat(posWidth)),
|
|
1069
|
+
].join(chalk.dim("โโผโ"));
|
|
1070
|
+
|
|
1071
|
+
lines.push(" " + separatorLine);
|
|
1072
|
+
|
|
1073
|
+
// Data rows
|
|
1074
|
+
const displayElements = elements.slice(0, maxRows);
|
|
1075
|
+
for (const el of displayElements) {
|
|
1076
|
+
const idx = String(el.index ?? "?");
|
|
1077
|
+
const type = el.type || "unknown";
|
|
1078
|
+
const content = this._truncate(el.content || "", maxContentLength);
|
|
1079
|
+
|
|
1080
|
+
// Format interactivity with color
|
|
1081
|
+
// Note: interactivity can be boolean (true/false) or string ("clickable", "non-interactive")
|
|
1082
|
+
let interactivity = el.interactivity || "-";
|
|
1083
|
+
let interactivityDisplay;
|
|
1084
|
+
if (interactivity === "clickable" || interactivity === true) {
|
|
1085
|
+
interactivityDisplay = chalk.green("โ clickable");
|
|
1086
|
+
} else if (interactivity === false || interactivity === "non-interactive") {
|
|
1087
|
+
interactivityDisplay = chalk.dim("-");
|
|
1088
|
+
} else {
|
|
1089
|
+
interactivityDisplay = chalk.dim(String(interactivity));
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// Format position from bbox
|
|
1093
|
+
let position = "-";
|
|
1094
|
+
if (el.bbox) {
|
|
1095
|
+
position = `(${el.bbox.x0}, ${el.bbox.y0})`;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// For interactivity column, we need to pad based on visible text, not chalk codes
|
|
1099
|
+
// "โ clickable" = 11 chars, "-" = 1 char, so we pad manually after getting visible length
|
|
1100
|
+
const interactPadded = this._padRight(interactivityDisplay, interactWidth);
|
|
1101
|
+
|
|
1102
|
+
const dataLine = [
|
|
1103
|
+
chalk.yellow(this._padRight(idx, idxWidth)),
|
|
1104
|
+
chalk.white(this._padRight(type, typeWidth)),
|
|
1105
|
+
chalk.gray(this._padRight(content, contentWidth)),
|
|
1106
|
+
interactPadded,
|
|
1107
|
+
chalk.dim(position),
|
|
1108
|
+
].join(chalk.dim(" โ "));
|
|
1109
|
+
|
|
1110
|
+
lines.push(" " + dataLine);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// Show truncation message if needed
|
|
1114
|
+
if (elements.length > maxRows) {
|
|
1115
|
+
lines.push(chalk.dim(` ... and ${elements.length - maxRows} more elements (showing first ${maxRows})`));
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return lines.join("\n");
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Truncate a string to a maximum length with ellipsis
|
|
1123
|
+
* @private
|
|
1124
|
+
* @param {string} str - String to truncate
|
|
1125
|
+
* @param {number} maxLength - Maximum length
|
|
1126
|
+
* @returns {string} Truncated string
|
|
1127
|
+
*/
|
|
1128
|
+
_truncate(str, maxLength) {
|
|
1129
|
+
if (!str) return "";
|
|
1130
|
+
// Remove newlines and extra whitespace
|
|
1131
|
+
const cleaned = str.replace(/\s+/g, " ").trim();
|
|
1132
|
+
if (cleaned.length <= maxLength) return cleaned;
|
|
1133
|
+
return cleaned.substring(0, maxLength - 1) + "โฆ";
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Pad a string to the right to a fixed width
|
|
1138
|
+
* @private
|
|
1139
|
+
* @param {string} str - String to pad
|
|
1140
|
+
* @param {number} width - Target width
|
|
1141
|
+
* @returns {string} Padded string
|
|
1142
|
+
*/
|
|
1143
|
+
_padRight(str, width) {
|
|
1144
|
+
// Handle chalk strings by getting visible length
|
|
1145
|
+
const visibleStr = String(str).replace(/\x1b\[[0-9;]*m/g, "");
|
|
1146
|
+
const padding = Math.max(0, width - visibleStr.length);
|
|
1147
|
+
return str + " ".repeat(padding);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// Export singleton instance
|
|
1152
|
+
const formatter = new SDKLogFormatter();
|
|
1153
|
+
|
|
1154
|
+
module.exports = {
|
|
1155
|
+
SDKLogFormatter,
|
|
1156
|
+
formatter,
|
|
1157
|
+
};
|