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,360 @@
|
|
|
1
|
+
# Hooks API
|
|
2
|
+
|
|
3
|
+
React-style hooks for managing TestDriver and Dashcam lifecycle in Vitest tests.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Hooks provide automatic lifecycle management - no more forgetting to disconnect or stop recording. They integrate seamlessly with Vitest's test context.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import { test } from 'vitest';
|
|
11
|
+
import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
|
|
12
|
+
|
|
13
|
+
test('my test', async (context) => {
|
|
14
|
+
const client = useTestDriver(context, { os: 'linux' });
|
|
15
|
+
const dashcam = useDashcam(context, client, {
|
|
16
|
+
autoStart: true,
|
|
17
|
+
autoStop: true
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Test code here
|
|
21
|
+
// Automatic cleanup at test end!
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Available Hooks
|
|
26
|
+
|
|
27
|
+
### useTestDriver
|
|
28
|
+
|
|
29
|
+
Creates and manages a TestDriver instance with automatic cleanup.
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { useTestDriver } from 'testdriverai/vitest/hooks';
|
|
33
|
+
|
|
34
|
+
test('my test', async (context) => {
|
|
35
|
+
const client = useTestDriver(context, {
|
|
36
|
+
os: 'linux', // Target OS (default: 'linux')
|
|
37
|
+
autoConnect: true, // Auto-connect to sandbox (default: true)
|
|
38
|
+
apiKey: process.env.TD_API_KEY,
|
|
39
|
+
apiRoot: 'https://...',
|
|
40
|
+
resolution: '1366x768',
|
|
41
|
+
analytics: true
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Client is ready to use
|
|
45
|
+
await client.find('button').click();
|
|
46
|
+
|
|
47
|
+
// Auto-disconnects at test end
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Parameters:**
|
|
52
|
+
- `context` - Vitest test context (required)
|
|
53
|
+
- `options` - TestDriver configuration options
|
|
54
|
+
|
|
55
|
+
**Options:**
|
|
56
|
+
- `os` - Target OS: `'linux'`, `'mac'`, or `'windows'` (default: `'linux'`)
|
|
57
|
+
- `autoConnect` - Automatically connect to sandbox (default: `true`)
|
|
58
|
+
- `apiKey` - TestDriver API key (default: `process.env.TD_API_KEY`)
|
|
59
|
+
- `apiRoot` - API endpoint URL
|
|
60
|
+
- `resolution` - Screen resolution (default: `'1366x768'`)
|
|
61
|
+
- `analytics` - Enable analytics (default: `true`)
|
|
62
|
+
- `cacheThresholds` - Cache settings for find operations
|
|
63
|
+
|
|
64
|
+
**Returns:**
|
|
65
|
+
- TestDriver instance, ready to use
|
|
66
|
+
|
|
67
|
+
### useDashcam
|
|
68
|
+
|
|
69
|
+
Creates and manages a Dashcam instance with optional auto-lifecycle.
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import { useDashcam } from 'testdriverai/vitest/hooks';
|
|
73
|
+
|
|
74
|
+
test('my test', async (context) => {
|
|
75
|
+
// Assuming you have a client from useTestDriver
|
|
76
|
+
const dashcam = useDashcam(context, client, {
|
|
77
|
+
autoAuth: true, // Auto-authenticate (default: true)
|
|
78
|
+
autoStart: true, // Auto-start recording (default: false)
|
|
79
|
+
autoStop: true, // Auto-stop at test end (default: false)
|
|
80
|
+
apiKey: process.env.TD_API_KEY // Optional - uses TD_API_KEY by default
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// If autoStart is false, manually start:
|
|
84
|
+
// await dashcam.start();
|
|
85
|
+
|
|
86
|
+
// Your test code
|
|
87
|
+
|
|
88
|
+
// If autoStop is true, URL is saved automatically
|
|
89
|
+
// Otherwise, manually stop:
|
|
90
|
+
// const url = await dashcam.stop();
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Parameters:**
|
|
95
|
+
- `context` - Vitest test context (required)
|
|
96
|
+
- `client` - TestDriver instance from `useTestDriver()` (required)
|
|
97
|
+
- `options` - Dashcam configuration options
|
|
98
|
+
|
|
99
|
+
**Options:**
|
|
100
|
+
- `autoAuth` - Automatically authenticate (default: `true`)
|
|
101
|
+
- `autoStart` - Automatically start recording (default: `false`)
|
|
102
|
+
- `autoStop` - Automatically stop recording at test end (default: `false`)
|
|
103
|
+
- `apiKey` - API key (default: `process.env.TD_API_KEY`, same as TestDriver)
|
|
104
|
+
|
|
105
|
+
**Returns:**
|
|
106
|
+
- Dashcam instance
|
|
107
|
+
|
|
108
|
+
### useTestDriverWithDashcam
|
|
109
|
+
|
|
110
|
+
Combined hook for the simplest usage - everything automatic.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
import { useTestDriverWithDashcam } from 'testdriverai/vitest/hooks';
|
|
114
|
+
|
|
115
|
+
test('my test', async (context) => {
|
|
116
|
+
const { client, dashcam } = useTestDriverWithDashcam(context, {
|
|
117
|
+
os: 'linux',
|
|
118
|
+
// All TestDriver options +
|
|
119
|
+
// All Dashcam options (autoAuth, autoStart, autoStop all true by default)
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Everything is ready - just write your test!
|
|
123
|
+
await client.find('button').click();
|
|
124
|
+
|
|
125
|
+
// Dashcam auto-stops, client auto-disconnects
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Parameters:**
|
|
130
|
+
- `context` - Vitest test context (required)
|
|
131
|
+
- `options` - Combined TestDriver and Dashcam options
|
|
132
|
+
|
|
133
|
+
**Returns:**
|
|
134
|
+
- `{ client, dashcam }` - Both instances, fully configured
|
|
135
|
+
|
|
136
|
+
## Complete Examples
|
|
137
|
+
|
|
138
|
+
### Basic Test with Hooks
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
import { describe, it, expect } from 'vitest';
|
|
142
|
+
import { useTestDriver } from 'testdriverai/vitest/hooks';
|
|
143
|
+
|
|
144
|
+
describe('Search Tests', () => {
|
|
145
|
+
it('should search successfully', async (context) => {
|
|
146
|
+
const client = useTestDriver(context, { os: 'linux' });
|
|
147
|
+
|
|
148
|
+
await client.focusApplication('Google Chrome');
|
|
149
|
+
await client.find('search box').type('testdriverai');
|
|
150
|
+
await client.pressKeys(['enter']);
|
|
151
|
+
|
|
152
|
+
const result = await client.assert('search results appear');
|
|
153
|
+
expect(result).toBeTruthy();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### With Dashcam Recording
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
|
|
162
|
+
|
|
163
|
+
test('recorded test', async (context) => {
|
|
164
|
+
const client = useTestDriver(context, { os: 'linux' });
|
|
165
|
+
const dashcam = useDashcam(context, client, {
|
|
166
|
+
autoAuth: true,
|
|
167
|
+
autoStart: true,
|
|
168
|
+
autoStop: true // URL saved automatically
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await client.focusApplication('Google Chrome');
|
|
172
|
+
await client.find('button').click();
|
|
173
|
+
|
|
174
|
+
// Dashcam URL will be in test results
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Fully Automatic
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
import { useTestDriverWithDashcam } from 'testdriverai/vitest/hooks';
|
|
182
|
+
|
|
183
|
+
test('automatic everything', async (context) => {
|
|
184
|
+
const { client } = useTestDriverWithDashcam(context);
|
|
185
|
+
|
|
186
|
+
await client.focusApplication('Google Chrome');
|
|
187
|
+
await client.find('login').click();
|
|
188
|
+
|
|
189
|
+
// Dashcam + TestDriver managed automatically
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Manual Control (Advanced)
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
|
|
197
|
+
|
|
198
|
+
test('manual control', async (context) => {
|
|
199
|
+
const client = useTestDriver(context, {
|
|
200
|
+
os: 'linux',
|
|
201
|
+
autoConnect: false // We'll connect manually
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Manual connection
|
|
205
|
+
await client.auth();
|
|
206
|
+
await client.connect({ new: true });
|
|
207
|
+
|
|
208
|
+
const dashcam = useDashcam(context, client, {
|
|
209
|
+
autoAuth: true,
|
|
210
|
+
autoStart: false, // Manual start
|
|
211
|
+
autoStop: false // Manual stop
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Start recording when ready
|
|
215
|
+
await dashcam.start();
|
|
216
|
+
|
|
217
|
+
await client.find('button').click();
|
|
218
|
+
|
|
219
|
+
// Stop and get URL
|
|
220
|
+
const url = await dashcam.stop();
|
|
221
|
+
console.log('Replay:', url);
|
|
222
|
+
|
|
223
|
+
// Cleanup still automatic!
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## How Hooks Work
|
|
228
|
+
|
|
229
|
+
Hooks use Vitest's `context.onTestFinished()` to register cleanup handlers:
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
// Internally, hooks do this:
|
|
233
|
+
export function useTestDriver(context, options = {}) {
|
|
234
|
+
const client = new TestDriver(apiKey, options);
|
|
235
|
+
|
|
236
|
+
// Register cleanup
|
|
237
|
+
context.onTestFinished(async () => {
|
|
238
|
+
await client.disconnect();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return client;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This ensures cleanup always runs, even if your test fails.
|
|
246
|
+
|
|
247
|
+
## TypeScript Support
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { useTestDriver, UseDashcamOptions } from 'testdriverai/vitest/hooks';
|
|
251
|
+
|
|
252
|
+
test('typed test', async (context) => {
|
|
253
|
+
const client = useTestDriver(context, {
|
|
254
|
+
os: 'linux', // ✅ Type-safe
|
|
255
|
+
resolution: '1920x1080'
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const dashcamOptions: UseDashcamOptions = {
|
|
259
|
+
autoAuth: true,
|
|
260
|
+
autoStart: true,
|
|
261
|
+
autoStop: true
|
|
262
|
+
// apiKey is optional - uses TD_API_KEY by default
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const dashcam = useDashcam(context, client, dashcamOptions);
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## When to Use Hooks
|
|
270
|
+
|
|
271
|
+
**Use hooks when:**
|
|
272
|
+
- You want automatic cleanup
|
|
273
|
+
- You're testing with Vitest
|
|
274
|
+
- You need custom lifecycle control
|
|
275
|
+
- You're a power user who understands the lifecycle
|
|
276
|
+
|
|
277
|
+
**Use `provision()` instead when:**
|
|
278
|
+
- You're testing Chrome, VS Code, or Electron
|
|
279
|
+
- You want zero configuration
|
|
280
|
+
- You're a beginner
|
|
281
|
+
- You want the simplest API
|
|
282
|
+
|
|
283
|
+
**Use core classes directly when:**
|
|
284
|
+
- You need full manual control
|
|
285
|
+
- You're not using Vitest
|
|
286
|
+
- You're debugging lifecycle issues
|
|
287
|
+
|
|
288
|
+
## Best Practices
|
|
289
|
+
|
|
290
|
+
1. **Always pass context** - Required for automatic cleanup
|
|
291
|
+
2. **One client per test** - Don't share clients between tests
|
|
292
|
+
3. **Use autoStart/autoStop for Dashcam** - Unless you have specific timing needs
|
|
293
|
+
4. **Leverage TypeScript** - Get autocomplete and catch errors early
|
|
294
|
+
5. **Keep options in variables** - Easier to maintain and reuse
|
|
295
|
+
|
|
296
|
+
## Common Patterns
|
|
297
|
+
|
|
298
|
+
### Shared Setup
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
function setupBrowser(context) {
|
|
302
|
+
const client = useTestDriver(context, { os: 'linux' });
|
|
303
|
+
const dashcam = useDashcam(context, client, {
|
|
304
|
+
autoAuth: true,
|
|
305
|
+
autoStart: true,
|
|
306
|
+
autoStop: true
|
|
307
|
+
});
|
|
308
|
+
return { client, dashcam };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
test('test 1', async (context) => {
|
|
312
|
+
const { client } = setupBrowser(context);
|
|
313
|
+
// ...
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test('test 2', async (context) => {
|
|
317
|
+
const { client } = setupBrowser(context);
|
|
318
|
+
// ...
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Conditional Dashcam
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
test('maybe record', async (context) => {
|
|
326
|
+
const client = useTestDriver(context);
|
|
327
|
+
|
|
328
|
+
const shouldRecord = process.env.RECORD_TESTS === 'true';
|
|
329
|
+
const dashcam = shouldRecord
|
|
330
|
+
? useDashcam(context, client, { autoStart: true, autoStop: true })
|
|
331
|
+
: null;
|
|
332
|
+
|
|
333
|
+
// Test code...
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Error Handling
|
|
338
|
+
|
|
339
|
+
Cleanup runs even if your test fails:
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
test('failing test', async (context) => {
|
|
343
|
+
const client = useTestDriver(context);
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
await client.find('nonexistent element').click();
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('Test failed:', error);
|
|
349
|
+
throw error; // Test marked as failed
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// client.disconnect() still runs automatically
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## See Also
|
|
357
|
+
|
|
358
|
+
- [Provision API](./PROVISION.md) - Simpler API for common apps
|
|
359
|
+
- [Core Classes](./CORE.md) - Manual control
|
|
360
|
+
- [Migration Guide](../MIGRATION.md) - Upgrading from v6
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Progressive Disclosure APIs
|
|
2
|
+
|
|
3
|
+
TestDriver v7 introduces **progressive disclosure** - three levels of API to match your experience and needs.
|
|
4
|
+
|
|
5
|
+
## Choose Your Level
|
|
6
|
+
|
|
7
|
+
### 🟢 Beginner: Provision API
|
|
8
|
+
|
|
9
|
+
**Best for:** Getting started, testing common apps (Chrome, VS Code, Electron)
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
import { provision } from 'testdriverai/presets';
|
|
13
|
+
|
|
14
|
+
test('my test', async (context) => {
|
|
15
|
+
const { testdriver } = await provision('chrome', {
|
|
16
|
+
url: 'https://example.com'
|
|
17
|
+
}, context);
|
|
18
|
+
|
|
19
|
+
await testdriver.find('Login').click();
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Why use this:**
|
|
24
|
+
- ✅ Zero configuration
|
|
25
|
+
- ✅ One line setup
|
|
26
|
+
- ✅ Automatic everything (launch, focus, cleanup, recording)
|
|
27
|
+
- ✅ Perfect for beginners
|
|
28
|
+
|
|
29
|
+
[Learn more →](./PROVISION.md)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### 🟡 Intermediate: Hooks API
|
|
34
|
+
|
|
35
|
+
**Best for:** Power users, custom workflows, manual lifecycle control
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
|
|
39
|
+
|
|
40
|
+
test('my test', async (context) => {
|
|
41
|
+
const client = useTestDriver(context, { os: 'linux' });
|
|
42
|
+
const dashcam = useDashcam(context, client, {
|
|
43
|
+
autoStart: true,
|
|
44
|
+
autoStop: true
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await client.find('button').click();
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Why use this:**
|
|
52
|
+
- ✅ Automatic cleanup
|
|
53
|
+
- ✅ Flexible configuration
|
|
54
|
+
- ✅ Control over lifecycle
|
|
55
|
+
- ✅ Works with any application
|
|
56
|
+
|
|
57
|
+
[Learn more →](./HOOKS.md)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### 🔴 Advanced: Core Classes
|
|
62
|
+
|
|
63
|
+
**Best for:** Full control, non-Vitest frameworks, custom integrations
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import { TestDriver, Dashcam } from 'testdriverai/core';
|
|
67
|
+
|
|
68
|
+
const client = new TestDriver(apiKey, { os: 'linux' });
|
|
69
|
+
await client.auth();
|
|
70
|
+
await client.connect();
|
|
71
|
+
|
|
72
|
+
const dashcam = new Dashcam(client);
|
|
73
|
+
await dashcam.start();
|
|
74
|
+
|
|
75
|
+
// Your code
|
|
76
|
+
|
|
77
|
+
await dashcam.stop();
|
|
78
|
+
await client.disconnect();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Why use this:**
|
|
82
|
+
- ✅ Complete manual control
|
|
83
|
+
- ✅ No framework dependencies
|
|
84
|
+
- ✅ Works anywhere
|
|
85
|
+
- ✅ Maximum flexibility
|
|
86
|
+
|
|
87
|
+
[Learn more →](./CORE.md)
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Architecture
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
┌─────────────────────────────────────────────┐
|
|
95
|
+
│ 🟢 Provision API (Easiest) │
|
|
96
|
+
│ provision('chrome', options, context) │
|
|
97
|
+
└─────────────────┬───────────────────────────┘
|
|
98
|
+
│
|
|
99
|
+
┌─────────────────▼───────────────────────────┐
|
|
100
|
+
│ 🟡 Hooks API (Flexible) │
|
|
101
|
+
│ useTestDriver(), useDashcam() │
|
|
102
|
+
└─────────────────┬───────────────────────────┘
|
|
103
|
+
│
|
|
104
|
+
┌─────────────────▼───────────────────────────┐
|
|
105
|
+
│ 🔴 Core Classes (Full Control) │
|
|
106
|
+
│ new TestDriver(), new Dashcam() │
|
|
107
|
+
└─────────────────────────────────────────────┘
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each level builds on the one below it. Choose the highest level that meets your needs.
|
|
111
|
+
|
|
112
|
+
## Quick Comparison
|
|
113
|
+
|
|
114
|
+
| Feature | Provision | Hooks | Core |
|
|
115
|
+
|---------|-----------|-------|------|
|
|
116
|
+
| Setup complexity | ⭐ 1 line | ⭐⭐ 2-3 lines | ⭐⭐⭐ 5+ lines |
|
|
117
|
+
| Cleanup | ✅ Automatic | ✅ Automatic | ❌ Manual |
|
|
118
|
+
| Framework requirement | Vitest | Vitest | None |
|
|
119
|
+
| Application support | Chrome, VS Code, Electron | Any | Any |
|
|
120
|
+
| Launch application | ✅ Automatic | ❌ Manual | ❌ Manual |
|
|
121
|
+
| Dashcam recording | ✅ Automatic | ⚡ Optional automatic | ❌ Manual |
|
|
122
|
+
| TypeScript support | ✅ Full | ✅ Full | ✅ Full |
|
|
123
|
+
| Customization | ⚡ Limited | ✅ High | ✅ Complete |
|
|
124
|
+
|
|
125
|
+
## When to Use Each
|
|
126
|
+
|
|
127
|
+
### Use Provision API when:
|
|
128
|
+
- ✅ Testing Chrome, VS Code, or Electron
|
|
129
|
+
- ✅ You're a beginner
|
|
130
|
+
- ✅ You want the simplest possible code
|
|
131
|
+
- ✅ You're okay with defaults
|
|
132
|
+
|
|
133
|
+
### Use Hooks API when:
|
|
134
|
+
- ✅ You need custom application launch
|
|
135
|
+
- ✅ You want automatic cleanup
|
|
136
|
+
- ✅ You're using Vitest
|
|
137
|
+
- ✅ You need fine-grained lifecycle control
|
|
138
|
+
|
|
139
|
+
### Use Core Classes when:
|
|
140
|
+
- ✅ Not using Vitest
|
|
141
|
+
- ✅ Integrating with other frameworks
|
|
142
|
+
- ✅ Building custom abstractions
|
|
143
|
+
- ✅ Debugging lifecycle issues
|
|
144
|
+
- ✅ Need complete manual control
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
### Simple Chrome Test
|
|
149
|
+
|
|
150
|
+
**Provision (1 line):**
|
|
151
|
+
```javascript
|
|
152
|
+
const { testdriver } = await provision('chrome', { url }, context);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Hooks (3 lines):**
|
|
156
|
+
```javascript
|
|
157
|
+
const client = useTestDriver(context);
|
|
158
|
+
await client.exec('sh', 'google-chrome "https://..." &', 30000);
|
|
159
|
+
await client.focusApplication('Google Chrome');
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Core (6+ lines):**
|
|
163
|
+
```javascript
|
|
164
|
+
const client = new TestDriver(apiKey, { os: 'linux' });
|
|
165
|
+
await client.auth();
|
|
166
|
+
await client.connect();
|
|
167
|
+
await client.exec('sh', 'google-chrome "https://..." &', 30000);
|
|
168
|
+
await client.focusApplication('Google Chrome');
|
|
169
|
+
// ... test code ...
|
|
170
|
+
await client.disconnect();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### With Dashcam Recording
|
|
174
|
+
|
|
175
|
+
**Provision (1 line):**
|
|
176
|
+
```javascript
|
|
177
|
+
const { testdriver, dashcam } = await provision('chrome', { url }, context);
|
|
178
|
+
// Recording automatic!
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Hooks (2 lines):**
|
|
182
|
+
```javascript
|
|
183
|
+
const client = useTestDriver(context);
|
|
184
|
+
const dashcam = useDashcam(context, client, { autoStart: true, autoStop: true });
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Core (7+ lines):**
|
|
188
|
+
```javascript
|
|
189
|
+
const client = new TestDriver(apiKey);
|
|
190
|
+
await client.auth();
|
|
191
|
+
await client.connect();
|
|
192
|
+
const dashcam = new Dashcam(client);
|
|
193
|
+
await dashcam.auth();
|
|
194
|
+
await dashcam.start();
|
|
195
|
+
// ... test code ...
|
|
196
|
+
await dashcam.stop();
|
|
197
|
+
await client.disconnect();
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Migration Path
|
|
201
|
+
|
|
202
|
+
Start simple, grow as needed:
|
|
203
|
+
|
|
204
|
+
1. **Start with Provision** - Get tests working quickly
|
|
205
|
+
2. **Move to Hooks** - When you need custom app launch
|
|
206
|
+
3. **Use Core** - Only if you need full control or non-Vitest
|
|
207
|
+
|
|
208
|
+
You can mix and match in the same project!
|
|
209
|
+
|
|
210
|
+
## Package Exports
|
|
211
|
+
|
|
212
|
+
All three APIs are available from the same package:
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
// Provision API
|
|
216
|
+
import { provision, chrome, vscode, electron } from 'testdriverai/presets';
|
|
217
|
+
|
|
218
|
+
// Hooks API
|
|
219
|
+
import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
|
|
220
|
+
|
|
221
|
+
// Core Classes
|
|
222
|
+
import { TestDriver, Dashcam } from 'testdriverai/core';
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## See Also
|
|
226
|
+
|
|
227
|
+
- [Provision API Reference](./PROVISION.md)
|
|
228
|
+
- [Hooks API Reference](./HOOKS.md)
|
|
229
|
+
- [Core Classes Reference](./CORE.md)
|
|
230
|
+
- [Migration Guide](../MIGRATION.md)
|