testdriverai 7.3.7 → 7.3.9

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [7.3.9](https://github.com/testdriverai/testdriverai/compare/v7.3.8...v7.3.9) (2026-02-12)
2
+
3
+
4
+
5
+ ## [7.3.8](https://github.com/testdriverai/testdriverai/compare/v7.3.7...v7.3.8) (2026-02-12)
6
+
7
+
8
+
1
9
  ## [7.3.7](https://github.com/testdriverai/testdriverai/compare/v7.3.6...v7.3.7) (2026-02-11)
2
10
 
3
11
 
@@ -255,12 +255,17 @@ const createCommands = (
255
255
  let cacheHit = false;
256
256
  let similarity = null;
257
257
 
258
+ let confidence = null;
259
+ let reasoning = null;
260
+
258
261
  if (typeof response.data === 'object' && response.data !== null) {
259
262
  // New structured response
260
263
  passed = response.data.passed;
261
264
  responseText = response.data.content || response.data.reasoning || '';
262
265
  cacheHit = response.cacheHit || response.data.cacheHit || false;
263
266
  similarity = response.similarity || response.data.cacheSimilarity;
267
+ confidence = response.confidence != null ? response.confidence : (response.data.confidence != null ? response.data.confidence : null);
268
+ reasoning = response.data.reasoning || null;
264
269
  } else {
265
270
  // Old string response (backward compatibility)
266
271
  responseText = response.data || '';
@@ -283,6 +288,10 @@ const createCommands = (
283
288
  success: passed,
284
289
  error: passed ? undefined : responseText,
285
290
  cacheHit: cacheHit,
291
+ confidence: confidence,
292
+ reasoning: reasoning,
293
+ similarity: similarity,
294
+ screenshotUrl: response?.screenshotKey ?? null,
286
295
  }).catch((err) => {
287
296
  console.warn("Failed to track assert interaction:", err.message);
288
297
  });
@@ -560,6 +569,10 @@ const createCommands = (
560
569
  cacheHit: elementData.cacheHit,
561
570
  selector: elementData.selector,
562
571
  selectorUsed: elementData.selectorUsed,
572
+ confidence: elementData.confidence ?? null,
573
+ reasoning: elementData.reasoning ?? null,
574
+ similarity: elementData.similarity ?? null,
575
+ screenshotUrl: elementData.screenshotUrl ?? null,
563
576
  }).catch((err) => {
564
577
  console.warn("Failed to track click interaction:", err.message);
565
578
  });
@@ -610,6 +623,9 @@ const createCommands = (
610
623
  cacheHit: elementData.cacheHit,
611
624
  selector: elementData.selector,
612
625
  selectorUsed: elementData.selectorUsed,
626
+ confidence: elementData.confidence ?? null,
627
+ reasoning: elementData.reasoning ?? null,
628
+ similarity: elementData.similarity ?? null,
613
629
  }).catch((err) => {
614
630
  console.warn("Failed to track click interaction:", err.message);
615
631
  });
@@ -679,6 +695,10 @@ const createCommands = (
679
695
  cacheHit: elementData.cacheHit,
680
696
  selector: elementData.selector,
681
697
  selectorUsed: elementData.selectorUsed,
698
+ confidence: elementData.confidence ?? null,
699
+ reasoning: elementData.reasoning ?? null,
700
+ similarity: elementData.similarity ?? null,
701
+ screenshotUrl: elementData.screenshotUrl ?? null,
682
702
  }).catch((err) => {
683
703
  console.warn("Failed to track hover interaction:", err.message);
684
704
  });
@@ -717,6 +737,10 @@ const createCommands = (
717
737
  cacheHit: elementData.cacheHit,
718
738
  selector: elementData.selector,
719
739
  selectorUsed: elementData.selectorUsed,
740
+ confidence: elementData.confidence ?? null,
741
+ reasoning: elementData.reasoning ?? null,
742
+ similarity: elementData.similarity ?? null,
743
+ screenshotUrl: elementData.screenshotUrl ?? null,
720
744
  }).catch((err) => {
721
745
  console.warn("Failed to track hover interaction:", err.message);
722
746
  });
@@ -2,6 +2,7 @@ const WebSocket = require("ws");
2
2
  const crypto = require("crypto");
3
3
  const { events } = require("../events");
4
4
  const logger = require("./logger");
5
+ const { version } = require("../../package.json");
5
6
 
6
7
  /**
7
8
  * Generate Sentry trace headers for distributed tracing
@@ -135,6 +136,7 @@ const createSandbox = (emitter, analytics, sessionInstance) => {
135
136
  let reply = await this.send({
136
137
  type: "authenticate",
137
138
  apiKey,
139
+ version,
138
140
  });
139
141
 
140
142
  if (reply.success) {
@@ -2,136 +2,136 @@
2
2
  "$schema": "./examples-manifest.schema.json",
3
3
  "examples": {
4
4
  "assert.test.mjs": {
5
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5fd7140c3fa7daacf27",
6
- "lastUpdated": "2026-02-11T01:13:44.455Z"
5
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16d4a2aa03cd95d96b60",
6
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
7
7
  },
8
8
  "drag-and-drop.test.mjs": {
9
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4a77140c3fa7daacde3",
10
- "lastUpdated": "2026-02-11T01:13:44.450Z"
9
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1528a2aa03cd95d96a2c",
10
+ "lastUpdated": "2026-02-12T00:06:25.196Z"
11
11
  },
12
12
  "exec-pwsh.test.mjs": {
13
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4a97140c3fa7daacdea",
14
- "lastUpdated": "2026-02-11T01:13:44.450Z"
13
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d152aa2aa03cd95d96a33",
14
+ "lastUpdated": "2026-02-12T00:06:25.197Z"
15
15
  },
16
16
  "match-image.test.mjs": {
17
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4aa7140c3fa7daacdeb",
18
- "lastUpdated": "2026-02-11T01:13:44.450Z"
17
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d152ba2aa03cd95d96a34",
18
+ "lastUpdated": "2026-02-12T00:06:25.197Z"
19
19
  },
20
20
  "scroll-until-text.test.mjs": {
21
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f37140c3fa7daace38",
22
- "lastUpdated": "2026-02-11T01:13:44.451Z"
21
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1590a2aa03cd95d96a82",
22
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
23
23
  },
24
24
  "hover-text-with-description.test.mjs": {
25
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f17140c3fa7daace31",
26
- "lastUpdated": "2026-02-11T01:13:44.451Z"
25
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d157fa2aa03cd95d96a73",
26
+ "lastUpdated": "2026-02-12T00:06:25.197Z"
27
27
  },
28
28
  "windows-installer.test.mjs": {
29
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f47140c3fa7daace3b",
30
- "lastUpdated": "2026-02-11T01:13:44.451Z"
29
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1591a2aa03cd95d96a84",
30
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
31
31
  },
32
32
  "exec-output.test.mjs": {
33
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd4f67140c3fa7daace3d",
34
- "lastUpdated": "2026-02-11T01:13:44.451Z"
33
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1592a2aa03cd95d96a85",
34
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
35
35
  },
36
36
  "chrome-extension.test.mjs": {
37
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5507140c3fa7daacea9",
38
- "lastUpdated": "2026-02-11T01:13:44.452Z"
37
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d15eea2aa03cd95d96ac8",
38
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
39
39
  },
40
40
  "launch-vscode-linux.test.mjs": {
41
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd54e7140c3fa7daacea2",
42
- "lastUpdated": "2026-02-11T01:13:44.452Z"
41
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d15eda2aa03cd95d96ac7",
42
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
43
43
  },
44
44
  "hover-image.test.mjs": {
45
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5387140c3fa7daace8e",
46
- "lastUpdated": "2026-02-11T01:13:44.452Z"
45
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d15dfa2aa03cd95d96abd",
46
+ "lastUpdated": "2026-02-12T00:06:25.198Z"
47
47
  },
48
48
  "installer.test.mjs": {
49
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd58f7140c3fa7daaced9",
50
- "lastUpdated": "2026-02-11T01:13:44.454Z"
49
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1640a2aa03cd95d96af3",
50
+ "lastUpdated": "2026-02-12T00:06:25.200Z"
51
51
  },
52
52
  "type.test.mjs": {
53
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ae7140c3fa7daaceea",
54
- "lastUpdated": "2026-02-11T01:13:44.454Z"
53
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1665a2aa03cd95d96b08",
54
+ "lastUpdated": "2026-02-12T00:06:25.200Z"
55
55
  },
56
56
  "press-keys.test.mjs": {
57
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5e87140c3fa7daacf12",
58
- "lastUpdated": "2026-02-11T01:13:44.455Z"
57
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16aca2aa03cd95d96b40",
58
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
59
59
  },
60
60
  "scroll-keyboard.test.mjs": {
61
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5d47140c3fa7daacf06",
62
- "lastUpdated": "2026-02-11T01:13:44.454Z"
61
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d169fa2aa03cd95d96b35",
62
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
63
63
  },
64
64
  "scroll.test.mjs": {
65
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd58f7140c3fa7daaced8",
66
- "lastUpdated": "2026-02-11T01:13:44.454Z"
65
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d164ca2aa03cd95d96afc",
66
+ "lastUpdated": "2026-02-12T00:06:25.200Z"
67
67
  },
68
68
  "scroll-until-image.test.mjs": {
69
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ea7140c3fa7daacf13",
70
- "lastUpdated": "2026-02-11T01:13:44.455Z"
69
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16ada2aa03cd95d96b42",
70
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
71
71
  },
72
72
  "prompt.test.mjs": {
73
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5eb7140c3fa7daacf14",
74
- "lastUpdated": "2026-02-11T01:13:44.455Z"
73
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16afa2aa03cd95d96b43",
74
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
75
75
  },
76
76
  "focus-window.test.mjs": {
77
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5ec7140c3fa7daacf1b",
78
- "lastUpdated": "2026-02-11T01:13:44.455Z"
77
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16b0a2aa03cd95d96b44",
78
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
79
79
  },
80
80
  "captcha-api.test.mjs": {
81
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd5fc7140c3fa7daacf26",
82
- "lastUpdated": "2026-02-11T01:13:44.455Z"
81
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16cfa2aa03cd95d96b57",
82
+ "lastUpdated": "2026-02-12T00:06:25.201Z"
83
83
  },
84
84
  "element-not-found.test.mjs": {
85
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6297140c3fa7daacf3f",
86
- "lastUpdated": "2026-02-11T01:13:44.455Z"
85
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1705a2aa03cd95d96b79",
86
+ "lastUpdated": "2026-02-12T00:06:25.202Z"
87
87
  },
88
88
  "formatted-logging.test.mjs": {
89
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6187140c3fa7daacf35",
90
- "lastUpdated": "2026-02-11T01:13:44.455Z"
89
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d16eaa2aa03cd95d96b6b",
90
+ "lastUpdated": "2026-02-12T00:06:25.202Z"
91
91
  },
92
92
  "hover-text.test.mjs": {
93
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6477140c3fa7daacf5a",
94
- "lastUpdated": "2026-02-11T01:13:44.457Z"
93
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1724a2aa03cd95d96b8d",
94
+ "lastUpdated": "2026-02-12T00:06:25.203Z"
95
95
  },
96
96
  "no-provision.test.mjs": {
97
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd64a7140c3fa7daacf63",
98
- "lastUpdated": "2026-02-11T01:13:44.457Z"
97
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d173ea2aa03cd95d96b9d",
98
+ "lastUpdated": "2026-02-12T00:06:25.203Z"
99
99
  },
100
100
  "ai.test.mjs": {
101
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6477140c3fa7daacf59",
102
- "lastUpdated": "2026-02-11T01:13:44.456Z"
101
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1763a2aa03cd95d96bae",
102
+ "lastUpdated": "2026-02-12T00:06:25.203Z"
103
103
  },
104
104
  "popup-loading.test.mjs": {
105
105
  "url": "https://console.testdriver.ai/runs/698bc89f7140c3fa7daaca8d/698bca7f7140c3fa7daacbf7",
106
106
  "lastUpdated": "2026-02-11T00:20:33.687Z"
107
107
  },
108
108
  "z_flake-diffthreshold-001.test.mjs": {
109
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6d47140c3fa7daacfbc",
110
- "lastUpdated": "2026-02-11T01:13:44.457Z"
109
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d186aa2aa03cd95d96c33",
110
+ "lastUpdated": "2026-02-12T00:06:25.204Z"
111
111
  },
112
112
  "z_flake-diffthreshold-01.test.mjs": {
113
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd6d17140c3fa7daacfb5",
114
- "lastUpdated": "2026-02-11T01:13:44.457Z"
113
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1838a2aa03cd95d96c16",
114
+ "lastUpdated": "2026-02-12T00:06:25.203Z"
115
115
  },
116
116
  "z_flake-diffthreshold-05.test.mjs": {
117
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd71b7140c3fa7daacfe9",
118
- "lastUpdated": "2026-02-11T01:13:44.457Z"
117
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d183ba2aa03cd95d96c1d",
118
+ "lastUpdated": "2026-02-12T00:06:25.203Z"
119
119
  },
120
120
  "z_flake-noredraw-cache.test.mjs": {
121
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd71b7140c3fa7daacfe8",
122
- "lastUpdated": "2026-02-11T01:13:44.457Z"
121
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d18b0a2aa03cd95d96c61",
122
+ "lastUpdated": "2026-02-12T00:06:25.205Z"
123
123
  },
124
124
  "z_flake-redraw-nocache.test.mjs": {
125
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd7be7140c3fa7daad047",
126
- "lastUpdated": "2026-02-11T01:13:44.459Z"
125
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1901a2aa03cd95d96c85",
126
+ "lastUpdated": "2026-02-12T00:06:25.205Z"
127
127
  },
128
128
  "z_flake-redraw-cache.test.mjs": {
129
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd7627140c3fa7daad013",
130
- "lastUpdated": "2026-02-11T01:13:44.458Z"
129
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d1976a2aa03cd95d96c9c",
130
+ "lastUpdated": "2026-02-12T00:06:25.205Z"
131
131
  },
132
132
  "z_flake-noredraw-nocache.test.mjs": {
133
- "url": "https://console.testdriver.ai/runs/698bd4a67140c3fa7daacde2/698bd77d7140c3fa7daad027",
134
- "lastUpdated": "2026-02-11T01:13:44.458Z"
133
+ "url": "https://console.testdriver.ai/runs/698d1527a2aa03cd95d96a2b/698d190ca2aa03cd95d96c8a",
134
+ "lastUpdated": "2026-02-12T00:06:25.205Z"
135
135
  }
136
136
  }
137
137
  }
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* ai.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6477140c3fa7daacf59/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1763a2aa03cd95d96bae/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* assert.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5fd7140c3fa7daacf27/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d16d4a2aa03cd95d96b60/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* captcha-api.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5fc7140c3fa7daacf26/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d16cfa2aa03cd95d96b57/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* chrome-extension.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5507140c3fa7daacea9/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d15eea2aa03cd95d96ac8/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* drag-and-drop.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4a77140c3fa7daacde3/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1528a2aa03cd95d96a2c/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* element-not-found.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6297140c3fa7daacf3f/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1705a2aa03cd95d96b79/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* hover-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5387140c3fa7daace8e/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d15dfa2aa03cd95d96abd/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* hover-text.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd6477140c3fa7daacf5a/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1724a2aa03cd95d96b8d/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* installer.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd58f7140c3fa7daaced9/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1640a2aa03cd95d96af3/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* launch-vscode-linux.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd54e7140c3fa7daacea2/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d15eda2aa03cd95d96ac7/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* match-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4aa7140c3fa7daacdeb/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d152ba2aa03cd95d96a34/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* press-keys.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5e87140c3fa7daacf12/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d16aca2aa03cd95d96b40/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-keyboard.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5d47140c3fa7daacf06/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d169fa2aa03cd95d96b35/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-until-image.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5ea7140c3fa7daacf13/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d16ada2aa03cd95d96b42/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll-until-text.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4f37140c3fa7daace38/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1590a2aa03cd95d96a82/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* scroll.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd58f7140c3fa7daaced8/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d164ca2aa03cd95d96afc/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* type.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd5ae7140c3fa7daaceea/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1665a2aa03cd95d96b08/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
12
12
 
13
13
  {/* windows-installer.test.mjs output */}
14
14
  <iframe
15
- src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698bd4f47140c3fa7daace3b/replay"
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/698d1591a2aa03cd95d96a84/replay"
16
16
  width="100%"
17
17
  height="390"
18
18
  style={{ border: "1px solid #333", borderRadius: "8px" }}
@@ -9,6 +9,7 @@ export function popupLoadingTest(label, options = {}) {
9
9
  describe(`Popup with Loading (${label})`, () => {
10
10
  it("should accept cookies and wait for completion", async (context) => {
11
11
  const testdriver = TestDriver(context, {
12
+ preview: 'ide',
12
13
  ip: context.ip || process.env.TD_IP,
13
14
  ...options,
14
15
  });
@@ -5,11 +5,20 @@
5
5
 
6
6
  import TestDriverSDK, { TestDriverOptions } from '../sdk';
7
7
 
8
+ /**
9
+ * A single dashcam URL entry for one retry attempt
10
+ */
11
+ export interface DashcamUrlEntry {
12
+ url: string | null;
13
+ platform: string;
14
+ attempt: number;
15
+ }
16
+
8
17
  /**
9
18
  * Plugin state object
10
19
  */
11
20
  export interface PluginState {
12
- dashcamUrls: Map<string, { url: string; platform: string }>;
21
+ dashcamUrls: Map<string, DashcamUrlEntry[]>;
13
22
  suiteTestRuns: Map<string, any>;
14
23
  testDriverOptions: TestDriverOptions;
15
24
  }
@@ -20,14 +29,19 @@ export interface PluginState {
20
29
  export const pluginState: PluginState;
21
30
 
22
31
  /**
23
- * Register a Dashcam URL for a test
32
+ * Register a Dashcam URL for a test attempt
33
+ */
34
+ export function registerDashcamUrl(testId: string, url: string, platform: string, attempt?: number): void;
35
+
36
+ /**
37
+ * Get the latest Dashcam URL entry for a test (backward compatible)
24
38
  */
25
- export function registerDashcamUrl(testId: string, url: string, platform: string): void;
39
+ export function getDashcamUrl(testId: string): DashcamUrlEntry | undefined;
26
40
 
27
41
  /**
28
- * Get Dashcam URL for a test
42
+ * Get all Dashcam URL entries for a test (all retry attempts)
29
43
  */
30
- export function getDashcamUrl(testId: string): { url: string; platform: string } | undefined;
44
+ export function getAllDashcamUrls(testId: string): DashcamUrlEntry[];
31
45
 
32
46
  /**
33
47
  * Clear all Dashcam URLs
@@ -9,6 +9,29 @@ import { setTestRunInfo } from "./shared-test-state.mjs";
9
9
  // Use createRequire to import CommonJS modules without esbuild processing
10
10
  const require = createRequire(import.meta.url);
11
11
 
12
+ /**
13
+ * Resolve the TestDriver SDK version using multiple strategies.
14
+ * Similar to resolveVitestVersion(), guards against import.meta.url rewriting.
15
+ * @returns {string|null}
16
+ */
17
+ function resolveTestDriverVersion() {
18
+ try {
19
+ return require("../package.json").version;
20
+ } catch {}
21
+
22
+ try {
23
+ const cwdRequire = createRequire(path.join(process.cwd(), "package.json"));
24
+ return cwdRequire("testdriverai/package.json").version;
25
+ } catch {}
26
+
27
+ try {
28
+ const pkgPath = path.join(process.cwd(), "node_modules", "testdriverai", "package.json");
29
+ return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
30
+ } catch {}
31
+
32
+ return null;
33
+ }
34
+
12
35
  /**
13
36
  * Minimum required Vitest major version
14
37
  */
@@ -170,21 +193,32 @@ export const pluginState = {
170
193
  // TestDriver options to pass to all instances
171
194
  testDriverOptions: {},
172
195
  // Dashcam URL tracking (in-memory, no files needed!)
173
- dashcamUrls: new Map(), // testId -> dashcamUrl
196
+ dashcamUrls: new Map(), // testId -> [{url, platform, attempt}]
174
197
  lastDashcamUrl: null, // Fallback for when test ID isn't available
175
198
  // Suite-level test run tracking
176
199
  suiteTestRuns: new Map(), // suiteId -> { runId, testRunDbId, token }
177
200
  };
178
201
 
179
202
  // Export functions that can be used by the reporter or tests
180
- export function registerDashcamUrl(testId, url, platform) {
181
- logger.debug(`Registering dashcam URL for test ${testId}:`, url);
182
- pluginState.dashcamUrls.set(testId, { url, platform });
203
+ export function registerDashcamUrl(testId, url, platform, attempt) {
204
+ logger.debug(`Registering dashcam URL for test ${testId} (attempt ${attempt || 1}):`, url);
205
+ // Support multiple attempts per test - store as array
206
+ if (!pluginState.dashcamUrls.has(testId)) {
207
+ pluginState.dashcamUrls.set(testId, []);
208
+ }
209
+ pluginState.dashcamUrls.get(testId).push({ url, platform, attempt: attempt || 1 });
183
210
  pluginState.lastDashcamUrl = url;
184
211
  }
185
212
 
186
213
  export function getDashcamUrl(testId) {
187
- return pluginState.dashcamUrls.get(testId);
214
+ const entries = pluginState.dashcamUrls.get(testId);
215
+ if (!entries) return undefined;
216
+ // Return the last entry for backward compatibility (single URL callers)
217
+ return entries[entries.length - 1];
218
+ }
219
+
220
+ export function getAllDashcamUrls(testId) {
221
+ return pluginState.dashcamUrls.get(testId) || [];
188
222
  }
189
223
 
190
224
  export function clearDashcamUrls() {
@@ -743,6 +777,17 @@ class TestDriverReporter {
743
777
  // Default to linux if no tests write platform info
744
778
  testRunData.platform = "linux";
745
779
 
780
+ // Send version metadata
781
+ testRunData.nodeVersion = process.version;
782
+ const tdVer = resolveTestDriverVersion();
783
+ if (tdVer) {
784
+ testRunData.testDriverVersion = tdVer;
785
+ }
786
+ const vitestVer = resolveVitestVersion();
787
+ if (vitestVer) {
788
+ testRunData.vitestVersion = vitestVer;
789
+ }
790
+
746
791
  logger.debug("Creating test run with data:", JSON.stringify(testRunData));
747
792
  pluginState.testRun = await createTestRun(testRunData);
748
793
  logger.debug("Test run created:", JSON.stringify(pluginState.testRun));
@@ -929,6 +974,7 @@ class TestDriverReporter {
929
974
  logger.debug(`Test meta for ${test.id}:`, meta);
930
975
 
931
976
  const dashcamUrl = meta.dashcamUrl || null;
977
+ const dashcamUrls = meta.dashcamUrls || []; // Per-attempt URLs
932
978
  const sessionId = meta.sessionId || null;
933
979
  const platform = meta.platform || null;
934
980
  const sandboxId = meta.sandboxId || null;
@@ -986,8 +1032,12 @@ class TestDriverReporter {
986
1032
 
987
1033
  const suiteName = test.suite?.name;
988
1034
  const startTime = Date.now() - duration; // Calculate start time from duration
1035
+ const retryCount = result.retryCount || 0;
1036
+ const testRunDbId = process.env.TD_TEST_RUN_DB_ID;
1037
+ const consoleUrl = getConsoleUrl(pluginState.apiRoot);
1038
+ const hasRetries = retryCount > 0 && dashcamUrls.length > 1;
989
1039
 
990
- // Record test case with all metadata
1040
+ // Record a single test case with all metadata
991
1041
  const testCaseData = {
992
1042
  runId: testRunId,
993
1043
  testName: test.name,
@@ -997,7 +1047,7 @@ class TestDriverReporter {
997
1047
  startTime: startTime,
998
1048
  endTime: Date.now(),
999
1049
  duration: duration,
1000
- retries: result.retryCount || 0,
1050
+ retries: retryCount,
1001
1051
  };
1002
1052
 
1003
1053
  // Add sessionId if available
@@ -1010,6 +1060,13 @@ class TestDriverReporter {
1010
1060
  testCaseData.replayUrl = dashcamUrl;
1011
1061
  }
1012
1062
 
1063
+ // Include per-attempt replay URLs for retry visibility
1064
+ if (dashcamUrls.length > 0) {
1065
+ const attemptUrls = dashcamUrls
1066
+ .map(a => ({ attempt: a.attempt, url: a.url || null, sessionId: a.sessionId || null }));
1067
+ testCaseData.replayUrls = attemptUrls;
1068
+ }
1069
+
1013
1070
  if (suiteName) testCaseData.suiteName = suiteName;
1014
1071
  if (errorMessage) testCaseData.errorMessage = errorMessage;
1015
1072
  if (errorStack) testCaseData.errorStack = errorStack;
@@ -1025,7 +1082,6 @@ class TestDriverReporter {
1025
1082
  );
1026
1083
 
1027
1084
  const testCaseDbId = testCaseResponse.data?.id;
1028
- const testRunDbId = process.env.TD_TEST_RUN_DB_ID;
1029
1085
 
1030
1086
  // Store test case data for GitHub comment generation
1031
1087
  pluginState.recordedTestCases.push({
@@ -1035,14 +1091,25 @@ class TestDriverReporter {
1035
1091
 
1036
1092
  console.log("");
1037
1093
  console.log(
1038
- `🔗 Test Report: ${getConsoleUrl(pluginState.apiRoot)}/runs/${testRunDbId}/${testCaseDbId}`,
1094
+ `🔗 Test Report: ${consoleUrl}/runs/${testRunDbId}/${testCaseDbId}`,
1039
1095
  );
1096
+
1097
+ // If there were retries, list all per-attempt dashcam URLs for debugging
1098
+ if (hasRetries) {
1099
+ const validAttempts = dashcamUrls.filter(a => a.url);
1100
+ if (validAttempts.length > 0) {
1101
+ console.log(`📋 Retry attempts (${dashcamUrls.length} total):`);
1102
+ for (const attempt of validAttempts) {
1103
+ console.log(` Attempt ${attempt.attempt}: ${attempt.url}`);
1104
+ }
1105
+ }
1106
+ }
1040
1107
 
1041
1108
  // Output parseable format for docs generation (examples only)
1042
1109
  if (testFile.startsWith("examples/")) {
1043
1110
  const testFileName = path.basename(testFile);
1044
1111
  console.log(
1045
- `TESTDRIVER_EXAMPLE_URL::${testFileName}::${getConsoleUrl(pluginState.apiRoot)}/runs/${testRunDbId}/${testCaseDbId}`,
1112
+ `TESTDRIVER_EXAMPLE_URL::${testFileName}::${consoleUrl}/runs/${testRunDbId}/${testCaseDbId}`,
1046
1113
  );
1047
1114
  }
1048
1115
  } catch (error) {
@@ -1099,12 +1166,16 @@ function getPlatform() {
1099
1166
  }
1100
1167
 
1101
1168
  // Try to get platform from dashcam URLs (registered during test cleanup)
1102
- for (const [, data] of pluginState.dashcamUrls) {
1103
- if (data.platform) {
1104
- logger.debug(
1105
- `Using platform from dashcam URL registration: ${data.platform}`,
1106
- );
1107
- return data.platform;
1169
+ for (const [, entries] of pluginState.dashcamUrls) {
1170
+ // entries is now an array of {url, platform, attempt}
1171
+ const arr = Array.isArray(entries) ? entries : [entries];
1172
+ for (const data of arr) {
1173
+ if (data.platform) {
1174
+ logger.debug(
1175
+ `Using platform from dashcam URL registration: ${data.platform}`,
1176
+ );
1177
+ return data.platform;
1178
+ }
1108
1179
  }
1109
1180
  }
1110
1181
 
@@ -415,11 +415,29 @@ export function TestDriver(context, options = {}) {
415
415
  context.task.meta.testOrder = 0;
416
416
  context.task.meta.sessionId = currentInstance.getSessionId?.() || null;
417
417
 
418
+ // Initialize dashcamUrls array for tracking per-attempt URLs (persists across retries)
419
+ if (!context.task.meta.dashcamUrls) {
420
+ context.task.meta.dashcamUrls = [];
421
+ }
422
+
423
+ // Determine the current attempt number (1-based)
424
+ const attemptNumber = context.task.meta.dashcamUrls.length + 1;
425
+ const isRetry = attemptNumber > 1;
426
+ const attemptLabel = isRetry ? ` (attempt ${attemptNumber})` : "";
427
+
418
428
  // Stop dashcam if it was started - with timeout to prevent hanging
419
429
  if (currentInstance._dashcam && currentInstance._dashcam.recording) {
420
430
  try {
421
431
  const dashcamUrl = await currentInstance.dashcam.stop();
422
- // Add dashcam URL to metadata
432
+
433
+ // Track this attempt's URL in the per-attempt array
434
+ context.task.meta.dashcamUrls.push({
435
+ attempt: attemptNumber,
436
+ url: dashcamUrl || null,
437
+ sessionId: currentInstance.getSessionId?.() || null,
438
+ });
439
+
440
+ // Keep backward compatibility - last attempt's URL
423
441
  context.task.meta.dashcamUrl = dashcamUrl || null;
424
442
 
425
443
  // Also register in memory if plugin is available (for cross-process scenarios)
@@ -428,16 +446,15 @@ export function TestDriver(context, options = {}) {
428
446
  context.task.id,
429
447
  dashcamUrl,
430
448
  platform,
449
+ attemptNumber,
431
450
  );
432
451
  }
433
452
 
434
- const debugMode =
435
- process.env.VERBOSE || process.env.DEBUG || process.env.TD_DEBUG;
436
-
437
- if (debugMode) {
453
+ // Always print the dashcam URL for each attempt so it's visible in logs
454
+ if (dashcamUrl) {
438
455
  console.log("");
439
456
  console.log(
440
- "🎥" + chalk.yellow(` Dashcam URL`) + `: ${dashcamUrl}`,
457
+ "🎥" + chalk.yellow(` Dashcam URL${attemptLabel}`) + `: ${dashcamUrl}`,
441
458
  );
442
459
  console.log("");
443
460
  }
@@ -461,11 +478,23 @@ export function TestDriver(context, options = {}) {
461
478
  if (currentInstance._dashcam) {
462
479
  currentInstance._dashcam.recording = false;
463
480
  }
481
+ // Track failed attempt
482
+ context.task.meta.dashcamUrls.push({
483
+ attempt: attemptNumber,
484
+ url: null,
485
+ sessionId: currentInstance.getSessionId?.() || null,
486
+ error: error.message,
487
+ });
464
488
  // Ensure dashcamUrl is set to null if stop failed
465
489
  context.task.meta.dashcamUrl = null;
466
490
  }
467
491
  } else {
468
- // No dashcam recording, set URL to null explicitly
492
+ // No dashcam recording - still track the attempt
493
+ context.task.meta.dashcamUrls.push({
494
+ attempt: attemptNumber,
495
+ url: null,
496
+ sessionId: currentInstance.getSessionId?.() || null,
497
+ });
469
498
  context.task.meta.dashcamUrl = null;
470
499
  }
471
500
 
@@ -15,14 +15,15 @@
15
15
  */
16
16
 
17
17
  import {
18
- clearDashcamUrls,
19
- clearSuiteTestRun,
20
- getDashcamUrl,
21
- getPluginState,
22
- getSuiteTestRun,
23
- pluginState,
24
- registerDashcamUrl,
25
- setSuiteTestRun,
18
+ clearDashcamUrls,
19
+ clearSuiteTestRun,
20
+ getAllDashcamUrls,
21
+ getDashcamUrl,
22
+ getPluginState,
23
+ getSuiteTestRun,
24
+ pluginState,
25
+ registerDashcamUrl,
26
+ setSuiteTestRun,
26
27
  } from '../../interfaces/vitest-plugin.mjs';
27
28
 
28
29
  // Set up global TestDriver plugin interface
@@ -31,6 +32,7 @@ globalThis.__testdriverPlugin = {
31
32
  state: pluginState,
32
33
  registerDashcamUrl,
33
34
  getDashcamUrl,
35
+ getAllDashcamUrls,
34
36
  clearDashcamUrls,
35
37
  getPluginState,
36
38
  getSuiteTestRun,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.3.7",
3
+ "version": "7.3.9",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",
package/sdk.js CHANGED
@@ -256,6 +256,7 @@ class ElementNotFoundError extends Error {
256
256
 
257
257
  if (this.aiResponse) {
258
258
  const responseText =
259
+ this.aiResponse.reasoning ||
259
260
  this.aiResponse.response?.content?.[0]?.text ||
260
261
  this.aiResponse.content?.[0]?.text ||
261
262
  "No detailed response available";
@@ -603,6 +604,10 @@ class Element {
603
604
  false,
604
605
  selector: response?.selector,
605
606
  selectorUsed: !!response?.selector,
607
+ confidence: response?.confidence ?? null,
608
+ reasoning: response?.reasoning ?? null,
609
+ similarity: response?.similarity ?? null,
610
+ screenshotUrl: response?.screenshotKey ?? null,
606
611
  })
607
612
  .catch((err) => {
608
613
  console.warn("Failed to track find interaction:", err.message);
@@ -969,6 +974,10 @@ class Element {
969
974
  cacheHit: this._response?.cacheHit,
970
975
  selectorUsed: !!this._response?.selector,
971
976
  selector: this._response?.selector,
977
+ confidence: this._response?.confidence ?? null,
978
+ reasoning: this._response?.reasoning ?? null,
979
+ similarity: this._response?.similarity ?? null,
980
+ screenshotUrl: this._response?.screenshotKey ?? null,
972
981
  };
973
982
 
974
983
  if (action === "hover") {
@@ -1018,6 +1027,7 @@ class Element {
1018
1027
  cacheHit: this._response?.cacheHit,
1019
1028
  selectorUsed: !!this._response?.selector,
1020
1029
  selector: this._response?.selector,
1030
+ screenshotUrl: this._response?.screenshotKey ?? null,
1021
1031
  };
1022
1032
 
1023
1033
  await this.commands.hover(
@@ -1646,10 +1656,24 @@ class TestDriverSDK {
1646
1656
  guest = false,
1647
1657
  } = options;
1648
1658
 
1649
- // If dashcam is available, add web logs for all websites
1659
+ // If dashcam is enabled, add web logs for all websites
1650
1660
  // Note: File log and dashcam.start() are handled by the connection promise in hooks.mjs
1651
- if (this._dashcam) {
1652
- await this._dashcam.addWebLog("**", "Web Logs");
1661
+ if (this.dashcamEnabled) {
1662
+ // get the domain from the url for more specific logging, e.g. "Web Logs - example.com"
1663
+ let domain = url;
1664
+ let protocol = "https:";
1665
+ try {
1666
+ const urlObj = new URL(url);
1667
+ domain = urlObj.hostname;
1668
+ protocol = urlObj.protocol;
1669
+ } catch (err) {
1670
+ // If URL parsing fails, fall back to using the full URL as the domain
1671
+ }
1672
+
1673
+ // the pattern should be protocol://domain* to match all subpages of the domain
1674
+ const webLogPattern = `${protocol}//${domain}*`;
1675
+
1676
+ await this.dashcam.addWebLog(webLogPattern, "Web Logs");
1653
1677
  }
1654
1678
 
1655
1679
  // Set up Chrome profile with preferences
@@ -1906,10 +1930,10 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
1906
1930
  );
1907
1931
  }
1908
1932
 
1909
- // If dashcam is available, add web logs for all websites
1933
+ // If dashcam is enabled, add web logs for all websites
1910
1934
  // Note: File log and dashcam.start() are handled by the connection promise in hooks.mjs
1911
- if (this._dashcam) {
1912
- await this._dashcam.addWebLog("**", "Web Logs");
1935
+ if (this.dashcamEnabled) {
1936
+ await this.dashcam.addWebLog("**", "Web Logs");
1913
1937
  }
1914
1938
 
1915
1939
  // Set up Chrome profile with preferences
@@ -2032,10 +2056,10 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2032
2056
 
2033
2057
  const shell = this.os === "windows" ? "pwsh" : "sh";
2034
2058
 
2035
- // If dashcam is available, add web logs for all websites
2059
+ // If dashcam is enabled, add web logs for all websites
2036
2060
  // Note: File log and dashcam.start() are handled by the connection promise in hooks.mjs
2037
- if (this._dashcam) {
2038
- await this._dashcam.addWebLog("**", "Web Logs");
2061
+ if (this.dashcamEnabled) {
2062
+ await this.dashcam.addWebLog("**", "Web Logs");
2039
2063
  }
2040
2064
 
2041
2065
  // Install extensions if provided
@@ -2108,10 +2132,10 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2108
2132
 
2109
2133
  const shell = this.os === "windows" ? "pwsh" : "sh";
2110
2134
 
2111
- // If dashcam is available, add web logs for all websites
2135
+ // If dashcam is enabled, add web logs for all websites
2112
2136
  // Note: File log and dashcam.start() are handled by the connection promise in hooks.mjs
2113
- if (this._dashcam) {
2114
- await this._dashcam.addWebLog("**", "Web Logs");
2137
+ if (this.dashcamEnabled) {
2138
+ await this.dashcam.addWebLog("**", "Web Logs");
2115
2139
  }
2116
2140
 
2117
2141
  // Determine download directory
@@ -2248,10 +2272,10 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2248
2272
 
2249
2273
  const shell = this.os === "windows" ? "pwsh" : "sh";
2250
2274
 
2251
- // If dashcam is available, add web logs for all websites
2275
+ // If dashcam is enabled, add web logs for all websites
2252
2276
  // Note: File log and dashcam.start() are handled by the connection promise in hooks.mjs
2253
- if (this._dashcam) {
2254
- await this._dashcam.addWebLog("**", "Web Logs");
2277
+ if (this.dashcamEnabled) {
2278
+ await this.dashcam.addWebLog("**", "Web Logs");
2255
2279
  }
2256
2280
 
2257
2281
  const argsString = args.join(" ");
@@ -2290,17 +2314,17 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2290
2314
  title,
2291
2315
  } = options;
2292
2316
 
2293
- // Ensure dashcam is available
2294
- if (!this._dashcam) {
2317
+ // Ensure dashcam is enabled
2318
+ if (!this.dashcamEnabled) {
2295
2319
  console.warn(
2296
- "[provision.dashcam] Dashcam is not available. Skipping.",
2320
+ "[provision.dashcam] Dashcam is not enabled. Skipping.",
2297
2321
  );
2298
2322
  return;
2299
2323
  }
2300
2324
 
2301
2325
  // Set custom title if provided
2302
2326
  if (title) {
2303
- this._dashcam.setTitle(title);
2327
+ this.dashcam.setTitle(title);
2304
2328
  }
2305
2329
 
2306
2330
  // Add file log tracking
@@ -2310,16 +2334,16 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
2310
2334
  ? "C:\\Users\\testdriver\\testdriver.log"
2311
2335
  : "/tmp/testdriver.log");
2312
2336
 
2313
- await this._dashcam.addFileLog(actualLogPath, logName);
2337
+ await this.dashcam.addFileLog(actualLogPath, logName);
2314
2338
 
2315
2339
  // Add web log tracking if enabled
2316
2340
  if (webLogs) {
2317
- await this._dashcam.addWebLog("**", "Web Logs");
2341
+ await this.dashcam.addWebLog("**", "Web Logs");
2318
2342
  }
2319
2343
 
2320
2344
  // Start recording if not already recording
2321
- if (!(await this._dashcam.isRecording())) {
2322
- await this._dashcam.start();
2345
+ if (!(await this.dashcam.isRecording())) {
2346
+ await this.dashcam.start();
2323
2347
  }
2324
2348
 
2325
2349
  console.log("[provision.dashcam] ✅ Dashcam recording started");
@@ -3038,6 +3062,7 @@ CAPTCHA_SOLVER_EOF`,
3038
3062
  cacheHit: response.cached || false,
3039
3063
  selector: response.selector,
3040
3064
  selectorUsed: !!response.selector,
3065
+ screenshotUrl: response.screenshotKey ?? null,
3041
3066
  })
3042
3067
  .catch((err) => {
3043
3068
  console.warn("Failed to track findAll interaction:", err.message);
@@ -3094,6 +3119,7 @@ CAPTCHA_SOLVER_EOF`,
3094
3119
  cacheHit: response?.cached || false,
3095
3120
  selector: response?.selector,
3096
3121
  selectorUsed: !!response?.selector,
3122
+ screenshotUrl: response?.screenshotKey ?? null,
3097
3123
  })
3098
3124
  .catch((err) => {
3099
3125
  console.warn("Failed to track findAll interaction:", err.message);
@@ -4049,6 +4075,9 @@ CAPTCHA_SOLVER_EOF`,
4049
4075
  }
4050
4076
  }
4051
4077
 
4078
+ // Expose SDK version as a static property for use by vitest hooks/plugins
4079
+ TestDriverSDK.version = require("./package.json").version;
4080
+
4052
4081
  module.exports = TestDriverSDK;
4053
4082
  module.exports.Element = Element;
4054
4083
  module.exports.ElementNotFoundError = ElementNotFoundError;
package/vitest.config.mjs CHANGED
@@ -10,6 +10,7 @@ const setupFiles = [
10
10
 
11
11
  export default defineConfig({
12
12
  test: {
13
+ retry: 1,
13
14
  testTimeout: 900000,
14
15
  hookTimeout: 900000,
15
16
  disableConsoleIntercept: true,