testdriverai 7.1.2 → 7.1.4
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/test-init.yml +145 -0
- package/agent/lib/commander.js +2 -2
- package/agent/lib/commands.js +10 -5
- package/docs/docs.json +29 -84
- package/docs/v7/_drafts/migration.mdx +3 -3
- package/docs/v7/api/act.mdx +1 -1
- package/docs/v7/api/assert.mdx +1 -1
- package/docs/v7/api/assertions.mdx +14 -14
- package/docs/v7/getting-started/quickstart.mdx +11 -9
- package/docs/v7/getting-started/running-and-debugging.mdx +3 -2
- package/docs/v7/getting-started/writing-tests.mdx +4 -5
- package/docs/v7/overview/readme.mdx +1 -1
- package/docs/v7/presets/chrome.mdx +38 -41
- package/docs/v7/presets/electron.mdx +107 -100
- package/docs/v7/presets/webapp.mdx +40 -43
- package/interfaces/cli/commands/init.js +79 -21
- package/interfaces/vitest-plugin.mjs +36 -9
- package/lib/vitest/hooks.mjs +5 -1
- package/manual/test-init-command.js +223 -0
- package/package.json +2 -2
- package/schema.json +5 -5
- package/sdk-log-formatter.js +1 -1
- package/sdk.d.ts +8 -8
- package/sdk.js +64 -14
- package/test/testdriver/exec-output.test.mjs +1 -1
- package/test/testdriver/exec-pwsh.test.mjs +1 -1
- package/test/testdriver/focus-window.test.mjs +1 -1
- package/test/testdriver/hover-image.test.mjs +1 -1
- package/test/testdriver/hover-text-with-description.test.mjs +0 -3
- package/test/testdriver/match-image.test.mjs +1 -1
- package/test/testdriver/scroll-keyboard.test.mjs +1 -1
- package/test/testdriver/scroll-until-image.test.mjs +1 -1
- package/test/testdriver/scroll-until-text.test.mjs +18 -1
- package/test/testdriver/scroll.test.mjs +1 -1
- package/test/testdriver/setup/lifecycleHelpers.mjs +105 -5
- package/test/testdriver/setup/testHelpers.mjs +6 -2
- package/vitest.config.mjs +7 -2
- package/test/testdriver/exec-js.test.mjs +0 -43
|
@@ -13,13 +13,12 @@ The `webApp()` preset provides a generic interface for testing web applications.
|
|
|
13
13
|
|
|
14
14
|
```javascript
|
|
15
15
|
import { test } from 'vitest';
|
|
16
|
-
import {
|
|
16
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
17
17
|
|
|
18
18
|
test('web app test', async (context) => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
19
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
20
|
+
|
|
21
|
+
await testdriver.provision.chrome({ url: 'https://myapp.com' });
|
|
23
22
|
|
|
24
23
|
await testdriver.find('Login button').click();
|
|
25
24
|
});
|
|
@@ -83,12 +82,12 @@ webApp(context, options): Promise<WebAppResult>
|
|
|
83
82
|
|
|
84
83
|
```javascript
|
|
85
84
|
import { test } from 'vitest';
|
|
86
|
-
import {
|
|
85
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
87
86
|
|
|
88
87
|
test('user login flow', async (context) => {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
});
|
|
88
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
89
|
+
|
|
90
|
+
await testdriver.provision.chrome({ url: 'https://myapp.com/login' });
|
|
92
91
|
|
|
93
92
|
await testdriver.find('email input').type('user@example.com');
|
|
94
93
|
await testdriver.find('password input').type('password123');
|
|
@@ -102,12 +101,12 @@ test('user login flow', async (context) => {
|
|
|
102
101
|
|
|
103
102
|
```javascript
|
|
104
103
|
import { test } from 'vitest';
|
|
105
|
-
import {
|
|
104
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
106
105
|
|
|
107
106
|
test('product purchase', async (context) => {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
});
|
|
107
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
108
|
+
|
|
109
|
+
await testdriver.provision.chrome({ url: 'https://shop.example.com' });
|
|
111
110
|
|
|
112
111
|
// Search for product
|
|
113
112
|
await testdriver.find('search input').type('laptop');
|
|
@@ -131,12 +130,12 @@ test('product purchase', async (context) => {
|
|
|
131
130
|
|
|
132
131
|
```javascript
|
|
133
132
|
import { test } from 'vitest';
|
|
134
|
-
import {
|
|
133
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
135
134
|
|
|
136
135
|
test('single page app routing', async (context) => {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
});
|
|
136
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
137
|
+
|
|
138
|
+
await testdriver.provision.chrome({ url: 'https://spa.example.com' });
|
|
140
139
|
|
|
141
140
|
// Navigate through SPA routes
|
|
142
141
|
await testdriver.find('About link').click();
|
|
@@ -154,12 +153,12 @@ test('single page app routing', async (context) => {
|
|
|
154
153
|
|
|
155
154
|
```javascript
|
|
156
155
|
import { test } from 'vitest';
|
|
157
|
-
import {
|
|
156
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
158
157
|
|
|
159
158
|
test('form validation errors', async (context) => {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
});
|
|
159
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
160
|
+
|
|
161
|
+
await testdriver.provision.chrome({ url: 'https://myapp.com/register' });
|
|
163
162
|
|
|
164
163
|
// Submit empty form
|
|
165
164
|
await testdriver.find('Submit button').click();
|
|
@@ -183,12 +182,12 @@ test('form validation errors', async (context) => {
|
|
|
183
182
|
|
|
184
183
|
```javascript
|
|
185
184
|
import { test } from 'vitest';
|
|
186
|
-
import {
|
|
185
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
187
186
|
|
|
188
187
|
test('data fetching and display', async (context) => {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
});
|
|
188
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
189
|
+
|
|
190
|
+
await testdriver.provision.chrome({ url: 'https://api-demo.example.com' });
|
|
192
191
|
|
|
193
192
|
// Trigger data fetch
|
|
194
193
|
await testdriver.find('Load Data button').click();
|
|
@@ -208,13 +207,12 @@ test('data fetching and display', async (context) => {
|
|
|
208
207
|
|
|
209
208
|
```javascript
|
|
210
209
|
import { test } from 'vitest';
|
|
211
|
-
import {
|
|
210
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
212
211
|
|
|
213
212
|
test('mobile menu', async (context) => {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
});
|
|
213
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
214
|
+
|
|
215
|
+
await testdriver.provision.chrome({ url: 'https://responsive.example.com' });
|
|
218
216
|
|
|
219
217
|
// Resize to mobile
|
|
220
218
|
await testdriver.exec('sh', 'xdotool getactivewindow windowsize 375 667', 5000);
|
|
@@ -231,13 +229,12 @@ test('mobile menu', async (context) => {
|
|
|
231
229
|
|
|
232
230
|
## Browser Support
|
|
233
231
|
|
|
234
|
-
Currently
|
|
232
|
+
Currently supports Chrome:
|
|
235
233
|
|
|
236
234
|
```javascript
|
|
237
235
|
// ✅ Supported
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
});
|
|
236
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
237
|
+
await testdriver.provision.chrome({ url: 'https://myapp.com' });
|
|
241
238
|
|
|
242
239
|
// ❌ Not yet supported
|
|
243
240
|
const { testdriver } = await webApp(context, {
|
|
@@ -310,9 +307,9 @@ The behavior is identical, but `webApp()` provides a more generic interface for
|
|
|
310
307
|
|
|
311
308
|
```javascript
|
|
312
309
|
test('login and navigate', async (context) => {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
});
|
|
310
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
311
|
+
|
|
312
|
+
await testdriver.provision.chrome({ url: 'https://myapp.com' });
|
|
316
313
|
|
|
317
314
|
// Login
|
|
318
315
|
await testdriver.find('email').type('user@example.com');
|
|
@@ -332,9 +329,9 @@ test('login and navigate', async (context) => {
|
|
|
332
329
|
|
|
333
330
|
```javascript
|
|
334
331
|
test('upload file', async (context) => {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
});
|
|
332
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
333
|
+
|
|
334
|
+
await testdriver.provision.chrome({ url: 'https://upload.example.com' });
|
|
338
335
|
|
|
339
336
|
// Click upload button
|
|
340
337
|
await testdriver.find('Upload file button').click();
|
|
@@ -352,9 +349,9 @@ test('upload file', async (context) => {
|
|
|
352
349
|
|
|
353
350
|
```javascript
|
|
354
351
|
test('search with filters', async (context) => {
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
});
|
|
352
|
+
const testdriver = TestDriver(context, { headless: true });
|
|
353
|
+
|
|
354
|
+
await testdriver.provision.chrome({ url: 'https://catalog.example.com' });
|
|
358
355
|
|
|
359
356
|
// Enter search query
|
|
360
357
|
await testdriver.find('search input').type('laptop');
|
|
@@ -143,7 +143,10 @@ class InitCommand extends BaseCommand {
|
|
|
143
143
|
},
|
|
144
144
|
keywords: ["testdriver", "testing", "e2e"],
|
|
145
145
|
author: "",
|
|
146
|
-
license: "ISC"
|
|
146
|
+
license: "ISC",
|
|
147
|
+
engines: {
|
|
148
|
+
node: ">=20.19.0"
|
|
149
|
+
}
|
|
147
150
|
};
|
|
148
151
|
|
|
149
152
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
@@ -159,6 +162,7 @@ class InitCommand extends BaseCommand {
|
|
|
159
162
|
async createVitestExample() {
|
|
160
163
|
const testDir = path.join(process.cwd(), "tests");
|
|
161
164
|
const testFile = path.join(testDir, "example.test.js");
|
|
165
|
+
const loginSnippetFile = path.join(testDir, "login.js");
|
|
162
166
|
const configFile = path.join(process.cwd(), "vitest.config.js");
|
|
163
167
|
|
|
164
168
|
// Create test directory if it doesn't exist
|
|
@@ -167,23 +171,73 @@ class InitCommand extends BaseCommand {
|
|
|
167
171
|
console.log(chalk.gray(` Created directory: ${testDir}`));
|
|
168
172
|
}
|
|
169
173
|
|
|
170
|
-
// Create
|
|
174
|
+
// Create login snippet file
|
|
175
|
+
const loginSnippetContent = `/**
|
|
176
|
+
* Login snippet - reusable login function
|
|
177
|
+
*
|
|
178
|
+
* This demonstrates how to create reusable test snippets that can be
|
|
179
|
+
* imported and used across multiple test files.
|
|
180
|
+
*/
|
|
181
|
+
export async function login(testdriver) {
|
|
182
|
+
|
|
183
|
+
// The password is displayed on screen, have TestDriver extract it
|
|
184
|
+
const password = await testdriver.extract('the password');
|
|
185
|
+
|
|
186
|
+
// Find the username field
|
|
187
|
+
const usernameField = await testdriver.find(
|
|
188
|
+
'Username, label above the username input field on the login form'
|
|
189
|
+
);
|
|
190
|
+
await usernameField.click();
|
|
191
|
+
|
|
192
|
+
// Type username
|
|
193
|
+
await testdriver.type('standard_user');
|
|
194
|
+
|
|
195
|
+
// Enter password form earlier
|
|
196
|
+
// Marked as secret so it's not logged or stored
|
|
197
|
+
await testdriver.pressKeys(['tab']);
|
|
198
|
+
await testdriver.type(password, { secret: true });
|
|
199
|
+
|
|
200
|
+
// Submit the form
|
|
201
|
+
await testdriver.find('submit button on the login form').click();
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
fs.writeFileSync(loginSnippetFile, loginSnippetContent);
|
|
206
|
+
console.log(chalk.green(` Created login snippet: ${loginSnippetFile}`));
|
|
207
|
+
|
|
208
|
+
// Create example Vitest test that uses the login snippet
|
|
171
209
|
const vitestContent = `import { test, expect } from 'vitest';
|
|
172
|
-
import {
|
|
210
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
211
|
+
import { login } from './login.js';
|
|
212
|
+
|
|
213
|
+
test('should login and add item to cart', async (context) => {
|
|
214
|
+
|
|
215
|
+
// Create TestDriver instance - automatically connects to sandbox
|
|
216
|
+
const testdriver = TestDriver(context);
|
|
217
|
+
|
|
218
|
+
// Launch chrome and navigate to demo app
|
|
219
|
+
await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
|
|
220
|
+
|
|
221
|
+
// Use the login snippet to handle authentication
|
|
222
|
+
// This demonstrates how to reuse test logic across multiple tests
|
|
223
|
+
await login(testdriver);
|
|
173
224
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
225
|
+
// Add item to cart
|
|
226
|
+
const addToCartButton = await testdriver.find(
|
|
227
|
+
'add to cart button under TestDriver Hat'
|
|
228
|
+
);
|
|
229
|
+
await addToCartButton.click();
|
|
180
230
|
|
|
181
|
-
//
|
|
182
|
-
const
|
|
183
|
-
|
|
231
|
+
// Open cart
|
|
232
|
+
const cartButton = await testdriver.find(
|
|
233
|
+
'cart button in the top right corner'
|
|
234
|
+
);
|
|
235
|
+
await cartButton.click();
|
|
184
236
|
|
|
185
|
-
|
|
186
|
-
|
|
237
|
+
// Verify item in cart
|
|
238
|
+
const result = await testdriver.assert('TestDriver Hat is in the cart');
|
|
239
|
+
expect(result).toBeTruthy();
|
|
240
|
+
|
|
187
241
|
});
|
|
188
242
|
`;
|
|
189
243
|
|
|
@@ -194,16 +248,20 @@ test('should navigate to example.com and find elements', async (context) => {
|
|
|
194
248
|
if (!fs.existsSync(configFile)) {
|
|
195
249
|
const configContent = `import { defineConfig } from 'vitest/config';
|
|
196
250
|
import TestDriver from 'testdriverai/vitest';
|
|
197
|
-
import
|
|
251
|
+
import { config } from 'dotenv';
|
|
198
252
|
|
|
199
253
|
// Load environment variables from .env file
|
|
200
|
-
|
|
254
|
+
config();
|
|
201
255
|
|
|
202
256
|
export default defineConfig({
|
|
203
|
-
plugins: [TestDriver()],
|
|
204
257
|
test: {
|
|
205
|
-
testTimeout:
|
|
206
|
-
hookTimeout:
|
|
258
|
+
testTimeout: 300000,
|
|
259
|
+
hookTimeout: 300000,
|
|
260
|
+
reporters: [
|
|
261
|
+
'default',
|
|
262
|
+
TestDriver(),
|
|
263
|
+
],
|
|
264
|
+
setupFiles: ['testdriverai/vitest/setup'],
|
|
207
265
|
},
|
|
208
266
|
});
|
|
209
267
|
`;
|
|
@@ -315,7 +373,7 @@ jobs:
|
|
|
315
373
|
console.log(chalk.cyan("\n Installing dependencies...\n"));
|
|
316
374
|
|
|
317
375
|
try {
|
|
318
|
-
execSync("npm install -D vitest testdriverai && npm install dotenv", {
|
|
376
|
+
execSync("npm install -D vitest testdriverai@beta && npm install dotenv", {
|
|
319
377
|
cwd: process.cwd(),
|
|
320
378
|
stdio: "inherit"
|
|
321
379
|
});
|
|
@@ -326,7 +384,7 @@ jobs:
|
|
|
326
384
|
"\n⚠️ Failed to install dependencies automatically. Please run:",
|
|
327
385
|
),
|
|
328
386
|
);
|
|
329
|
-
console.log(chalk.gray(" npm install -D vitest testdriverai"));
|
|
387
|
+
console.log(chalk.gray(" npm install -D vitest testdriverai@beta"));
|
|
330
388
|
console.log(chalk.gray(" npm install dotenv\n"));
|
|
331
389
|
}
|
|
332
390
|
}
|
|
@@ -423,7 +423,7 @@ export default function testDriverPlugin(options = {}) {
|
|
|
423
423
|
options.apiRoot || process.env.TD_API_ROOT || "https://testdriver-api.onrender.com";
|
|
424
424
|
pluginState.ciProvider = detectCI();
|
|
425
425
|
pluginState.gitInfo = getGitInfo();
|
|
426
|
-
|
|
426
|
+
|
|
427
427
|
// Store TestDriver-specific options (excluding plugin-specific ones)
|
|
428
428
|
const { apiKey, apiRoot, ...testDriverOptions } = options;
|
|
429
429
|
pluginState.testDriverOptions = testDriverOptions;
|
|
@@ -441,7 +441,13 @@ export default function testDriverPlugin(options = {}) {
|
|
|
441
441
|
logger.debug("Global TestDriver options:", testDriverOptions);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
|
|
444
|
+
// Create reporter instance
|
|
445
|
+
const reporter = new TestDriverReporter(options);
|
|
446
|
+
|
|
447
|
+
// Add name property for Vitest
|
|
448
|
+
reporter.name = 'testdriver';
|
|
449
|
+
|
|
450
|
+
return reporter;
|
|
445
451
|
}
|
|
446
452
|
|
|
447
453
|
/**
|
|
@@ -458,6 +464,10 @@ class TestDriverReporter {
|
|
|
458
464
|
this.ctx = ctx;
|
|
459
465
|
logger.debug("onInit called - UPDATED VERSION");
|
|
460
466
|
|
|
467
|
+
// Store project root for making file paths relative
|
|
468
|
+
pluginState.projectRoot = ctx.config.root || process.cwd();
|
|
469
|
+
logger.debug("Project root:", pluginState.projectRoot);
|
|
470
|
+
|
|
461
471
|
// NOW read the API key and API root (after setupFiles have run, including dotenv/config)
|
|
462
472
|
pluginState.apiKey = this.options.apiKey || process.env.TD_API_KEY;
|
|
463
473
|
pluginState.apiRoot = this.options.apiRoot || process.env.TD_API_ROOT || "https://testdriver-api.onrender.com";
|
|
@@ -668,11 +678,15 @@ class TestDriverReporter {
|
|
|
668
678
|
dashcamUrl = testResult.dashcamUrl || null;
|
|
669
679
|
const platform = testResult.platform || null;
|
|
670
680
|
sessionId = testResult.sessionId || null;
|
|
671
|
-
|
|
681
|
+
const absolutePath =
|
|
672
682
|
testResult.testFile ||
|
|
673
683
|
test.file?.filepath ||
|
|
674
684
|
test.file?.name ||
|
|
675
685
|
"unknown";
|
|
686
|
+
// Make path relative to project root
|
|
687
|
+
testFile = pluginState.projectRoot && absolutePath !== "unknown"
|
|
688
|
+
? path.relative(pluginState.projectRoot, absolutePath)
|
|
689
|
+
: absolutePath;
|
|
676
690
|
testOrder =
|
|
677
691
|
testResult.testOrder !== undefined ? testResult.testOrder : 0;
|
|
678
692
|
// Don't override duration from file - use Vitest's result.duration
|
|
@@ -696,7 +710,7 @@ class TestDriverReporter {
|
|
|
696
710
|
logger.debug(`No result file found for test: ${test.id}`);
|
|
697
711
|
// Fallback to test object properties - try multiple sources
|
|
698
712
|
// In Vitest, the file path is on test.module.task.filepath
|
|
699
|
-
|
|
713
|
+
const absolutePath =
|
|
700
714
|
test.module?.task?.filepath ||
|
|
701
715
|
test.module?.file?.filepath ||
|
|
702
716
|
test.module?.file?.name ||
|
|
@@ -706,13 +720,17 @@ class TestDriverReporter {
|
|
|
706
720
|
test.suite?.file?.name ||
|
|
707
721
|
test.location?.file ||
|
|
708
722
|
"unknown";
|
|
723
|
+
// Make path relative to project root
|
|
724
|
+
testFile = pluginState.projectRoot && absolutePath !== "unknown"
|
|
725
|
+
? path.relative(pluginState.projectRoot, absolutePath)
|
|
726
|
+
: absolutePath;
|
|
709
727
|
logger.debug(`Resolved testFile: ${testFile}`);
|
|
710
728
|
}
|
|
711
729
|
} catch (error) {
|
|
712
730
|
logger.error("Failed to read test result file:", error.message);
|
|
713
731
|
// Fallback to test object properties - try multiple sources
|
|
714
732
|
// In Vitest, the file path is on test.module.task.filepath
|
|
715
|
-
|
|
733
|
+
const absolutePath =
|
|
716
734
|
test.module?.task?.filepath ||
|
|
717
735
|
test.module?.file?.filepath ||
|
|
718
736
|
test.module?.file?.name ||
|
|
@@ -722,6 +740,10 @@ class TestDriverReporter {
|
|
|
722
740
|
test.suite?.file?.name ||
|
|
723
741
|
test.location?.file ||
|
|
724
742
|
"unknown";
|
|
743
|
+
// Make path relative to project root
|
|
744
|
+
testFile = pluginState.projectRoot && absolutePath !== "unknown"
|
|
745
|
+
? path.relative(pluginState.projectRoot, absolutePath)
|
|
746
|
+
: absolutePath;
|
|
725
747
|
logger.debug(`Resolved testFile from fallback: ${testFile}`);
|
|
726
748
|
}
|
|
727
749
|
|
|
@@ -901,8 +923,9 @@ function getGitInfo() {
|
|
|
901
923
|
encoding: "utf8",
|
|
902
924
|
stdio: ["pipe", "pipe", "ignore"]
|
|
903
925
|
}).trim();
|
|
926
|
+
logger.debug("Git commit from local:", info.commit);
|
|
904
927
|
} catch (e) {
|
|
905
|
-
|
|
928
|
+
logger.debug("Failed to get git commit:", e.message);
|
|
906
929
|
}
|
|
907
930
|
}
|
|
908
931
|
|
|
@@ -912,8 +935,9 @@ function getGitInfo() {
|
|
|
912
935
|
encoding: "utf8",
|
|
913
936
|
stdio: ["pipe", "pipe", "ignore"]
|
|
914
937
|
}).trim();
|
|
938
|
+
logger.debug("Git branch from local:", info.branch);
|
|
915
939
|
} catch (e) {
|
|
916
|
-
|
|
940
|
+
logger.debug("Failed to get git branch:", e.message);
|
|
917
941
|
}
|
|
918
942
|
}
|
|
919
943
|
|
|
@@ -923,8 +947,9 @@ function getGitInfo() {
|
|
|
923
947
|
encoding: "utf8",
|
|
924
948
|
stdio: ["pipe", "pipe", "ignore"]
|
|
925
949
|
}).trim();
|
|
950
|
+
logger.debug("Git author from local:", info.author);
|
|
926
951
|
} catch (e) {
|
|
927
|
-
|
|
952
|
+
logger.debug("Failed to get git author:", e.message);
|
|
928
953
|
}
|
|
929
954
|
}
|
|
930
955
|
|
|
@@ -941,12 +966,14 @@ function getGitInfo() {
|
|
|
941
966
|
const match = remoteUrl.match(/[:/]([^/:]+\/[^/:]+?)(\.git)?$/);
|
|
942
967
|
if (match) {
|
|
943
968
|
info.repo = match[1];
|
|
969
|
+
logger.debug("Git repo from local:", info.repo);
|
|
944
970
|
}
|
|
945
971
|
} catch (e) {
|
|
946
|
-
|
|
972
|
+
logger.debug("Failed to get git repo:", e.message);
|
|
947
973
|
}
|
|
948
974
|
}
|
|
949
975
|
|
|
976
|
+
logger.info("Collected git info:", info);
|
|
950
977
|
return info;
|
|
951
978
|
}
|
|
952
979
|
|
package/lib/vitest/hooks.mjs
CHANGED
|
@@ -246,7 +246,11 @@ export function TestDriver(context, options = {}) {
|
|
|
246
246
|
if (dashcamUrl) {
|
|
247
247
|
const testId = context.task.id;
|
|
248
248
|
const platform = testdriver.os || 'linux';
|
|
249
|
-
const
|
|
249
|
+
const absolutePath = context.task.file?.filepath || context.task.file?.name || 'unknown';
|
|
250
|
+
const projectRoot = process.cwd();
|
|
251
|
+
const testFile = absolutePath !== 'unknown'
|
|
252
|
+
? path.relative(projectRoot, absolutePath)
|
|
253
|
+
: absolutePath;
|
|
250
254
|
|
|
251
255
|
// Create results directory if it doesn't exist
|
|
252
256
|
const resultsDir = path.join(os.tmpdir(), 'testdriver-results');
|