testdriverai 6.2.2 → 7.0.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/MIGRATION.md +389 -0
- package/PLUGIN_MIGRATION.md +222 -0
- package/PROMPT_CACHE.md +200 -0
- package/SDK_LOGGING.md +222 -0
- package/SDK_MIGRATION.md +474 -0
- package/SDK_README.md +1122 -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 +258 -68
- 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 +143 -188
- package/agent/lib/redraw.js +6 -3
- package/agent/lib/sandbox.js +19 -5
- package/agent/lib/sdk.js +1 -0
- package/agent/lib/system.js +0 -3
- package/agent/lib/validation.js +1 -7
- package/debug-locate-response.js +82 -0
- package/debug-screenshot-1763401388589.png +0 -0
- package/debugger/index.html +15 -4
- package/docs/ARCHITECTURE.md +424 -0
- package/docs/AWESOME_LOGS_QUICK_REF.md +100 -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 +232 -152
- 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/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/pressKeys.mdx +349 -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/quickstart.mdx +199 -0
- package/docs/v7/guides/migration.mdx +562 -0
- package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
- package/docs/v7/playwright.mdx +342 -0
- package/eslint.config.js +19 -1
- package/examples/run-tests-with-recording.sh +70 -0
- package/examples/screenshot-example.js +63 -0
- package/examples/sdk-awesome-logs-demo.js +177 -0
- package/examples/sdk-cache-thresholds.js +96 -0
- package/examples/sdk-element-properties.js +155 -0
- package/examples/sdk-simple-example.js +65 -0
- package/examples/test-recording-example.test.js +166 -0
- 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 +744 -0
- package/mcp-server/AI_GUIDELINES.md +57 -0
- package/package.json +18 -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 +735 -0
- package/sdk.js +1906 -0
- package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
- package/setup/aws/cloudformation.yaml +9 -2
- package/test/mcp-example-test.yaml +27 -0
- package/test-find-api.js +73 -0
- package/test-prompt-cache.js +96 -0
- package/test-sandbox-render.js +28 -0
- package/test-sdk-methods.js +15 -0
- package/test-sdk-refactor.js +53 -0
- package/test-stack-trace.mjs +57 -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 +44 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +70 -0
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +38 -0
- package/testdriver/acceptance-sdk/exec-js.test.mjs +55 -0
- package/testdriver/acceptance-sdk/exec-output.test.mjs +71 -0
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +69 -0
- package/testdriver/acceptance-sdk/focus-window.test.mjs +48 -0
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +41 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +43 -0
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +50 -0
- package/testdriver/acceptance-sdk/hover-text.test.mjs +41 -0
- package/testdriver/acceptance-sdk/match-image.test.mjs +48 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +64 -0
- package/testdriver/acceptance-sdk/prompt.test.mjs +45 -0
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +52 -0
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +51 -0
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +42 -0
- package/testdriver/acceptance-sdk/scroll.test.mjs +50 -0
- package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +239 -0
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +648 -0
- package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
- package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
- package/testdriver/acceptance-sdk/type.test.mjs +84 -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 +65 -0
- package/vitest.config.mjs.bak +44 -0
- package/.github/workflows/acceptance-v6.yml +0 -169
- 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,222 @@
|
|
|
1
|
+
# Vitest Reporter → Plugin Migration
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Converted the Vitest reporter to a plugin architecture with much simpler dashcam URL tracking.
|
|
6
|
+
|
|
7
|
+
## Key Changes
|
|
8
|
+
|
|
9
|
+
### 1. Plugin Architecture ✅
|
|
10
|
+
|
|
11
|
+
**Before (Reporter):**
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
class TestDriverReporter {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.testRun = null;
|
|
18
|
+
// ... many instance properties
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**After (Plugin):**
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
const pluginState = {
|
|
27
|
+
testRun: null,
|
|
28
|
+
testRunId: null,
|
|
29
|
+
dashcamUrls: new Map(), // In-memory tracking!
|
|
30
|
+
// ... all state in one place
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default function testDriverPlugin(options = {}) {
|
|
34
|
+
return {
|
|
35
|
+
name: "testdriver-plugin",
|
|
36
|
+
// ... hooks
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Dashcam URL Tracking (MUCH SIMPLER) ✅
|
|
42
|
+
|
|
43
|
+
**Before:**
|
|
44
|
+
|
|
45
|
+
- ❌ Write JSON to temp files in `/tmp/`
|
|
46
|
+
- ❌ Read temp files on test completion
|
|
47
|
+
- ❌ Complex matching logic (sessionId, taskId, filename patterns)
|
|
48
|
+
- ❌ Manual cleanup of temp files
|
|
49
|
+
- ❌ Race conditions with parallel tests
|
|
50
|
+
- ❌ Multiple fallback mechanisms (globalThis, registry, meta)
|
|
51
|
+
|
|
52
|
+
**After:**
|
|
53
|
+
|
|
54
|
+
- ✅ Simple in-memory Map (testId → dashcamUrl)
|
|
55
|
+
- ✅ Direct API: `registerDashcamUrl(testId, url, platform)`
|
|
56
|
+
- ✅ No file system operations
|
|
57
|
+
- ✅ No complex matching
|
|
58
|
+
- ✅ No cleanup needed
|
|
59
|
+
- ✅ Thread-safe for parallel tests
|
|
60
|
+
|
|
61
|
+
### 3. Usage in Tests
|
|
62
|
+
|
|
63
|
+
**Before:**
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// Tests had to write to temp files
|
|
67
|
+
const tempFile = path.join(
|
|
68
|
+
os.tmpdir(),
|
|
69
|
+
`testdriver-dashcam-${sessionId}-${taskId}-${Date.now()}.json`,
|
|
70
|
+
);
|
|
71
|
+
fs.writeFileSync(
|
|
72
|
+
tempFile,
|
|
73
|
+
JSON.stringify({
|
|
74
|
+
dashcamUrl,
|
|
75
|
+
replayObjectId,
|
|
76
|
+
platform: client.os,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
pid: process.pid,
|
|
79
|
+
sessionId,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// AND set global variables
|
|
84
|
+
globalThis.__testdriverMeta.__lastDashcamUrl__ = dashcamUrl;
|
|
85
|
+
|
|
86
|
+
// AND register in registry
|
|
87
|
+
globalThis.__testdriverRegistry.setClient(taskId, client);
|
|
88
|
+
|
|
89
|
+
// AND store in task.meta
|
|
90
|
+
task.meta.testdriverDashcamUrl = dashcamUrl;
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**After:**
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// Just one simple call
|
|
97
|
+
globalThis.__testdriverPlugin.registerDashcamUrl(taskId, dashcamUrl, client.os);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 4. Configuration
|
|
101
|
+
|
|
102
|
+
**Before:**
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
export default defineConfig({
|
|
106
|
+
test: {
|
|
107
|
+
reporters: [
|
|
108
|
+
"default",
|
|
109
|
+
[
|
|
110
|
+
"./interfaces/vitest-reporter.js",
|
|
111
|
+
{
|
|
112
|
+
apiKey: process.env.TD_API_KEY,
|
|
113
|
+
apiRoot: process.env.TD_API_ROOT,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**After:**
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
export default defineConfig({
|
|
125
|
+
plugins: [
|
|
126
|
+
testDriverPlugin({
|
|
127
|
+
apiKey: process.env.TD_API_KEY,
|
|
128
|
+
apiRoot: process.env.TD_API_ROOT,
|
|
129
|
+
}),
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Files Modified
|
|
135
|
+
|
|
136
|
+
### Plugin
|
|
137
|
+
|
|
138
|
+
- ✅ **Created**: `interfaces/vitest-plugin.mjs` (new plugin)
|
|
139
|
+
- ✅ **Deleted**: `interfaces/vitest-reporter.js` (old reporter)
|
|
140
|
+
- ✅ **Updated**: `vitest.config.mjs` (uses plugin)
|
|
141
|
+
- ✅ **Updated**: `vitest.config.example.js` (uses plugin)
|
|
142
|
+
|
|
143
|
+
### Test Helpers
|
|
144
|
+
|
|
145
|
+
- ✅ **Updated**: `testdriver/acceptance-sdk/setup/testHelpers.mjs`
|
|
146
|
+
- Removed temp file writing
|
|
147
|
+
- Removed `__testdriverRegistry` initialization
|
|
148
|
+
- Removed `__testdriverMeta` initialization
|
|
149
|
+
- Uses `globalThis.__testdriverPlugin.registerDashcamUrl()` instead
|
|
150
|
+
|
|
151
|
+
### Documentation
|
|
152
|
+
|
|
153
|
+
- ✅ **Updated**: `docs/QUICK_START_TEST_RECORDING.md`
|
|
154
|
+
- ✅ **Updated**: `docs/TEST_RECORDING.md`
|
|
155
|
+
- ✅ **Updated**: `docs/ARCHITECTURE.md`
|
|
156
|
+
|
|
157
|
+
## Code Removed
|
|
158
|
+
|
|
159
|
+
### From Plugin (vs old reporter)
|
|
160
|
+
|
|
161
|
+
- ❌ ~200 lines of file system operations
|
|
162
|
+
- ❌ `indexDashcamFiles()` - complex temp file reading
|
|
163
|
+
- ❌ `findReplayForTest()` - complex sessionId matching
|
|
164
|
+
- ❌ `findReplayForTestInline()` - complex fallback logic
|
|
165
|
+
- ❌ Imports: `fs`, `os` (no longer needed!)
|
|
166
|
+
|
|
167
|
+
### From Test Helpers
|
|
168
|
+
|
|
169
|
+
- ❌ ~60 lines of temp file writing
|
|
170
|
+
- ❌ Global registry setup
|
|
171
|
+
- ❌ Global meta initialization
|
|
172
|
+
- ❌ Multiple storage mechanisms
|
|
173
|
+
|
|
174
|
+
## Benefits
|
|
175
|
+
|
|
176
|
+
1. **Simpler**: ~300 fewer lines of complex code
|
|
177
|
+
2. **Faster**: No file I/O overhead
|
|
178
|
+
3. **More Reliable**: No file system race conditions
|
|
179
|
+
4. **Better DX**: Single API call to register dashcam URLs
|
|
180
|
+
5. **Easier to Debug**: All state in one place
|
|
181
|
+
6. **Thread-Safe**: Map-based storage handles parallel tests
|
|
182
|
+
7. **Cleaner**: No temp file cleanup needed
|
|
183
|
+
|
|
184
|
+
## Testing
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Verify plugin loads
|
|
188
|
+
node -e "import('./interfaces/vitest-plugin.mjs').then(m => console.log('Exports:', Object.keys(m)))"
|
|
189
|
+
|
|
190
|
+
# Run tests
|
|
191
|
+
TD_API_KEY=xxx npx vitest run
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Migration for Users
|
|
195
|
+
|
|
196
|
+
If you're using the TestDriver SDK in your tests:
|
|
197
|
+
|
|
198
|
+
**Old way (still works but deprecated):**
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// Multiple places to set dashcam URL
|
|
202
|
+
globalThis.__testdriverMeta.__lastDashcamUrl__ = dashcamUrl;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**New way:**
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
// Single plugin API
|
|
209
|
+
if (globalThis.__testdriverPlugin) {
|
|
210
|
+
globalThis.__testdriverPlugin.registerDashcamUrl(
|
|
211
|
+
testId,
|
|
212
|
+
dashcamUrl,
|
|
213
|
+
platform,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Notes
|
|
219
|
+
|
|
220
|
+
- The plugin automatically makes itself available globally as `globalThis.__testdriverPlugin`
|
|
221
|
+
- Test helpers now use this API automatically
|
|
222
|
+
- No breaking changes for end users - just better internals!
|
package/PROMPT_CACHE.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Prompt Cache System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The TestDriver SDK includes automatic caching of `.ai()` responses. When you use the SDK to execute natural language prompts, the YAML commands generated by the AI are cached locally to improve performance and reduce API calls.
|
|
6
|
+
|
|
7
|
+
**Caching is enabled by default** and can be controlled per-call using the `cache` parameter.
|
|
8
|
+
|
|
9
|
+
## How It Works
|
|
10
|
+
|
|
11
|
+
1. **First Call**: When you call `.ai('click the submit button')` for the first time:
|
|
12
|
+
|
|
13
|
+
- The SDK sends the prompt and screenshot to the API
|
|
14
|
+
- The API returns YAML commands in markdown format
|
|
15
|
+
- The response is cached to `.testdriver/.cache/{prompt-hash}.yaml`
|
|
16
|
+
|
|
17
|
+
2. **Subsequent Calls**: When you call `.ai('click the submit button')` again:
|
|
18
|
+
|
|
19
|
+
- The SDK checks the cache first
|
|
20
|
+
- If found, it uses the cached YAML instead of calling the API
|
|
21
|
+
- You'll see `(using cached response)` in the output
|
|
22
|
+
|
|
23
|
+
3. **Disable Caching**: Pass `false` as the second parameter:
|
|
24
|
+
- `.ai('click the submit button', false)` bypasses the cache
|
|
25
|
+
- Forces a fresh API call and updates the cache
|
|
26
|
+
|
|
27
|
+
## Cache Location
|
|
28
|
+
|
|
29
|
+
Cached responses are stored in:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
.testdriver/.cache/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Files are named using a combination of:
|
|
36
|
+
|
|
37
|
+
- Sanitized prompt (first 50 chars, alphanumeric only)
|
|
38
|
+
- MD5 hash of the full prompt for uniqueness
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
.testdriver/.cache/click-the-submit-button-a1b2c3d4e5f6.yaml
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Benefits
|
|
47
|
+
|
|
48
|
+
- **Faster execution**: No API call needed for repeated prompts
|
|
49
|
+
- **Cost savings**: Reduces API usage for repeated operations
|
|
50
|
+
- **Offline testing**: Can rerun tests without network access
|
|
51
|
+
- **Deterministic tests**: Same prompt always produces same commands
|
|
52
|
+
|
|
53
|
+
## Disabling Cache
|
|
54
|
+
|
|
55
|
+
To disable caching for a specific `.ai()` call, pass `false` as the second parameter:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Force fresh API call, bypass cache
|
|
59
|
+
await client.ai("click the submit button", false);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Caching is **enabled by default** (equivalent to passing `true`):
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// These are equivalent
|
|
66
|
+
await client.ai("click the submit button");
|
|
67
|
+
await client.ai("click the submit button", true);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Clearing Cache
|
|
71
|
+
|
|
72
|
+
To clear all cached prompts:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
rm -rf .testdriver/.cache/*.yaml
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or programmatically:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
const promptCache = require("testdriverai/agent/lib/cache.js");
|
|
82
|
+
promptCache.clearCache();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage Examples
|
|
86
|
+
|
|
87
|
+
### Basic Usage (Automatic Caching)
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
const TestDriver = require("testdriverai");
|
|
91
|
+
|
|
92
|
+
const client = new TestDriver(process.env.TD_API_KEY);
|
|
93
|
+
await client.connect();
|
|
94
|
+
|
|
95
|
+
// First call - makes API request and caches
|
|
96
|
+
await client.ai("click the login button");
|
|
97
|
+
|
|
98
|
+
// Second call - uses cache (instant)
|
|
99
|
+
await client.ai("click the login button");
|
|
100
|
+
|
|
101
|
+
// Force fresh API call, bypass cache
|
|
102
|
+
await client.ai("click the login button", false);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Check Cache Status
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const promptCache = require("testdriverai/agent/lib/cache.js");
|
|
109
|
+
|
|
110
|
+
// Check if a prompt is cached
|
|
111
|
+
if (promptCache.hasCache("click the login button")) {
|
|
112
|
+
console.log("This prompt is cached");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Get cache statistics
|
|
116
|
+
const stats = promptCache.getCacheStats();
|
|
117
|
+
console.log(`Cached prompts: ${stats.count}`);
|
|
118
|
+
console.log(`Files: ${stats.files}`);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Read Cached YAML
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const promptCache = require("testdriverai/agent/lib/cache.js");
|
|
125
|
+
|
|
126
|
+
const yaml = promptCache.readCache("click the login button");
|
|
127
|
+
if (yaml) {
|
|
128
|
+
console.log("Cached YAML:", yaml);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Manual Cache Control
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const promptCache = require("testdriverai/agent/lib/cache.js");
|
|
136
|
+
|
|
137
|
+
// Write to cache manually
|
|
138
|
+
promptCache.writeCache("custom prompt", yamlContent);
|
|
139
|
+
|
|
140
|
+
// Clear entire cache
|
|
141
|
+
promptCache.clearCache();
|
|
142
|
+
|
|
143
|
+
// Get cache path for a prompt
|
|
144
|
+
const path = promptCache.getCachePath("my prompt");
|
|
145
|
+
console.log(`Cache file: ${path}`);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Important Notes
|
|
149
|
+
|
|
150
|
+
1. **Prompt Matching**: Cache keys are based on the exact prompt text (case-insensitive, trimmed)
|
|
151
|
+
|
|
152
|
+
- `'Click the button'` and `'click the button'` will match
|
|
153
|
+
- `'Click the button'` and `'Click the button '` will match (trailing space trimmed)
|
|
154
|
+
- `'Click button'` and `'Click the button'` will NOT match
|
|
155
|
+
|
|
156
|
+
2. **No Screenshot Comparison**: The cache does NOT compare screenshots
|
|
157
|
+
|
|
158
|
+
- Cached responses assume the UI state is similar
|
|
159
|
+
- Use different prompts if the UI state has changed significantly
|
|
160
|
+
|
|
161
|
+
3. **Manual Cache Management**: The cache never expires automatically
|
|
162
|
+
|
|
163
|
+
- Clear it periodically if needed
|
|
164
|
+
- Delete specific files if a prompt's behavior should change
|
|
165
|
+
|
|
166
|
+
4. **Version Compatibility**: Cache files are plain YAML
|
|
167
|
+
- Compatible across TestDriver versions
|
|
168
|
+
- Safe to commit to version control (though not recommended)
|
|
169
|
+
|
|
170
|
+
## Testing
|
|
171
|
+
|
|
172
|
+
Run the test suite to verify caching:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
node test-prompt-cache.js
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
This will:
|
|
179
|
+
|
|
180
|
+
- Make an initial `.ai()` call that caches the response
|
|
181
|
+
- Make a second `.ai()` call that uses the cache
|
|
182
|
+
- Show cache statistics and file locations
|
|
183
|
+
|
|
184
|
+
## Troubleshooting
|
|
185
|
+
|
|
186
|
+
### Cache not being used
|
|
187
|
+
|
|
188
|
+
- Check that prompts match exactly (case-insensitive)
|
|
189
|
+
- Verify `TD_NO_PROMPT_CACHE` is not set
|
|
190
|
+
- Check `.testdriver/.cache/` directory exists and is writable
|
|
191
|
+
|
|
192
|
+
### Stale cache data
|
|
193
|
+
|
|
194
|
+
- Clear the cache directory
|
|
195
|
+
- Or delete specific cached prompt files
|
|
196
|
+
|
|
197
|
+
### Permission errors
|
|
198
|
+
|
|
199
|
+
- Ensure `.testdriver/.cache/` is writable
|
|
200
|
+
- Check file system permissions
|
package/SDK_LOGGING.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# TestDriver SDK - Formatted Logging for Dashcam
|
|
2
|
+
|
|
3
|
+
The TestDriver SDK now includes clean, structured logging that makes logs easy to read when replayed in Dashcam.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✨ **Clean, structured formatting** with clear labels and timestamps
|
|
8
|
+
⏱️ **Timestamp tracking** showing elapsed time from test start
|
|
9
|
+
🎯 **Action-specific formatting** for find, click, type, hover, scroll, assert
|
|
10
|
+
⚡ **Cache indicators** showing when elements are found from cache
|
|
11
|
+
📊 **Test context integration** with Vitest test information
|
|
12
|
+
🎥 **Dashcam-optimized** - no emojis or ANSI codes, pure text format
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
All logs sent through the `log:log` event are automatically formatted before being sent to Dashcam. This means when you replay your test in Dashcam, you'll see beautiful, easy-to-read logs with:
|
|
17
|
+
|
|
18
|
+
- **Clear prefixes** like `[FIND]`, `[CLICK]`, `[ASSERT]` for different action types
|
|
19
|
+
- **Highlighted information** for element descriptions and coordinates
|
|
20
|
+
- **Elapsed timestamps** from test start like `[30.59s]`
|
|
21
|
+
- **Cache hit indicators** showing performance optimizations with `(cached)`
|
|
22
|
+
- **Duration information** for operations
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Setup
|
|
27
|
+
|
|
28
|
+
The formatter is automatically integrated into the SDK. Just use the SDK normally:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import { afterAll, beforeAll, describe, it } from "vitest";
|
|
32
|
+
import {
|
|
33
|
+
createTestClient,
|
|
34
|
+
setupTest,
|
|
35
|
+
teardownTest,
|
|
36
|
+
} from "./setup/testHelpers.mjs";
|
|
37
|
+
|
|
38
|
+
describe("My Test", () => {
|
|
39
|
+
let testdriver;
|
|
40
|
+
|
|
41
|
+
beforeAll(async () => {
|
|
42
|
+
testdriver = createTestClient();
|
|
43
|
+
await setupTest(testdriver);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterAll(async () => {
|
|
47
|
+
await teardownTest(testdriver);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should have nice logs", async () => {
|
|
51
|
+
// Logs are automatically formatted!
|
|
52
|
+
const button = await testdriver.find("Submit button");
|
|
53
|
+
await button.click();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Enhanced Logging with Test Context
|
|
59
|
+
|
|
60
|
+
For even better logging with timestamps, set the test context:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
beforeAll(async () => {
|
|
64
|
+
testdriver = createTestClient();
|
|
65
|
+
|
|
66
|
+
// Set test context for enhanced logging
|
|
67
|
+
testdriver.setTestContext({
|
|
68
|
+
file: "my-test.spec.mjs",
|
|
69
|
+
test: "My Test Suite",
|
|
70
|
+
startTime: Date.now(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await setupTest(testdriver);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This enables elapsed time display like `[30.59s]` in your logs.
|
|
78
|
+
|
|
79
|
+
## Log Format Examples
|
|
80
|
+
|
|
81
|
+
### Element Found
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
[30.59s] [FIND] Found "Submit button" at (682, 478) 1597ms (cached)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Click Action
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
[35.43s] [CLICK] Click "Submit button"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Hover Action
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
[12.15s] [HOVER] Hover "Menu item"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Type Action
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
[8.32s] [TYPE] Type "user@example.com"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Assertion
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
[42.10s] [ASSERT] "form submission successful" PASSED
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Error
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
[15.23s] [FAIL] Failed to save debug image - Error: ENOENT: no such file or directory
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Formatter API
|
|
118
|
+
|
|
119
|
+
The formatter is available through the `sdk-log-formatter.js` module:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
const { formatter } = require("./sdk-log-formatter");
|
|
123
|
+
|
|
124
|
+
// Format different types of messages
|
|
125
|
+
formatter.formatElementFound("Button", {
|
|
126
|
+
x: 100,
|
|
127
|
+
y: 200,
|
|
128
|
+
duration: "1500ms",
|
|
129
|
+
cacheHit: true,
|
|
130
|
+
});
|
|
131
|
+
formatter.formatAction("click", "Submit button");
|
|
132
|
+
formatter.formatAssertion("form is visible", true);
|
|
133
|
+
formatter.formatError("Connection failed", error);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Available Methods
|
|
137
|
+
|
|
138
|
+
- `formatElementFound(description, meta)` - Format element discovery
|
|
139
|
+
- `formatAction(action, description, meta)` - Format user actions
|
|
140
|
+
- `formatAssertion(assertion, passed, meta)` - Format test assertions
|
|
141
|
+
- `formatError(message, error)` - Format error messages
|
|
142
|
+
- `formatHeader(title)` - Create section headers
|
|
143
|
+
- `formatSummary(stats)` - Format test summaries
|
|
144
|
+
- `setTestContext(context)` - Update test context for timing
|
|
145
|
+
|
|
146
|
+
## Dashcam Compatibility
|
|
147
|
+
|
|
148
|
+
The formatter is designed specifically for Dashcam replay compatibility:
|
|
149
|
+
|
|
150
|
+
- **No emojis** - Uses text labels like `[FIND]`, `[CLICK]` instead of icons
|
|
151
|
+
- **No ANSI colors** - Plain text formatting that displays correctly in all environments
|
|
152
|
+
- **No special characters** - Simple ASCII characters only
|
|
153
|
+
- **Clean structure** - Easy to read in logs without terminal formatting
|
|
154
|
+
|
|
155
|
+
## Integration with Dashcam
|
|
156
|
+
|
|
157
|
+
When you run tests with Dashcam recording:
|
|
158
|
+
|
|
159
|
+
1. The SDK sends formatted logs to the `log:log` event
|
|
160
|
+
2. These logs are forwarded to the sandbox via `_forwardLogToSandbox()`
|
|
161
|
+
3. Dashcam captures and stores these logs with precise timestamps
|
|
162
|
+
4. When you replay in Dashcam, you see beautifully formatted logs synchronized with the video
|
|
163
|
+
|
|
164
|
+
### Example Workflow
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Run tests with Dashcam
|
|
168
|
+
npx vitest run testdriver/acceptance-sdk/formatted-logging.test.mjs
|
|
169
|
+
|
|
170
|
+
# View the replay with formatted logs
|
|
171
|
+
# Open the Dashcam URL from the test output
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Customization
|
|
175
|
+
|
|
176
|
+
### Custom Action Types
|
|
177
|
+
|
|
178
|
+
Add custom action types to the formatter:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
// In sdk-log-formatter.js, add to getPrefix():
|
|
182
|
+
const prefixes = {
|
|
183
|
+
// ...existing prefixes
|
|
184
|
+
myAction: "[CUSTOM]",
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Custom Message Formatting
|
|
189
|
+
|
|
190
|
+
Override the `formatMessage` method for custom text highlighting:
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
formatMessage(type, message) {
|
|
194
|
+
// Add custom highlighting
|
|
195
|
+
message = message.replace(/\[custom\]/g, chalk.green('[custom]'));
|
|
196
|
+
return super.formatMessage(type, message);
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Examples
|
|
201
|
+
|
|
202
|
+
See `testdriver/acceptance-sdk/formatted-logging.test.mjs` for a complete example.
|
|
203
|
+
|
|
204
|
+
## Benefits for Dashcam Replay
|
|
205
|
+
|
|
206
|
+
- **Better debugging**: Quickly identify what happened at each step
|
|
207
|
+
- **Professional appearance**: Share polished test recordings with stakeholders
|
|
208
|
+
- **Faster analysis**: Labeled actions make it easy to scan logs
|
|
209
|
+
- **Context awareness**: Timestamps help correlate logs with video timeline
|
|
210
|
+
- **Performance insights**: Cache indicators show optimization opportunities
|
|
211
|
+
- **Universal compatibility**: Works in any environment without terminal support
|
|
212
|
+
|
|
213
|
+
## Technical Details
|
|
214
|
+
|
|
215
|
+
The formatter uses:
|
|
216
|
+
|
|
217
|
+
- **Plain text formatting** for universal compatibility
|
|
218
|
+
- **Event emitters** to intercept log events
|
|
219
|
+
- **Base64 encoding** for safe transmission to sandbox
|
|
220
|
+
- **Test context** from Vitest for timing information
|
|
221
|
+
|
|
222
|
+
Logs are sent through the existing `log:log` event system, ensuring compatibility with all existing TestDriver infrastructure.
|