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,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver Presets
|
|
3
|
+
*
|
|
4
|
+
* DEPRECATED: These presets are maintained for backwards compatibility.
|
|
5
|
+
* New code should use the simpler API:
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
9
|
+
*
|
|
10
|
+
* test('my test', async (context) => {
|
|
11
|
+
* const testdriver = TestDriver(context, { headless: true });
|
|
12
|
+
*
|
|
13
|
+
* await testdriver.connected();
|
|
14
|
+
* await testdriver.dashcam.start();
|
|
15
|
+
* await testdriver.provision.chrome({ url: 'https://example.com' });
|
|
16
|
+
* await testdriver.find('Login').click();
|
|
17
|
+
* await testdriver.dashcam.stop();
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { TestDriver } from '../vitest/hooks.mjs';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Chrome Browser Preset (DEPRECATED)
|
|
25
|
+
* Use testdriver.provision.chrome() instead
|
|
26
|
+
*/
|
|
27
|
+
export async function chrome(context, options = {}) {
|
|
28
|
+
console.warn('[chrome preset] DEPRECATED: Use TestDriver() + testdriver.provision.chrome() instead');
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
url = 'http://testdriver-sandbox.vercel.app/',
|
|
32
|
+
dashcam: enableDashcam = false,
|
|
33
|
+
...testDriverOptions
|
|
34
|
+
} = options;
|
|
35
|
+
|
|
36
|
+
const testdriver = TestDriver(context, testDriverOptions);
|
|
37
|
+
|
|
38
|
+
// Wait for connection to complete if autoConnect was enabled
|
|
39
|
+
if (testdriver.__connectionPromise) {
|
|
40
|
+
await testdriver.__connectionPromise;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (enableDashcam) {
|
|
44
|
+
await testdriver.dashcam.start();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await testdriver.provision.chrome({ url });
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
testdriver: testdriver,
|
|
51
|
+
dashcam: enableDashcam ? testdriver.dashcam : null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* VS Code Preset
|
|
57
|
+
* Automatically sets up VS Code with TestDriver and Dashcam
|
|
58
|
+
*
|
|
59
|
+
* @param {object} context - Vitest test context
|
|
60
|
+
* @param {object} options - Preset options (accepts all useTestDriver options)
|
|
61
|
+
* @param {string} [options.workspace] - Workspace/folder to open
|
|
62
|
+
* @param {string[]} [options.extensions=[]] - Extensions to install
|
|
63
|
+
* @param {boolean} [options.dashcam=true] - Enable Dashcam recording
|
|
64
|
+
* @param {boolean} [options.newSandbox=true] - Create new sandbox
|
|
65
|
+
* @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
|
|
66
|
+
* @param {string} [options.apiKey] - TestDriver API key
|
|
67
|
+
* @param {string} [options.apiRoot] - API endpoint
|
|
68
|
+
* @param {boolean} [options.autoConnect=true] - Automatically connect to sandbox
|
|
69
|
+
* @returns {Promise<{testdriver: TestDriver, vscode: TestDriver, dashcam: Dashcam}>}
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* test('extension test', async (context) => {
|
|
73
|
+
* const { vscode } = await vscode(context, {
|
|
74
|
+
* workspace: '/tmp/test-project',
|
|
75
|
+
* extensions: ['ms-python.python']
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* await vscode.find('File menu').click();
|
|
79
|
+
* await vscode.find('New File').click();
|
|
80
|
+
* });
|
|
81
|
+
*/
|
|
82
|
+
export async function vscode(context, options = {}) {
|
|
83
|
+
// Extract vscode-specific options
|
|
84
|
+
const {
|
|
85
|
+
workspace = null,
|
|
86
|
+
dashcam: enableDashcam = true,
|
|
87
|
+
extensions = [],
|
|
88
|
+
// All other options are passed directly to useTestDriver
|
|
89
|
+
...testDriverOptions
|
|
90
|
+
} = options;
|
|
91
|
+
|
|
92
|
+
// Set up TestDriver client - all options pass through directly
|
|
93
|
+
const client = TestDriver(context, testDriverOptions);
|
|
94
|
+
|
|
95
|
+
// Wait for client to connect (if autoConnect was enabled)
|
|
96
|
+
if (client.__connectionPromise) {
|
|
97
|
+
await client.__connectionPromise;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Set up Dashcam if enabled (dashcam is built into TestDriver)
|
|
101
|
+
if (enableDashcam) {
|
|
102
|
+
await client.dashcam.start();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Install extensions if provided
|
|
106
|
+
for (const extension of extensions) {
|
|
107
|
+
const os = testDriverOptions.os || 'linux';
|
|
108
|
+
const shell = os === 'windows' ? 'pwsh' : 'sh';
|
|
109
|
+
await client.exec(
|
|
110
|
+
shell,
|
|
111
|
+
`code --install-extension ${extension}`,
|
|
112
|
+
60000,
|
|
113
|
+
true
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Launch VS Code
|
|
118
|
+
const os = testDriverOptions.os || 'linux';
|
|
119
|
+
const shell = os === 'windows' ? 'pwsh' : 'sh';
|
|
120
|
+
const workspaceArg = workspace ? `"${workspace}"` : '';
|
|
121
|
+
|
|
122
|
+
if (os === 'windows') {
|
|
123
|
+
await client.exec(
|
|
124
|
+
shell,
|
|
125
|
+
`Start-Process code -ArgumentList ${workspaceArg}`,
|
|
126
|
+
30000
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
await client.exec(
|
|
130
|
+
shell,
|
|
131
|
+
`code ${workspaceArg} >/dev/null 2>&1 &`,
|
|
132
|
+
30000
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Wait for VS Code to be ready
|
|
137
|
+
await client.focusApplication('Visual Studio Code');
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
testdriver: client,
|
|
141
|
+
vscode: client, // Alias for semantic clarity
|
|
142
|
+
dashcam: enableDashcam ? client.dashcam : null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create a custom preset
|
|
148
|
+
* Builder function for creating your own presets
|
|
149
|
+
*
|
|
150
|
+
* @param {object} config - Preset configuration
|
|
151
|
+
* @param {Function} config.setup - Setup function (async)
|
|
152
|
+
* @param {string} config.name - Preset name
|
|
153
|
+
* @param {object} config.defaults - Default options (accepts all useTestDriver options)
|
|
154
|
+
* @returns {Function} Preset function
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* const myElectronPreset = createPreset({
|
|
158
|
+
* name: 'Electron App',
|
|
159
|
+
* defaults: { os: 'linux', dashcam: true },
|
|
160
|
+
* async setup(context, client, dashcam, options) {
|
|
161
|
+
* await client.exec('sh', `electron ${options.appPath} &`, 30000);
|
|
162
|
+
* await client.focusApplication('Electron');
|
|
163
|
+
* return { app: client };
|
|
164
|
+
* }
|
|
165
|
+
* });
|
|
166
|
+
*
|
|
167
|
+
* // Use your custom preset
|
|
168
|
+
* test('my test', async (context) => {
|
|
169
|
+
* const { app } = await myElectronPreset(context, {
|
|
170
|
+
* appPath: './dist',
|
|
171
|
+
* newSandbox: false // All useTestDriver options work!
|
|
172
|
+
* });
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
export function createPreset(config) {
|
|
176
|
+
const { setup, name = 'Custom Preset', defaults = {} } = config;
|
|
177
|
+
|
|
178
|
+
if (typeof setup !== 'function') {
|
|
179
|
+
throw new Error(`Preset "${name}" requires a setup function`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return async function preset(context, options = {}) {
|
|
183
|
+
const finalOptions = { ...defaults, ...options };
|
|
184
|
+
const {
|
|
185
|
+
dashcam: enableDashcam = true,
|
|
186
|
+
// All other options are passed directly to TestDriver
|
|
187
|
+
...testDriverOptions
|
|
188
|
+
} = finalOptions;
|
|
189
|
+
|
|
190
|
+
// Set up TestDriver client - all options pass through directly
|
|
191
|
+
const client = TestDriver(context, testDriverOptions);
|
|
192
|
+
|
|
193
|
+
// Wait for client to connect (if autoConnect was enabled)
|
|
194
|
+
if (client.__connectionPromise) {
|
|
195
|
+
await client.__connectionPromise;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Set up Dashcam if enabled (dashcam is built into TestDriver)
|
|
199
|
+
if (enableDashcam) {
|
|
200
|
+
await client.dashcam.start();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Call user's setup function
|
|
204
|
+
const result = await setup(context, client, enableDashcam ? client.dashcam : null, finalOptions);
|
|
205
|
+
|
|
206
|
+
// Ensure we return testdriver and dashcam
|
|
207
|
+
return {
|
|
208
|
+
testdriver: client,
|
|
209
|
+
dashcam: enableDashcam ? client.dashcam : null,
|
|
210
|
+
...result,
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Run Electron App
|
|
217
|
+
* Automatically sets up an Electron application with TestDriver
|
|
218
|
+
*
|
|
219
|
+
* @param {object} context - Vitest test context
|
|
220
|
+
* @param {object} options - Preset options (accepts all useTestDriver options)
|
|
221
|
+
* @param {string} options.appPath - Path to Electron app (required)
|
|
222
|
+
* @param {string[]} [options.args=[]] - Additional electron args
|
|
223
|
+
* @param {boolean} [options.dashcam=true] - Enable Dashcam recording
|
|
224
|
+
* @param {boolean} [options.newSandbox=true] - Create new sandbox
|
|
225
|
+
* @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
|
|
226
|
+
* @param {string} [options.apiKey] - TestDriver API key
|
|
227
|
+
* @param {string} [options.apiRoot] - API endpoint
|
|
228
|
+
* @param {boolean} [options.autoConnect=true] - Automatically connect to sandbox
|
|
229
|
+
* @returns {Promise<{testdriver: TestDriver, app: TestDriver, dashcam: Dashcam}>}
|
|
230
|
+
*/
|
|
231
|
+
export const electron = createPreset({
|
|
232
|
+
name: 'Electron App',
|
|
233
|
+
defaults: { dashcam: true, args: [] },
|
|
234
|
+
async setup(context, client, dashcam, options) {
|
|
235
|
+
const { appPath, args = [], os = 'linux' } = options;
|
|
236
|
+
|
|
237
|
+
if (!appPath) {
|
|
238
|
+
throw new Error('electron preset requires appPath option');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const shell = os === 'windows' ? 'pwsh' : 'sh';
|
|
242
|
+
const argsString = args.join(' ');
|
|
243
|
+
|
|
244
|
+
if (os === 'windows') {
|
|
245
|
+
await client.exec(
|
|
246
|
+
shell,
|
|
247
|
+
`Start-Process electron -ArgumentList "${appPath}", ${argsString}`,
|
|
248
|
+
30000
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
await client.exec(
|
|
252
|
+
shell,
|
|
253
|
+
`electron "${appPath}" ${argsString} >/dev/null 2>&1 &`,
|
|
254
|
+
30000
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await client.focusApplication('Electron');
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
testdriver: client,
|
|
262
|
+
app: client,
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Run Web App (generic browser)
|
|
269
|
+
* Simplified preset for any web application
|
|
270
|
+
*
|
|
271
|
+
* @param {object} context - Vitest test context
|
|
272
|
+
* @param {object} options - Preset options (accepts all useTestDriver options)
|
|
273
|
+
* @param {string} options.url - URL to navigate to (required)
|
|
274
|
+
* @param {string} [options.browser='chrome'] - Browser to use: 'chrome', 'firefox', 'edge'
|
|
275
|
+
* @param {boolean} [options.newSandbox=true] - Create new sandbox
|
|
276
|
+
* @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
|
|
277
|
+
* @param {boolean} [options.dashcam=true] - Enable Dashcam recording
|
|
278
|
+
* @returns {Promise<{testdriver: TestDriver, dashcam: Dashcam}>}
|
|
279
|
+
*/
|
|
280
|
+
export async function webApp(context, options = {}) {
|
|
281
|
+
const { browser = 'chrome', ...restOptions } = options;
|
|
282
|
+
|
|
283
|
+
// Currently only Chrome is implemented
|
|
284
|
+
// All options are automatically forwarded to the browser preset
|
|
285
|
+
if (browser === 'chrome') {
|
|
286
|
+
return chrome(context, restOptions);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
throw new Error(`Browser "${browser}" not yet implemented. Use 'chrome' for now.`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Provision application preset
|
|
294
|
+
* Main entry point for provisioning any application preset
|
|
295
|
+
*
|
|
296
|
+
* @param {string} app - Application type: 'chrome', 'vscode', 'electron', 'webapp'
|
|
297
|
+
* @param {object} options - Preset options (varies by app type)
|
|
298
|
+
* @param {object} context - Vitest test context
|
|
299
|
+
* @returns {Promise<{testdriver: TestDriver, dashcam: Dashcam, ...}>}
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* test('my test', async (context) => {
|
|
303
|
+
* const { testdriver } = await provision('chrome', {
|
|
304
|
+
* url: 'https://example.com'
|
|
305
|
+
* }, context);
|
|
306
|
+
*
|
|
307
|
+
* await testdriver.find('button').click();
|
|
308
|
+
* });
|
|
309
|
+
*/
|
|
310
|
+
export async function provision(app, options = {}, context) {
|
|
311
|
+
const presets = {
|
|
312
|
+
chrome,
|
|
313
|
+
vscode,
|
|
314
|
+
electron,
|
|
315
|
+
webapp: webApp,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const preset = presets[app.toLowerCase()];
|
|
319
|
+
|
|
320
|
+
if (!preset) {
|
|
321
|
+
throw new Error(`Unknown app type "${app}". Available: ${Object.keys(presets).join(', ')}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return preset(context, options);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Export aliases for backwards compatibility
|
|
328
|
+
export const chromePreset = chrome;
|
|
329
|
+
export const vscodePreset = vscode;
|
|
330
|
+
export const electronPreset = electron;
|
|
331
|
+
export const webAppPreset = webApp;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended Vitest test functions for TestDriver
|
|
3
|
+
*
|
|
4
|
+
* Provides custom test modifiers:
|
|
5
|
+
* - it.once() - Only runs once per sandbox session (skipped on reconnect)
|
|
6
|
+
*
|
|
7
|
+
* The reconnection state is determined by the TestDriver SDK after connect().
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { describe, it, beforeAll, expect } from 'testdriverai/vitest';
|
|
11
|
+
* import TestDriver from 'testdriverai';
|
|
12
|
+
*
|
|
13
|
+
* describe('My Test', () => {
|
|
14
|
+
* let testdriver;
|
|
15
|
+
*
|
|
16
|
+
* beforeAll(async () => {
|
|
17
|
+
* testdriver = new TestDriver(process.env.TD_API_KEY);
|
|
18
|
+
* await testdriver.connect();
|
|
19
|
+
*
|
|
20
|
+
* // Store globally so it.once() can access it
|
|
21
|
+
* globalThis.__testdriver = testdriver;
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Only runs once per sandbox session (skipped on reconnect)
|
|
25
|
+
* it.once('launch the application', async () => {
|
|
26
|
+
* await testdriver.exec('sh', 'google-chrome https://example.com', 5000);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Always runs
|
|
30
|
+
* it('click the button', async () => {
|
|
31
|
+
* await testdriver.find('Button').click();
|
|
32
|
+
* });
|
|
33
|
+
* });
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { afterAll, beforeAll, expect, describe as vitestDescribe, it as vitestIt, test as vitestTest } from 'vitest';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the TestDriver instance from global state
|
|
40
|
+
* @returns {Object|null} TestDriver instance or null
|
|
41
|
+
*/
|
|
42
|
+
function getTestDriver() {
|
|
43
|
+
return globalThis.__testdriver || null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if we're reconnected to an existing sandbox
|
|
48
|
+
* Uses the SDK's isReconnected property set after connect()
|
|
49
|
+
* @returns {boolean} true if reconnected to existing sandbox
|
|
50
|
+
*/
|
|
51
|
+
function isReconnected() {
|
|
52
|
+
const testdriver = getTestDriver();
|
|
53
|
+
if (!testdriver) {
|
|
54
|
+
// No testdriver yet - assume new sandbox (setup should run)
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return testdriver.isReconnected === true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extended test function with .once() modifier
|
|
62
|
+
*/
|
|
63
|
+
function createExtendedIt(baseIt) {
|
|
64
|
+
const extended = function(name, fn, timeout) {
|
|
65
|
+
return baseIt(name, fn, timeout);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Copy all properties from base it
|
|
69
|
+
Object.assign(extended, baseIt);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* it.once() - Only runs once per sandbox session, skipped on reconnect
|
|
73
|
+
* Use for provisioning, app launch, initial navigation
|
|
74
|
+
*
|
|
75
|
+
* The test checks testdriver.isReconnected which is set by the SDK
|
|
76
|
+
* after connect() based on whether it reconnected to an existing
|
|
77
|
+
* sandbox or created a new one.
|
|
78
|
+
*/
|
|
79
|
+
extended.once = function(name, fn, timeout) {
|
|
80
|
+
return baseIt(name, async (...args) => {
|
|
81
|
+
if (isReconnected()) {
|
|
82
|
+
console.log(`⏭️ Skipping (already run in this sandbox): ${name}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
return fn(...args);
|
|
86
|
+
}, timeout);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Preserve skip, only, todo, etc.
|
|
90
|
+
if (baseIt.skip) extended.skip = baseIt.skip;
|
|
91
|
+
if (baseIt.only) extended.only = baseIt.only;
|
|
92
|
+
if (baseIt.todo) extended.todo = baseIt.todo;
|
|
93
|
+
if (baseIt.concurrent) extended.concurrent = baseIt.concurrent;
|
|
94
|
+
if (baseIt.sequential) extended.sequential = baseIt.sequential;
|
|
95
|
+
|
|
96
|
+
return extended;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create extended test functions
|
|
100
|
+
export const it = createExtendedIt(vitestIt);
|
|
101
|
+
export const test = createExtendedIt(vitestTest);
|
|
102
|
+
|
|
103
|
+
// Re-export other vitest functions unchanged
|
|
104
|
+
export { afterAll, beforeAll, vitestDescribe as describe, expect };
|
|
105
|
+
|
|
106
|
+
// Also export utility for manual checking
|
|
107
|
+
export { getTestDriver, isReconnected };
|
|
108
|
+
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript definitions for TestDriver Vitest Hooks
|
|
3
|
+
* @module testdriverai/vitest/hooks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Dashcam, DashcamOptions, TestDriver, TestDriverOptions } from '../core/index';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Vitest test context (from test function parameter)
|
|
10
|
+
*/
|
|
11
|
+
export interface VitestContext {
|
|
12
|
+
/**
|
|
13
|
+
* Current test task
|
|
14
|
+
*/
|
|
15
|
+
task: any;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register cleanup handler
|
|
19
|
+
*/
|
|
20
|
+
onTestFinished?: (fn: () => void | Promise<void>) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Options for useTestDriver hook
|
|
25
|
+
*/
|
|
26
|
+
export interface UseTestDriverOptions extends TestDriverOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Automatically connect to sandbox (default: true)
|
|
29
|
+
*/
|
|
30
|
+
autoConnect?: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create new sandbox (default: true)
|
|
34
|
+
*/
|
|
35
|
+
new?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Options for useDashcam hook
|
|
40
|
+
*/
|
|
41
|
+
export interface UseDashcamOptions extends DashcamOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Automatically authenticate (default: true)
|
|
44
|
+
*/
|
|
45
|
+
autoAuth?: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Automatically start recording (default: false)
|
|
49
|
+
*/
|
|
50
|
+
autoStart?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Automatically stop recording at test end (default: false)
|
|
54
|
+
*/
|
|
55
|
+
autoStop?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Use TestDriver client in a test
|
|
60
|
+
* Creates and manages TestDriver instance for the current test
|
|
61
|
+
*
|
|
62
|
+
* @param context - Vitest test context (from async (context) => {})
|
|
63
|
+
* @param options - TestDriver options
|
|
64
|
+
* @returns TestDriver client instance
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* test('my test', async (context) => {
|
|
68
|
+
* const client = useTestDriver(context, { os: 'linux' });
|
|
69
|
+
* await client.find('Login button').click();
|
|
70
|
+
* });
|
|
71
|
+
*/
|
|
72
|
+
export function useTestDriver(context: VitestContext, options?: UseTestDriverOptions): TestDriver;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Use Dashcam in a test
|
|
76
|
+
* Creates and manages Dashcam instance for the current test
|
|
77
|
+
*
|
|
78
|
+
* @param context - Vitest test context
|
|
79
|
+
* @param client - TestDriver client instance (from useTestDriver)
|
|
80
|
+
* @param options - Dashcam options
|
|
81
|
+
* @returns Dashcam instance
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* test('my test', async (context) => {
|
|
85
|
+
* const client = useTestDriver(context);
|
|
86
|
+
* const dashcam = useDashcam(context, client, {
|
|
87
|
+
* autoStart: true,
|
|
88
|
+
* autoStop: true
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* await client.find('button').click();
|
|
92
|
+
* });
|
|
93
|
+
*/
|
|
94
|
+
export function useDashcam(context: VitestContext, client: TestDriver, options?: UseDashcamOptions): Dashcam;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Use TestDriver with Dashcam in one call
|
|
98
|
+
* Combined hook for the simplest usage pattern
|
|
99
|
+
*
|
|
100
|
+
* @param context - Vitest test context
|
|
101
|
+
* @param options - Combined options for TestDriver and Dashcam
|
|
102
|
+
* @returns Object with client and dashcam instances
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* test('my test', async (context) => {
|
|
106
|
+
* const { client, dashcam } = useTestDriverWithDashcam(context, {
|
|
107
|
+
* os: 'linux'
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* await client.find('button').click();
|
|
111
|
+
* });
|
|
112
|
+
*/
|
|
113
|
+
export function useTestDriverWithDashcam(
|
|
114
|
+
context: VitestContext,
|
|
115
|
+
options?: UseTestDriverOptions & UseDashcamOptions
|
|
116
|
+
): {
|
|
117
|
+
client: TestDriver;
|
|
118
|
+
dashcam: Dashcam;
|
|
119
|
+
};
|