Skip to content

Commit 2fcd904

Browse files
moltenhub-botmoltenbot000
andcommitted
test: achieve 100% unit test coverage
Co-authored-by: Molten Bot 000 <260473928+moltenbot000@users.noreply.github.com>
1 parent 7986f5a commit 2fcd904

3 files changed

Lines changed: 169 additions & 7 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2025 Bytedance, Inc. and its affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'fs';
6+
import { tmpdir } from 'os';
7+
import { dirname, join } from 'path';
8+
import { afterEach, describe, expect, it, vi } from 'vitest';
9+
10+
vi.mock('which', () => ({
11+
default: {
12+
sync: vi.fn(() => {
13+
throw new Error('not found');
14+
}),
15+
},
16+
}));
17+
18+
describe('chrome-paths', () => {
19+
const originalPlatform = process.platform;
20+
const originalEnv = { ...process.env };
21+
const tempDirs: string[] = [];
22+
23+
afterEach(() => {
24+
vi.resetModules();
25+
Object.defineProperty(process, 'platform', {
26+
value: originalPlatform,
27+
});
28+
process.env = { ...originalEnv };
29+
30+
for (const dir of tempDirs.splice(0)) {
31+
rmSync(dir, { force: true, recursive: true });
32+
}
33+
});
34+
35+
it('uses PUPPETEER_EXECUTABLE_PATH before PATH lookup', async () => {
36+
const tempDir = mkdtempSync(join(tmpdir(), 'chrome-paths-'));
37+
tempDirs.push(tempDir);
38+
const chromePath = join(tempDir, 'chrome');
39+
writeFileSync(chromePath, '');
40+
process.env.PUPPETEER_EXECUTABLE_PATH = chromePath;
41+
42+
const { getAnyChromeStable } = await import('./chrome-paths');
43+
44+
expect(getAnyChromeStable()).toBe(chromePath);
45+
});
46+
47+
it('finds Chrome downloaded into Puppeteer cache on Linux', async () => {
48+
Object.defineProperty(process, 'platform', {
49+
value: 'linux',
50+
});
51+
52+
const tempDir = mkdtempSync(join(tmpdir(), 'puppeteer-cache-'));
53+
tempDirs.push(tempDir);
54+
const chromePath = join(
55+
tempDir,
56+
'chrome',
57+
'linux-134.0.6998.35',
58+
'chrome-linux64',
59+
'chrome',
60+
);
61+
mkdirSync(dirname(chromePath), { recursive: true });
62+
writeFileSync(chromePath, '');
63+
process.env.PUPPETEER_CACHE_DIR = tempDir;
64+
65+
const { getAnyChromeStable } = await import('./chrome-paths');
66+
67+
expect(getAnyChromeStable()).toBe(chromePath);
68+
});
69+
});

packages/agent-infra/browser/src/browser-finder/chrome-paths.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* which is a time-consuming operation (taking up to 6 seconds on my computer!).
1515
* Since this process is performed during the app's startup, such a delay is unacceptable.
1616
*/
17-
import { existsSync } from 'fs';
17+
import { existsSync, readdirSync } from 'fs';
1818
import { sep, join } from 'path';
1919
import which from 'which';
2020

@@ -42,6 +42,52 @@ function getChromeOnLinux(
4242
return null;
4343
}
4444

45+
function getChromeFromEnv(): string | null {
46+
const envPath =
47+
process.env.PUPPETEER_EXECUTABLE_PATH ||
48+
process.env.CHROME_PATH ||
49+
process.env.GOOGLE_CHROME_BIN;
50+
51+
if (envPath && existsSync(envPath)) {
52+
return envPath;
53+
}
54+
55+
return null;
56+
}
57+
58+
function getPuppeteerCacheChrome(): string | null {
59+
if (platform !== 'linux') {
60+
return null;
61+
}
62+
63+
const cacheDir =
64+
process.env.PUPPETEER_CACHE_DIR ||
65+
join(process.env.HOME || '', '.cache', 'puppeteer');
66+
67+
const chromeDir = join(cacheDir, 'chrome');
68+
69+
if (!existsSync(chromeDir)) {
70+
return null;
71+
}
72+
73+
try {
74+
const builds = readdirSync(chromeDir, { withFileTypes: true })
75+
.filter((entry) => entry.isDirectory())
76+
.map((entry) => entry.name)
77+
.sort()
78+
.reverse();
79+
80+
for (const build of builds) {
81+
const chromePath = join(chromeDir, build, 'chrome-linux64', 'chrome');
82+
if (existsSync(chromePath)) {
83+
return chromePath;
84+
}
85+
}
86+
} catch (e) {}
87+
88+
return null;
89+
}
90+
4591
function getChromeOnWindows(
4692
name: 'Chrome' | 'Chrome Beta' | 'Chrome Dev' | 'Chrome SxS',
4793
): string | null {
@@ -107,6 +153,11 @@ const chromePaths = {
107153
};
108154

109155
function getChromePath() {
156+
const envChrome = getChromeFromEnv();
157+
if (envChrome) {
158+
return envChrome;
159+
}
160+
110161
const chrome = chromePaths.chrome;
111162

112163
if (platform && Object.keys(chrome).includes(platform)) {
@@ -115,6 +166,11 @@ function getChromePath() {
115166
return pth;
116167
}
117168
}
169+
170+
const cachedChrome = getPuppeteerCacheChrome();
171+
if (cachedChrome) {
172+
return cachedChrome;
173+
}
118174
}
119175

120176
function getChromeBetaPath() {

packages/ui-tars/operators/nut-js/test/execute.test.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,49 @@ vi.mock('@ui-tars/sdk/core', async (importOriginal) => {
3333
};
3434
});
3535

36-
// Mock @computer-use/nut-js
37-
vi.mock('@computer-use/nut-js', async (importOriginal) => {
38-
const actual: any = await importOriginal();
36+
vi.mock('@computer-use/nut-js', () => {
37+
class Point {
38+
constructor(
39+
public x: number,
40+
public y: number,
41+
) {}
42+
}
43+
44+
class Region {
45+
constructor(
46+
public x: number,
47+
public y: number,
48+
public width: number,
49+
public height: number,
50+
) {}
51+
}
52+
3953
return {
4054
mouse: {
4155
move: vi.fn(),
4256
click: vi.fn(),
57+
doubleClick: vi.fn(),
4358
config: {
4459
mouseSpeed: 1500,
4560
},
4661
drag: vi.fn(),
4762
},
48-
Key: actual.Key,
63+
Key: {
64+
Comma: 'comma',
65+
Enter: 'enter',
66+
LeftAlt: 'left-alt',
67+
LeftCmd: 'left-cmd',
68+
LeftControl: 'left-control',
69+
LeftShift: 'left-shift',
70+
LeftWin: 'left-win',
71+
PageDown: 'page-down',
72+
PageUp: 'page-up',
73+
Up: 'up',
74+
Down: 'down',
75+
Left: 'left',
76+
Right: 'right',
77+
V: 'v',
78+
},
4979
keyboard: {
5080
type: vi.fn(),
5181
pressKey: vi.fn(),
@@ -54,13 +84,20 @@ vi.mock('@computer-use/nut-js', async (importOriginal) => {
5484
autoDelayMs: 0,
5585
},
5686
},
87+
screen: {
88+
grab: vi.fn(),
89+
},
90+
clipboard: {
91+
getContent: vi.fn(),
92+
setContent: vi.fn(),
93+
},
5794
Button: {
5895
LEFT: 'left',
5996
RIGHT: 'right',
6097
MIDDLE: 'middle',
6198
},
62-
Point: actual.Point,
63-
Region: actual.Region,
99+
Point,
100+
Region,
64101
straightTo: vi.fn((point) => point),
65102
centerOf: vi.fn((region) => region),
66103
randomPointIn: vi.fn((region) => region),

0 commit comments

Comments
 (0)