testdriverai 7.2.54 → 7.2.56
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/docs/v7/screenshot.mdx +11 -22
- package/examples/hover-text-with-description.test.mjs +9 -6
- package/interfaces/cli/commands/init.js +48 -21
- package/package.json +1 -1
- package/sdk.d.ts +7 -13
- package/sdk.js +16 -16
package/docs/v7/screenshot.mdx
CHANGED
|
@@ -12,21 +12,13 @@ Capture a screenshot of the current screen and automatically save it to a local
|
|
|
12
12
|
## Syntax
|
|
13
13
|
|
|
14
14
|
```javascript
|
|
15
|
-
const filePath = await testdriver.screenshot(
|
|
15
|
+
const filePath = await testdriver.screenshot(filename)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Parameters
|
|
19
19
|
|
|
20
|
-
<ParamField path="
|
|
21
|
-
|
|
22
|
-
</ParamField>
|
|
23
|
-
|
|
24
|
-
<ParamField path="silent" type="boolean" default="false">
|
|
25
|
-
Whether to suppress the log message showing where the screenshot was saved
|
|
26
|
-
</ParamField>
|
|
27
|
-
|
|
28
|
-
<ParamField path="mouse" type="boolean" default="false">
|
|
29
|
-
Whether to include the mouse cursor in the screenshot
|
|
20
|
+
<ParamField path="filename" type="string" optional>
|
|
21
|
+
Custom filename for the screenshot (without .png extension). If not provided, a timestamp-based filename is generated automatically.
|
|
30
22
|
</ParamField>
|
|
31
23
|
|
|
32
24
|
## Returns
|
|
@@ -42,7 +34,7 @@ Screenshots are automatically saved to `.testdriver/screenshots/<test-file-name>
|
|
|
42
34
|
screenshots/
|
|
43
35
|
login.test/
|
|
44
36
|
screenshot-1737633600000.png
|
|
45
|
-
|
|
37
|
+
login-page.png
|
|
46
38
|
checkout.test/
|
|
47
39
|
screenshot-1737633700000.png
|
|
48
40
|
```
|
|
@@ -56,23 +48,20 @@ Screenshots are automatically saved to `.testdriver/screenshots/<test-file-name>
|
|
|
56
48
|
### Basic Screenshot
|
|
57
49
|
|
|
58
50
|
```javascript
|
|
59
|
-
// Capture a screenshot with
|
|
51
|
+
// Capture a screenshot with auto-generated filename
|
|
60
52
|
const screenshotPath = await testdriver.screenshot();
|
|
61
53
|
console.log('Screenshot saved to:', screenshotPath);
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
###
|
|
56
|
+
### Custom Filename
|
|
65
57
|
|
|
66
58
|
```javascript
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
### Silent Screenshot
|
|
59
|
+
// Save with a descriptive filename
|
|
60
|
+
await testdriver.screenshot("login-page");
|
|
61
|
+
// Saves to: .testdriver/screenshots/<test>/login-page.png
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
await testdriver.screenshot(1, true);
|
|
63
|
+
await testdriver.screenshot("after-click");
|
|
64
|
+
// Saves to: .testdriver/screenshots/<test>/after-click.png
|
|
76
65
|
```
|
|
77
66
|
|
|
78
67
|
### Debugging with Screenshots
|
|
@@ -14,9 +14,7 @@ import { TestDriver } from "../lib/vitest/hooks.mjs";
|
|
|
14
14
|
async function performLogin(client, username = "standard_user") {
|
|
15
15
|
await client.focusApplication("Google Chrome");
|
|
16
16
|
const password = await client.extract("the password");
|
|
17
|
-
const usernameField = await client.find(
|
|
18
|
-
"username input",
|
|
19
|
-
);
|
|
17
|
+
const usernameField = await client.find("username input");
|
|
20
18
|
await usernameField.click();
|
|
21
19
|
await client.type(username);
|
|
22
20
|
await client.pressKeys(["tab"]);
|
|
@@ -27,8 +25,13 @@ async function performLogin(client, username = "standard_user") {
|
|
|
27
25
|
|
|
28
26
|
describe("Hover Text With Description Test", () => {
|
|
29
27
|
it("should add TestDriver Hat to cart and verify", async (context) => {
|
|
30
|
-
const testdriver = TestDriver(context, {
|
|
31
|
-
|
|
28
|
+
const testdriver = TestDriver(context, {
|
|
29
|
+
ip: context.ip || process.env.TD_IP,
|
|
30
|
+
headless: true,
|
|
31
|
+
});
|
|
32
|
+
await testdriver.provision.chrome({
|
|
33
|
+
url: "http://testdriver-sandbox.vercel.app/login",
|
|
34
|
+
});
|
|
32
35
|
|
|
33
36
|
//
|
|
34
37
|
// Perform login first
|
|
@@ -47,7 +50,7 @@ describe("Hover Text With Description Test", () => {
|
|
|
47
50
|
await cartButton.click();
|
|
48
51
|
|
|
49
52
|
// Assert the TestDriver Hat is in the cart
|
|
50
|
-
const result = await testdriver.assert("
|
|
53
|
+
const result = await testdriver.assert("There is an item in the cart");
|
|
51
54
|
expect(result).toBeTruthy();
|
|
52
55
|
});
|
|
53
56
|
});
|
|
@@ -37,16 +37,22 @@ class InitCommand extends BaseCommand {
|
|
|
37
37
|
if (fs.existsSync(envPath)) {
|
|
38
38
|
const envContent = fs.readFileSync(envPath, "utf8");
|
|
39
39
|
if (envContent.includes("TD_API_KEY=")) {
|
|
40
|
-
console.log(
|
|
40
|
+
console.log(
|
|
41
|
+
chalk.gray("\n API key already configured in .env, skipping...\n"),
|
|
42
|
+
);
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
console.log(chalk.cyan(" Setting up your TestDriver API key...\n"));
|
|
46
|
-
console.log(
|
|
48
|
+
console.log(
|
|
49
|
+
chalk.gray(" Get your API key from: https://console.testdriver.ai/team"),
|
|
50
|
+
);
|
|
47
51
|
|
|
48
52
|
// Ask if user wants to open the browser
|
|
49
|
-
const shouldOpen = await this.askYesNo(
|
|
53
|
+
const shouldOpen = await this.askYesNo(
|
|
54
|
+
" Open API keys page in browser? (Y/n): ",
|
|
55
|
+
);
|
|
50
56
|
if (shouldOpen) {
|
|
51
57
|
try {
|
|
52
58
|
// Dynamic import for ES module
|
|
@@ -54,12 +60,16 @@ class InitCommand extends BaseCommand {
|
|
|
54
60
|
await open("https://console.testdriver.ai/team");
|
|
55
61
|
console.log(chalk.gray(" Opening browser...\n"));
|
|
56
62
|
} catch (error) {
|
|
57
|
-
console.log(
|
|
63
|
+
console.log(
|
|
64
|
+
chalk.yellow(" ⚠️ Could not open browser automatically\n"),
|
|
65
|
+
);
|
|
58
66
|
}
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
// Prompt for API key with hidden input
|
|
62
|
-
const apiKey = await this.promptHidden(
|
|
70
|
+
const apiKey = await this.promptHidden(
|
|
71
|
+
" Enter your API key (input will be hidden): ",
|
|
72
|
+
);
|
|
63
73
|
|
|
64
74
|
if (apiKey && apiKey.trim()) {
|
|
65
75
|
// Save to .env
|
|
@@ -70,7 +80,11 @@ class InitCommand extends BaseCommand {
|
|
|
70
80
|
fs.writeFileSync(envPath, envContent + `TD_API_KEY=${apiKey.trim()}\n`);
|
|
71
81
|
console.log(chalk.green("\n ✓ API key saved to .env\n"));
|
|
72
82
|
} else {
|
|
73
|
-
console.log(
|
|
83
|
+
console.log(
|
|
84
|
+
chalk.yellow(
|
|
85
|
+
"\n ⚠️ No API key entered. You can add it later to .env:\n",
|
|
86
|
+
),
|
|
87
|
+
);
|
|
74
88
|
console.log(chalk.gray(" TD_API_KEY=your_api_key\n"));
|
|
75
89
|
}
|
|
76
90
|
}
|
|
@@ -81,7 +95,7 @@ class InitCommand extends BaseCommand {
|
|
|
81
95
|
async promptHidden(question) {
|
|
82
96
|
return new Promise((resolve) => {
|
|
83
97
|
process.stdout.write(question);
|
|
84
|
-
|
|
98
|
+
|
|
85
99
|
const stdin = process.stdin;
|
|
86
100
|
const wasRaw = stdin.isRaw;
|
|
87
101
|
stdin.setRawMode(true);
|
|
@@ -131,7 +145,9 @@ class InitCommand extends BaseCommand {
|
|
|
131
145
|
rl.question(question, (answer) => {
|
|
132
146
|
rl.close();
|
|
133
147
|
const normalized = answer.toLowerCase().trim();
|
|
134
|
-
resolve(
|
|
148
|
+
resolve(
|
|
149
|
+
normalized === "" || normalized === "y" || normalized === "yes",
|
|
150
|
+
);
|
|
135
151
|
});
|
|
136
152
|
});
|
|
137
153
|
}
|
|
@@ -153,17 +169,20 @@ class InitCommand extends BaseCommand {
|
|
|
153
169
|
scripts: {
|
|
154
170
|
test: "vitest run",
|
|
155
171
|
"test:watch": "vitest",
|
|
156
|
-
"test:ui": "vitest --ui"
|
|
172
|
+
"test:ui": "vitest --ui",
|
|
157
173
|
},
|
|
158
174
|
keywords: ["testdriver", "testing", "e2e"],
|
|
159
175
|
author: "",
|
|
160
176
|
license: "ISC",
|
|
161
177
|
engines: {
|
|
162
|
-
node: ">=20.19.0"
|
|
163
|
-
}
|
|
178
|
+
node: ">=20.19.0",
|
|
179
|
+
},
|
|
164
180
|
};
|
|
165
181
|
|
|
166
|
-
fs.writeFileSync(
|
|
182
|
+
fs.writeFileSync(
|
|
183
|
+
packageJsonPath,
|
|
184
|
+
JSON.stringify(packageJson, null, 2) + "\n",
|
|
185
|
+
);
|
|
167
186
|
console.log(chalk.green(` Created package.json`));
|
|
168
187
|
} else {
|
|
169
188
|
console.log(chalk.gray(" package.json already exists, skipping..."));
|
|
@@ -249,7 +268,7 @@ test('should login and add item to cart', async (context) => {
|
|
|
249
268
|
await cartButton.click();
|
|
250
269
|
|
|
251
270
|
// Verify item in cart
|
|
252
|
-
const result = await testdriver.assert('
|
|
271
|
+
const result = await testdriver.assert('There is an item in the cart');
|
|
253
272
|
expect(result).toBeTruthy();
|
|
254
273
|
|
|
255
274
|
});
|
|
@@ -283,7 +302,6 @@ export default defineConfig({
|
|
|
283
302
|
fs.writeFileSync(configFile, configContent);
|
|
284
303
|
console.log(chalk.green(` Created config file: ${configFile}`));
|
|
285
304
|
}
|
|
286
|
-
|
|
287
305
|
}
|
|
288
306
|
|
|
289
307
|
/**
|
|
@@ -387,10 +405,13 @@ jobs:
|
|
|
387
405
|
console.log(chalk.cyan("\n Installing dependencies...\n"));
|
|
388
406
|
|
|
389
407
|
try {
|
|
390
|
-
execSync(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
408
|
+
execSync(
|
|
409
|
+
"npm install -D vitest testdriverai@beta && npm install dotenv",
|
|
410
|
+
{
|
|
411
|
+
cwd: process.cwd(),
|
|
412
|
+
stdio: "inherit",
|
|
413
|
+
},
|
|
414
|
+
);
|
|
394
415
|
console.log(chalk.green("\n Dependencies installed successfully!"));
|
|
395
416
|
} catch (error) {
|
|
396
417
|
console.log(
|
|
@@ -410,10 +431,16 @@ jobs:
|
|
|
410
431
|
console.log(chalk.cyan("Next steps:\n"));
|
|
411
432
|
console.log(" 1. Run your tests:");
|
|
412
433
|
console.log(chalk.gray(" npx vitest run\n"));
|
|
413
|
-
console.log(" 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets");
|
|
414
|
-
console.log(chalk.gray(" Settings → Secrets → Actions → New repository secret\n"));
|
|
415
434
|
console.log(
|
|
416
|
-
|
|
435
|
+
" 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets",
|
|
436
|
+
);
|
|
437
|
+
console.log(
|
|
438
|
+
chalk.gray(" Settings → Secrets → Actions → New repository secret\n"),
|
|
439
|
+
);
|
|
440
|
+
console.log(
|
|
441
|
+
chalk.cyan(
|
|
442
|
+
"Learn more at https://docs.testdriver.ai/v7/getting-started/\n",
|
|
443
|
+
),
|
|
417
444
|
);
|
|
418
445
|
}
|
|
419
446
|
}
|
package/package.json
CHANGED
package/sdk.d.ts
CHANGED
|
@@ -1137,25 +1137,19 @@ export default class TestDriverSDK {
|
|
|
1137
1137
|
|
|
1138
1138
|
/**
|
|
1139
1139
|
* Capture a screenshot of the current screen and save it to .testdriver/screenshots
|
|
1140
|
-
* @param
|
|
1141
|
-
* @param silent - Whether to suppress logging (default: false)
|
|
1142
|
-
* @param mouse - Whether to include mouse cursor (default: false)
|
|
1140
|
+
* @param filename - Custom filename (without .png extension)
|
|
1143
1141
|
* @returns The file path where the screenshot was saved
|
|
1144
1142
|
*
|
|
1145
1143
|
* @example
|
|
1146
|
-
* // Capture a screenshot
|
|
1147
|
-
* const screenshotPath = await
|
|
1148
|
-
* console.log('Screenshot saved to:', screenshotPath);
|
|
1144
|
+
* // Capture a screenshot with auto-generated filename
|
|
1145
|
+
* const screenshotPath = await testdriver.screenshot();
|
|
1149
1146
|
*
|
|
1150
1147
|
* @example
|
|
1151
|
-
* // Capture with
|
|
1152
|
-
* const screenshotPath = await
|
|
1148
|
+
* // Capture with custom filename
|
|
1149
|
+
* const screenshotPath = await testdriver.screenshot("login-page");
|
|
1150
|
+
* // Saves to: .testdriver/screenshots/<test>/login-page.png
|
|
1153
1151
|
*/
|
|
1154
|
-
screenshot(
|
|
1155
|
-
scale?: number,
|
|
1156
|
-
silent?: boolean,
|
|
1157
|
-
mouse?: boolean,
|
|
1158
|
-
): Promise<string>;
|
|
1152
|
+
screenshot(filename?: string): Promise<string>;
|
|
1159
1153
|
|
|
1160
1154
|
/**
|
|
1161
1155
|
* Wait for specified time
|
package/sdk.js
CHANGED
|
@@ -2772,24 +2772,27 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2772
2772
|
// ====================================
|
|
2773
2773
|
|
|
2774
2774
|
/**
|
|
2775
|
-
* Capture a screenshot of the current screen and save it to .
|
|
2776
|
-
* @param {
|
|
2777
|
-
* @param {boolean} [silent=false] - Whether to suppress logging
|
|
2778
|
-
* @param {boolean} [mouse=false] - Whether to include mouse cursor
|
|
2775
|
+
* Capture a screenshot of the current screen and save it to .testdriver/screenshots
|
|
2776
|
+
* @param {string} [filename] - Custom filename (without .png extension)
|
|
2779
2777
|
* @returns {Promise<string>} The file path where the screenshot was saved
|
|
2780
2778
|
*
|
|
2781
2779
|
* @example
|
|
2782
|
-
* // Capture a screenshot
|
|
2783
|
-
* const screenshotPath = await
|
|
2784
|
-
* console.log('Screenshot saved to:', screenshotPath);
|
|
2780
|
+
* // Capture a screenshot with auto-generated filename
|
|
2781
|
+
* const screenshotPath = await testdriver.screenshot();
|
|
2785
2782
|
*
|
|
2786
2783
|
* @example
|
|
2787
|
-
* // Capture with
|
|
2788
|
-
* const screenshotPath = await
|
|
2784
|
+
* // Capture with custom filename
|
|
2785
|
+
* const screenshotPath = await testdriver.screenshot("login-page");
|
|
2786
|
+
* // Saves to: .testdriver/screenshots/<test>/login-page.png
|
|
2789
2787
|
*/
|
|
2790
|
-
async screenshot(
|
|
2788
|
+
async screenshot(filename) {
|
|
2791
2789
|
this._ensureConnected();
|
|
2792
|
-
|
|
2790
|
+
|
|
2791
|
+
const finalFilename = filename
|
|
2792
|
+
? (filename.endsWith('.png') ? filename : `${filename}.png`)
|
|
2793
|
+
: `screenshot-${Date.now()}.png`;
|
|
2794
|
+
|
|
2795
|
+
const base64Data = await this.system.captureScreenBase64(1, false, false);
|
|
2793
2796
|
|
|
2794
2797
|
// Save to .testdriver/screenshots/<test-file-name> directory
|
|
2795
2798
|
let screenshotsDir = path.join(process.cwd(), ".testdriver", "screenshots");
|
|
@@ -2801,8 +2804,7 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2801
2804
|
fs.mkdirSync(screenshotsDir, { recursive: true });
|
|
2802
2805
|
}
|
|
2803
2806
|
|
|
2804
|
-
const
|
|
2805
|
-
const filePath = path.join(screenshotsDir, filename);
|
|
2807
|
+
const filePath = path.join(screenshotsDir, finalFilename);
|
|
2806
2808
|
|
|
2807
2809
|
// Remove data:image/png;base64, prefix if present
|
|
2808
2810
|
const cleanBase64 = base64Data.replace(/^data:image\/\w+;base64,/, "");
|
|
@@ -2810,9 +2812,7 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2810
2812
|
|
|
2811
2813
|
fs.writeFileSync(filePath, buffer);
|
|
2812
2814
|
|
|
2813
|
-
|
|
2814
|
-
this.emitter.emit("log:info", `📸 Screenshot saved to: ${filePath}`);
|
|
2815
|
-
}
|
|
2815
|
+
this.emitter.emit("log:info", `📸 Screenshot saved to: ${filePath}`);
|
|
2816
2816
|
|
|
2817
2817
|
return filePath;
|
|
2818
2818
|
}
|