testdriverai 6.2.2 → 7.1.0
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/.github/workflows/acceptance-linux.yml +75 -0
- package/.github/workflows/acceptance-sdk-tests.yml +133 -0
- package/.vscode/settings.json +5 -1
- package/AGENTS.md +550 -0
- package/CODEOWNERS +0 -1
- package/README.md +126 -0
- package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
- package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
- package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
- package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
- package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
- package/_testdriver/lifecycle/prerun.yaml +15 -0
- package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
- package/agent/index.js +300 -85
- package/agent/interface.js +15 -0
- package/agent/lib/cache.js +142 -0
- package/agent/lib/commander.js +1 -39
- package/agent/lib/commands.js +910 -296
- package/agent/lib/redraw.js +129 -41
- package/agent/lib/sandbox.js +29 -6
- package/agent/lib/sdk.js +22 -0
- package/agent/lib/system.js +0 -3
- package/agent/lib/validation.js +1 -7
- package/debug-locate-response.js +82 -0
- package/debugger/index.html +15 -4
- package/docs/ARCHITECTURE.md +424 -0
- package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
- package/docs/MIGRATION.md +425 -0
- package/docs/PRESETS.md +210 -0
- package/docs/QUICK_START_TEST_RECORDING.md +215 -0
- package/docs/SDK_AWESOME_LOGS.md +468 -0
- package/docs/TEST_RECORDING.md +388 -0
- package/docs/docs.json +286 -152
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/sdk-browser-rendering.md +167 -0
- package/docs/v6/getting-started/self-hosting.mdx +407 -0
- package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
- package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
- package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v7/README.md +135 -0
- package/docs/v7/api/ai.mdx +205 -0
- package/docs/v7/api/assert.mdx +285 -0
- package/docs/v7/api/assertions.mdx +403 -0
- package/docs/v7/api/click.mdx +287 -0
- package/docs/v7/api/client.mdx +322 -0
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/elements.mdx +479 -0
- package/docs/v7/api/exec.mdx +346 -0
- package/docs/v7/api/find.mdx +316 -0
- package/docs/v7/api/focusApplication.mdx +294 -0
- package/docs/v7/api/hover.mdx +279 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/pressKeys.mdx +349 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/api/sandbox.mdx +404 -0
- package/docs/v7/api/scroll.mdx +300 -0
- package/docs/v7/api/type.mdx +314 -0
- package/docs/v7/commands/assert.mdx +45 -0
- package/docs/v7/commands/exec.mdx +282 -0
- package/docs/v7/commands/focus-application.mdx +44 -0
- package/docs/v7/commands/hover-image.mdx +69 -0
- package/docs/v7/commands/hover-text.mdx +47 -0
- package/docs/v7/commands/if.mdx +53 -0
- package/docs/v7/commands/match-image.mdx +67 -0
- package/docs/v7/commands/press-keys.mdx +87 -0
- package/docs/v7/commands/remember.mdx +49 -0
- package/docs/v7/commands/run.mdx +44 -0
- package/docs/v7/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/commands/scroll.mdx +69 -0
- package/docs/v7/commands/type.mdx +45 -0
- package/docs/v7/commands/wait-for-image.mdx +54 -0
- package/docs/v7/commands/wait-for-text.mdx +48 -0
- package/docs/v7/commands/wait.mdx +45 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/quickstart.mdx +332 -0
- package/docs/v7/guides/best-practices.mdx +486 -0
- package/docs/v7/guides/caching-ai.mdx +215 -0
- package/docs/v7/guides/caching-selectors.mdx +292 -0
- package/docs/v7/guides/caching.mdx +366 -0
- package/docs/v7/guides/ci-cd/azure.mdx +587 -0
- package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
- package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
- package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
- package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
- package/docs/v7/guides/ci-cd/travis.mdx +438 -0
- package/docs/v7/guides/debugging.mdx +349 -0
- package/docs/v7/guides/faq.mdx +393 -0
- package/docs/v7/guides/migration.mdx +562 -0
- package/docs/v7/guides/performance.mdx +517 -0
- package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
- package/docs/v7/guides/troubleshooting.mdx +526 -0
- package/docs/v7/guides/vitest-plugin.mdx +477 -0
- package/docs/v7/guides/vitest.mdx +535 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/playwright.mdx +342 -0
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +287 -0
- package/docs/v7/presets/electron.mdx +435 -0
- package/docs/v7/presets/vscode.mdx +398 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/docs/v7/progressive-apis/CORE.md +459 -0
- package/docs/v7/progressive-apis/HOOKS.md +360 -0
- package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
- package/docs/v7/progressive-apis/PROVISION.md +266 -0
- package/eslint.config.js +19 -1
- package/interfaces/cli/lib/base.js +10 -4
- package/interfaces/logger.js +2 -1
- package/interfaces/shared-test-state.mjs +69 -0
- package/interfaces/vitest-plugin.mjs +830 -0
- package/package.json +29 -5
- package/schema.json +8 -29
- package/scripts/view-test-results.mjs +96 -0
- package/sdk-log-formatter.js +714 -0
- package/sdk.d.ts +1028 -0
- package/sdk.js +2567 -0
- package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
- package/setup/aws/cloudformation.yaml +9 -2
- package/src/core/Dashcam.js +469 -0
- package/src/core/index.d.ts +150 -0
- package/src/core/index.js +12 -0
- package/src/presets/index.mjs +331 -0
- package/src/vitest/extended.mjs +108 -0
- package/src/vitest/hooks.d.ts +119 -0
- package/src/vitest/hooks.mjs +298 -0
- package/src/vitest/index.mjs +64 -0
- package/src/vitest/lifecycle.mjs +277 -0
- package/src/vitest/utils.mjs +150 -0
- package/test/dashcam.test.js +137 -0
- package/test/mcp-example-test.yaml +27 -0
- package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
- package/testdriver/acceptance-sdk/README.md +128 -0
- package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +26 -0
- package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
- package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +58 -0
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +25 -0
- package/testdriver/acceptance-sdk/exec-js.test.mjs +43 -0
- package/testdriver/acceptance-sdk/exec-output.test.mjs +59 -0
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +57 -0
- package/testdriver/acceptance-sdk/focus-window.test.mjs +36 -0
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +26 -0
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +34 -0
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-text.test.mjs +27 -0
- package/testdriver/acceptance-sdk/match-image.test.mjs +36 -0
- package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +50 -0
- package/testdriver/acceptance-sdk/prompt.test.mjs +33 -0
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +38 -0
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +39 -0
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +28 -0
- package/testdriver/acceptance-sdk/scroll.test.mjs +41 -0
- package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +420 -0
- package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
- package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
- package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
- package/testdriver/acceptance-sdk/type.test.mjs +45 -0
- package/verify-element-api.js +89 -0
- package/verify-types.js +0 -0
- package/vitest.config.example.js +19 -0
- package/vitest.config.mjs +66 -0
- package/vitest.config.mjs.bak +44 -0
- package/.github/workflows/acceptance-v6.yml +0 -169
- package/.vscode/mcp.json +0 -9
- package/docs/overview/comparison.mdx +0 -82
- package/testdriver/lifecycle/prerun.yaml +0 -17
- /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
- /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
- /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
- /package/docs/{account → v6/account}/dashboard.mdx +0 -0
- /package/docs/{account → v6/account}/enterprise.mdx +0 -0
- /package/docs/{account → v6/account}/pricing.mdx +0 -0
- /package/docs/{account → v6/account}/projects.mdx +0 -0
- /package/docs/{account → v6/account}/team.mdx +0 -0
- /package/docs/{action → v6/action}/ami.mdx +0 -0
- /package/docs/{action → v6/action}/performance.mdx +0 -0
- /package/docs/{action → v6/action}/secrets.mdx +0 -0
- /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
- /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
- /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
- /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
- /package/docs/{cli → v6/cli}/overview.mdx +0 -0
- /package/docs/{commands → v6/commands}/assert.mdx +0 -0
- /package/docs/{commands → v6/commands}/exec.mdx +0 -0
- /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/if.mdx +0 -0
- /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
- /package/docs/{commands → v6/commands}/remember.mdx +0 -0
- /package/docs/{commands → v6/commands}/run.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
- /package/docs/{commands → v6/commands}/type.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
- /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
- /package/docs/{features → v6/features}/generation.mdx +0 -0
- /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
- /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
- /package/docs/{features → v6/features}/selectorless.mdx +0 -0
- /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
- /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
- /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
- /package/docs/{guide → v6/guide}/code.mdx +0 -0
- /package/docs/{guide → v6/guide}/locating.mdx +0 -0
- /package/docs/{guide → v6/guide}/protips.mdx +0 -0
- /package/docs/{guide → v6/guide}/variables.mdx +0 -0
- /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
- /package/docs/{importing → v6/importing}/csv.mdx +0 -0
- /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
- /package/docs/{importing → v6/importing}/jira.mdx +0 -0
- /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
- /package/docs/{overview → v6/overview}/faq.mdx +0 -0
- /package/docs/{overview → v6/overview}/performance.mdx +0 -0
- /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
- /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
- /package/docs/{security → v6/security}/action.mdx +0 -0
- /package/docs/{security → v6/security}/agent.mdx +0 -0
- /package/docs/{security → v6/security}/platform.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility Functions for TestDriver Vitest Plugin
|
|
3
|
+
*
|
|
4
|
+
* General-purpose utilities for testing with TestDriver.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { retryAsync, setupEventLogging } from 'testdriverai/vitest';
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Set up detailed event logging for debugging
|
|
12
|
+
* @param {TestDriver} client - TestDriver client
|
|
13
|
+
*/
|
|
14
|
+
export function setupEventLogging(client) {
|
|
15
|
+
const emitter = client.getEmitter();
|
|
16
|
+
|
|
17
|
+
// Log all events
|
|
18
|
+
emitter.on("**", function (data) {
|
|
19
|
+
const event = this.event;
|
|
20
|
+
if (event.startsWith("log:debug")) return; // Skip debug logs
|
|
21
|
+
console.log(`[EVENT] ${event}`, data || "");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Log command lifecycle
|
|
25
|
+
emitter.on("command:start", (data) => {
|
|
26
|
+
console.log("🚀 Command started:", data);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
emitter.on("command:success", (data) => {
|
|
30
|
+
console.log("✅ Command succeeded:", data);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
emitter.on("command:error", (data) => {
|
|
34
|
+
console.error("❌ Command error:", data);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Log sandbox events
|
|
38
|
+
emitter.on("sandbox:connected", () => {
|
|
39
|
+
console.log("🔌 Sandbox connected");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
emitter.on("sandbox:authenticated", () => {
|
|
43
|
+
console.log("🔐 Sandbox authenticated");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
emitter.on("sandbox:error", (error) => {
|
|
47
|
+
console.error("⚠️ Sandbox error:", error);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Log SDK API calls
|
|
51
|
+
emitter.on("sdk:request", (data) => {
|
|
52
|
+
console.log("📤 SDK Request:", data);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
emitter.on("sdk:response", (data) => {
|
|
56
|
+
console.log("📥 SDK Response:", data);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Retry an async function with configurable attempts and delay
|
|
62
|
+
* @param {Function} fn - Async function to retry
|
|
63
|
+
* @param {number} retries - Number of retries (default: 3)
|
|
64
|
+
* @param {number} delay - Delay between retries in ms (default: 1000)
|
|
65
|
+
* @returns {Promise} Result of successful execution
|
|
66
|
+
* @throws {Error} Last error if all retries fail
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const result = await retryAsync(
|
|
70
|
+
* () => testdriver.find("Flaky Element"),
|
|
71
|
+
* 5,
|
|
72
|
+
* 2000
|
|
73
|
+
* );
|
|
74
|
+
*/
|
|
75
|
+
export async function retryAsync(fn, retries = 3, delay = 1000) {
|
|
76
|
+
let lastError;
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < retries; i++) {
|
|
79
|
+
try {
|
|
80
|
+
return await fn();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
lastError = error;
|
|
83
|
+
if (i < retries - 1) {
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw lastError;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Wait for a condition to be true
|
|
94
|
+
* @param {Function} condition - Function that returns boolean or Promise<boolean>
|
|
95
|
+
* @param {object} options - Wait options
|
|
96
|
+
* @param {number} options.timeout - Maximum time to wait in ms (default: 30000)
|
|
97
|
+
* @param {number} options.interval - Poll interval in ms (default: 500)
|
|
98
|
+
* @param {string} options.message - Error message if timeout (default: "Condition not met")
|
|
99
|
+
* @returns {Promise<boolean>} True if condition met
|
|
100
|
+
* @throws {Error} If timeout reached
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* await waitFor(() => element.exists(), { timeout: 10000 });
|
|
104
|
+
*/
|
|
105
|
+
export async function waitFor(condition, options = {}) {
|
|
106
|
+
const {
|
|
107
|
+
timeout = 30000,
|
|
108
|
+
interval = 500,
|
|
109
|
+
message = "Condition not met within timeout"
|
|
110
|
+
} = options;
|
|
111
|
+
|
|
112
|
+
const startTime = Date.now();
|
|
113
|
+
|
|
114
|
+
while (Date.now() - startTime < timeout) {
|
|
115
|
+
try {
|
|
116
|
+
const result = await condition();
|
|
117
|
+
if (result) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// Condition threw, keep polling
|
|
122
|
+
}
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error(`${message} (timeout: ${timeout}ms)`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Sleep for a specified duration
|
|
131
|
+
* @param {number} ms - Milliseconds to sleep
|
|
132
|
+
* @returns {Promise<void>}
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* await sleep(1000); // Wait 1 second
|
|
136
|
+
*/
|
|
137
|
+
export function sleep(ms) {
|
|
138
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Generate a unique test ID
|
|
143
|
+
* @param {string} prefix - Optional prefix for the ID
|
|
144
|
+
* @returns {string} Unique ID
|
|
145
|
+
*/
|
|
146
|
+
export function generateTestId(prefix = "test") {
|
|
147
|
+
const timestamp = Date.now().toString(36);
|
|
148
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
149
|
+
return `${prefix}-${timestamp}-${random}`;
|
|
150
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashcam Class - Unit Tests
|
|
3
|
+
* Tests for the new Dashcam class
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Dashcam } = require('../src/core');
|
|
7
|
+
|
|
8
|
+
// Mock TestDriver client
|
|
9
|
+
class MockTestDriver {
|
|
10
|
+
constructor(os = 'linux') {
|
|
11
|
+
this.os = os;
|
|
12
|
+
this.commands = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async exec(shell, command, timeout, silent) {
|
|
16
|
+
this.commands.push({ shell, command, timeout, silent });
|
|
17
|
+
|
|
18
|
+
// Mock responses
|
|
19
|
+
if (command.includes('npm prefix -g')) {
|
|
20
|
+
return this.os === 'windows'
|
|
21
|
+
? 'C:\\Users\\testdriver\\AppData\\Roaming\\npm'
|
|
22
|
+
: '/usr/local';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (command.includes('stop')) {
|
|
26
|
+
return 'Recording stopped\nReplay URL: https://app.testdriver.ai/replay/abc123?share=xyz';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return 'OK';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
wait(ms) {
|
|
33
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function testDashcamCreation() {
|
|
38
|
+
console.log('🧪 Test: Dashcam creation');
|
|
39
|
+
|
|
40
|
+
const client = new MockTestDriver();
|
|
41
|
+
const dashcam = new Dashcam(client);
|
|
42
|
+
|
|
43
|
+
console.assert(dashcam.client === client, 'Client should be stored');
|
|
44
|
+
console.assert(dashcam.recording === false, 'Should not be recording initially');
|
|
45
|
+
console.assert(dashcam._authenticated === false, 'Should not be authenticated initially');
|
|
46
|
+
|
|
47
|
+
console.log('✅ Dashcam creation test passed');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function testDashcamWithOptions() {
|
|
51
|
+
console.log('\n🧪 Test: Dashcam with options');
|
|
52
|
+
|
|
53
|
+
const client = new MockTestDriver();
|
|
54
|
+
const dashcam = new Dashcam(client, {
|
|
55
|
+
apiKey: 'custom-key',
|
|
56
|
+
autoStart: true,
|
|
57
|
+
logs: [
|
|
58
|
+
{ type: 'file', path: '/tmp/test.log', name: 'Test Log' }
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.assert(dashcam.apiKey === 'custom-key', 'Custom API key should be used');
|
|
63
|
+
console.assert(dashcam.autoStart === true, 'Auto start should be enabled');
|
|
64
|
+
console.assert(dashcam.logs.length === 1, 'Logs should be stored');
|
|
65
|
+
|
|
66
|
+
console.log('✅ Dashcam options test passed');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function testErrorOnMissingClient() {
|
|
70
|
+
console.log('\n🧪 Test: Error when client missing');
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
new Dashcam();
|
|
74
|
+
console.error('❌ Should have thrown error');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.assert(
|
|
78
|
+
error.message.includes('TestDriver client'),
|
|
79
|
+
'Error message should mention TestDriver client'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('✅ Missing client error test passed');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function testStopReturnsUrl() {
|
|
87
|
+
console.log('\n🧪 Test: Stop returns URL');
|
|
88
|
+
|
|
89
|
+
const client = new MockTestDriver();
|
|
90
|
+
const dashcam = new Dashcam(client);
|
|
91
|
+
|
|
92
|
+
dashcam.recording = true; // Simulate recording state
|
|
93
|
+
const url = await dashcam.stop();
|
|
94
|
+
|
|
95
|
+
console.assert(url !== null, 'URL should be returned');
|
|
96
|
+
console.assert(url.includes('replay'), 'URL should contain replay');
|
|
97
|
+
console.assert(dashcam.recording === false, 'Should stop recording');
|
|
98
|
+
|
|
99
|
+
console.log('✅ Stop returns URL test passed');
|
|
100
|
+
console.log(' URL:', url);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function testIsRecording() {
|
|
104
|
+
console.log('\n🧪 Test: isRecording method');
|
|
105
|
+
|
|
106
|
+
const client = new MockTestDriver();
|
|
107
|
+
const dashcam = new Dashcam(client);
|
|
108
|
+
|
|
109
|
+
let recording = await dashcam.isRecording();
|
|
110
|
+
console.assert(recording === false, 'Should not be recording initially');
|
|
111
|
+
|
|
112
|
+
dashcam.recording = true;
|
|
113
|
+
recording = await dashcam.isRecording();
|
|
114
|
+
console.assert(recording === true, 'Should return recording state');
|
|
115
|
+
|
|
116
|
+
console.log('✅ isRecording test passed');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function runAllTests() {
|
|
120
|
+
console.log('🚀 Running Dashcam class tests...\n');
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await testDashcamCreation();
|
|
124
|
+
await testDashcamWithOptions();
|
|
125
|
+
await testErrorOnMissingClient();
|
|
126
|
+
await testStopReturnsUrl();
|
|
127
|
+
await testIsRecording();
|
|
128
|
+
|
|
129
|
+
console.log('\n✅ All tests passed!');
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('\n❌ Test failed:', error.message);
|
|
132
|
+
console.error(error.stack);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
runAllTests();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: MCP Example Test
|
|
2
|
+
description: Test created using TestDriver MCP to navigate example.com
|
|
3
|
+
|
|
4
|
+
steps:
|
|
5
|
+
# Open Chrome browser and navigate to example.com
|
|
6
|
+
- exec:
|
|
7
|
+
code: Start-Process chrome "https://example.com"
|
|
8
|
+
language: pwsh
|
|
9
|
+
timeout: 5000
|
|
10
|
+
|
|
11
|
+
# Wait for the page to load
|
|
12
|
+
- wait-for-text:
|
|
13
|
+
text: Example Domain
|
|
14
|
+
timeout: 10000
|
|
15
|
+
|
|
16
|
+
# Assert that the Example Domain heading is visible
|
|
17
|
+
- assert:
|
|
18
|
+
expect: The text "Example Domain" is visible on the page
|
|
19
|
+
|
|
20
|
+
# Click on the "Learn more" link
|
|
21
|
+
- hover-text:
|
|
22
|
+
text: Learn more
|
|
23
|
+
action: click
|
|
24
|
+
|
|
25
|
+
# Wait for navigation to complete
|
|
26
|
+
- wait:
|
|
27
|
+
timeout: 2000
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# SDK Test Reporting - Quick Reference
|
|
2
|
+
|
|
3
|
+
## 🚀 Commands
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Run all tests
|
|
7
|
+
npm run test:sdk
|
|
8
|
+
|
|
9
|
+
# View terminal summary
|
|
10
|
+
npm run test:sdk:results
|
|
11
|
+
|
|
12
|
+
# Open HTML report
|
|
13
|
+
npm run test:sdk:report
|
|
14
|
+
|
|
15
|
+
# Watch mode (dev)
|
|
16
|
+
npm run test:sdk:watch
|
|
17
|
+
|
|
18
|
+
# Interactive UI
|
|
19
|
+
npm run test:sdk:ui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 📊 What You Get
|
|
23
|
+
|
|
24
|
+
### Locally
|
|
25
|
+
|
|
26
|
+
1. **Console**: Verbose logs with full test output
|
|
27
|
+
2. **Terminal Summary**: `npm run test:sdk:results` - Quick pass/fail counts
|
|
28
|
+
3. **HTML Report**: `npm run test:sdk:report` - Interactive browser viewer
|
|
29
|
+
|
|
30
|
+
### GitHub Actions
|
|
31
|
+
|
|
32
|
+
1. **Step Summary**: Markdown tables in workflow summary page
|
|
33
|
+
2. **Test Summary Action**: Badge counts and annotations
|
|
34
|
+
3. **Artifacts**: Download junit.xml, results.json, and index.html
|
|
35
|
+
|
|
36
|
+
## 📁 Output Files
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
test-results/
|
|
40
|
+
├── junit.xml # For CI/CD tools
|
|
41
|
+
├── results.json # Machine-readable
|
|
42
|
+
└── index.html # Interactive report
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## ⚡ Quick Tips
|
|
46
|
+
|
|
47
|
+
- **Debugging failures?** → `npm run test:sdk:report` (HTML has best error context)
|
|
48
|
+
- **Quick status check?** → `npm run test:sdk:results` (terminal summary)
|
|
49
|
+
- **PR review?** → Check GitHub Actions summary tab
|
|
50
|
+
- **Need history?** → Download artifacts from GitHub Actions runs
|
|
51
|
+
|
|
52
|
+
## 🔍 GitHub Summary Preview
|
|
53
|
+
|
|
54
|
+
Every test run creates a summary with:
|
|
55
|
+
|
|
56
|
+
- ✅ Pass/fail counts table
|
|
57
|
+
- ❌ Failed test details with errors
|
|
58
|
+
- ✅ List of all passing tests
|
|
59
|
+
- ⏱️ Duration metrics
|
|
60
|
+
|
|
61
|
+
Find it: Actions → Your workflow run → Summary tab
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# TestDriver SDK Acceptance Tests
|
|
2
|
+
|
|
3
|
+
This directory contains acceptance tests for the TestDriver SDK using Vitest.
|
|
4
|
+
|
|
5
|
+
## Running Tests
|
|
6
|
+
|
|
7
|
+
### Run All Tests (Cross-Platform)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm run test:sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Run Platform-Specific Tests
|
|
14
|
+
|
|
15
|
+
Use the `TEST_PLATFORM` environment variable to run tests for a specific platform:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Run Windows-only tests
|
|
19
|
+
npm run test:sdk:windows
|
|
20
|
+
|
|
21
|
+
# Run macOS-only tests
|
|
22
|
+
npm run test:sdk:mac
|
|
23
|
+
|
|
24
|
+
# Run Linux-only tests
|
|
25
|
+
npm run test:sdk:linux
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or set the environment variable directly:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
TEST_PLATFORM=windows npm run test:sdk
|
|
32
|
+
TEST_PLATFORM=mac npm run test:sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Test Organization
|
|
36
|
+
|
|
37
|
+
### Cross-Platform Tests
|
|
38
|
+
|
|
39
|
+
Tests without a platform suffix run on all platforms:
|
|
40
|
+
|
|
41
|
+
- `hover-text.test.mjs` - Runs everywhere
|
|
42
|
+
- `scroll.test.mjs` - Runs everywhere
|
|
43
|
+
- `screenshot.test.mjs` - Runs everywhere
|
|
44
|
+
|
|
45
|
+
### Platform-Specific Tests
|
|
46
|
+
|
|
47
|
+
Platform-specific tests use naming conventions:
|
|
48
|
+
|
|
49
|
+
- `*.windows.test.mjs` - Windows-only tests (e.g., `exec-pwsh.windows.test.mjs`)
|
|
50
|
+
- `*.mac.test.mjs` - macOS-only tests
|
|
51
|
+
- `*.linux.test.mjs` - Linux-only tests
|
|
52
|
+
|
|
53
|
+
### Conditional Test Skipping
|
|
54
|
+
|
|
55
|
+
Some tests use `skipIf` to conditionally skip based on the platform:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
it.skipIf(() => testdriver.os === "linux")(
|
|
59
|
+
"should run on Windows/Mac",
|
|
60
|
+
async () => {
|
|
61
|
+
// This test will be skipped on Linux
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Environment Variables
|
|
67
|
+
|
|
68
|
+
- `TEST_PLATFORM` - Filter tests by platform (`windows`, `mac`, `linux`)
|
|
69
|
+
- `TD_OS` - Override the sandbox OS (defaults to `linux`)
|
|
70
|
+
- `TD_API_KEY` - Your TestDriver API key (required)
|
|
71
|
+
- `TD_API_ROOT` - API endpoint (optional)
|
|
72
|
+
- `DEBUG_ENV` - Show environment variable loading (optional)
|
|
73
|
+
- `DEBUG_EVENTS` - Enable detailed event logging (optional)
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Run only Windows tests on a Windows sandbox
|
|
79
|
+
TEST_PLATFORM=windows npm run test:sdk
|
|
80
|
+
|
|
81
|
+
# Run all tests but use a Windows sandbox
|
|
82
|
+
TD_OS=windows npm run test:sdk
|
|
83
|
+
|
|
84
|
+
# Run with debugging enabled
|
|
85
|
+
DEBUG_ENV=true DEBUG_EVENTS=true npm run test:sdk
|
|
86
|
+
|
|
87
|
+
# Watch mode for development
|
|
88
|
+
npm run test:sdk:watch
|
|
89
|
+
|
|
90
|
+
# Generate coverage report
|
|
91
|
+
npm run test:sdk:coverage
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Test Structure
|
|
95
|
+
|
|
96
|
+
Each test follows this pattern:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
import { afterEach, beforeEach, describe, it } from "vitest";
|
|
100
|
+
import {
|
|
101
|
+
createTestClient,
|
|
102
|
+
setupTest,
|
|
103
|
+
teardownTest,
|
|
104
|
+
} from "./setup/testHelpers.mjs";
|
|
105
|
+
|
|
106
|
+
describe("My Test", () => {
|
|
107
|
+
let testdriver;
|
|
108
|
+
|
|
109
|
+
beforeEach(async () => {
|
|
110
|
+
testdriver = createTestClient();
|
|
111
|
+
await setupTest(testdriver);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
afterEach(async () => {
|
|
115
|
+
await teardownTest(testdriver);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should do something", async () => {
|
|
119
|
+
// Your test logic
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## See Also
|
|
125
|
+
|
|
126
|
+
- [SDK README](../../SDK_README.md) - Full SDK documentation
|
|
127
|
+
- [Quick Reference](./QUICK_REFERENCE.md) - SDK method quick reference
|
|
128
|
+
- [Test Reporting](./TEST_REPORTING.md) - Test recording and reporting docs
|