Skip to content

Commit 858c72a

Browse files
authored
feat(web-integration): agent support override report name (#998)
1 parent 1abbcb2 commit 858c72a

File tree

4 files changed

+161
-23
lines changed

4 files changed

+161
-23
lines changed

packages/core/tests/unit-test/utils.test.ts

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import {
1313
safeParseJson,
1414
} from '@/ai-model/service-caller';
1515
import { getMidsceneRunSubDir } from '@midscene/shared/common';
16-
import { getAIConfig, overrideAIConfig } from '@midscene/shared/env';
16+
import {
17+
getAIConfig,
18+
overrideAIConfig,
19+
vlLocateMode,
20+
} from '@midscene/shared/env';
1721
import { describe, expect, it } from 'vitest';
1822
import { reportHTMLContent, writeDumpReport } from '../../dist/es/utils'; // use modules from dist, otherwise we will miss the template file
1923
import {
@@ -565,14 +569,17 @@ describe('search area', () => {
565569
{ left: 100, top: 100, width: 100, height: 100 },
566570
{ width: 1000, height: 1000 },
567571
);
568-
expect(result).toMatchInlineSnapshot(`
569-
{
570-
"height": 300,
571-
"left": 0,
572-
"top": 0,
573-
"width": 300,
574-
}
575-
`);
572+
573+
// Dynamic expectation based on vlLocateMode
574+
const isDoubaoVision = vlLocateMode() === 'doubao-vision';
575+
const expectedSize = isDoubaoVision ? 500 : 300;
576+
577+
expect(result).toEqual({
578+
height: expectedSize,
579+
left: 0,
580+
top: 0,
581+
width: expectedSize,
582+
});
576583
});
577584

578585
it('expandSearchArea with a big rect', () => {
@@ -595,14 +602,27 @@ describe('search area', () => {
595602
{ left: 951, top: 800, width: 50, height: 50 },
596603
{ width: 1000, height: 1000 },
597604
);
598-
expect(result).toMatchInlineSnapshot(`
599-
{
600-
"height": 300,
601-
"left": 826,
602-
"top": 675,
603-
"width": 174,
604-
}
605-
`);
605+
606+
// Dynamic expectation based on vlLocateMode
607+
const isDoubaoVision = vlLocateMode() === 'doubao-vision';
608+
609+
if (isDoubaoVision) {
610+
// minEdgeSize = 500, paddingSize = 225
611+
expect(result).toEqual({
612+
height: 425, // min(50 + 225*2, 1000 - 575) = min(500, 425) = 425
613+
left: 726, // max(0, 951 - 225) = 726
614+
top: 575, // max(0, 800 - 225) = 575
615+
width: 274, // min(50 + 225*2, 1000 - 726) = min(500, 274) = 274
616+
});
617+
} else {
618+
// minEdgeSize = 300, paddingSize = 125
619+
expect(result).toEqual({
620+
height: 300, // min(50 + 125*2, 1000 - 675) = min(300, 325) = 300
621+
left: 826, // max(0, 951 - 125) = 826
622+
top: 675, // max(0, 800 - 125) = 675
623+
width: 174, // min(50 + 125*2, 1000 - 826) = min(300, 174) = 174
624+
});
625+
}
606626
});
607627
});
608628

packages/web-integration/src/common/agent.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export interface PageAgentOpt {
9090
autoPrintReportMsg?: boolean;
9191
onTaskStartTip?: OnTaskStartTip;
9292
aiActionContext?: string;
93+
/* custom report file name */
94+
reportFileName?: string;
9395
}
9496

9597
export type WebPageAgentOpt = PageAgentOpt & WebPageOpt;
@@ -176,9 +178,9 @@ export class PageAgent<PageType extends WebPage = WebPage> {
176178
onTaskStart: this.callbackOnTaskStartTip.bind(this),
177179
});
178180
this.dump = this.resetDump();
179-
this.reportFileName = getReportFileName(
180-
opts?.testId || this.page.pageType || 'web',
181-
);
181+
this.reportFileName =
182+
opts?.reportFileName ||
183+
getReportFileName(opts?.testId || this.page.pageType || 'web');
182184
}
183185

184186
async getUIContext(action?: InsightAction): Promise<WebUIContext> {

packages/web-integration/tests/ai/web/static/static-page.test.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { join } from 'node:path';
33
import { StaticPageAgent } from '@/playground/agent';
44
import PlaygroundServer from '@/playground/server';
55
import StaticPage from '@/playground/static-page';
6-
import { describe, expect, it } from 'vitest';
6+
import { allConfigFromEnv } from '@midscene/shared/env';
7+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
78

89
const dumpFilePath = join(__dirname, '../../fixtures/ui-context.json');
910
const context = readFileSync(dumpFilePath, { encoding: 'utf-8' });
@@ -12,6 +13,16 @@ const contextJson = JSON.parse(context);
1213
describe(
1314
'static page agent',
1415
() => {
16+
let server: PlaygroundServer | null = null;
17+
18+
afterEach(() => {
19+
// Clean up server if it exists
20+
if (server) {
21+
server.close();
22+
server = null;
23+
}
24+
});
25+
1526
it('agent should work', async () => {
1627
const page = new StaticPage(contextJson);
1728

@@ -23,7 +34,17 @@ describe(
2334
});
2435

2536
it('server should work', async () => {
26-
const server = new PlaygroundServer(StaticPage, StaticPageAgent);
37+
// Save current config before creating server
38+
const currentConfig = (globalThis as any).midsceneGlobalConfig;
39+
40+
server = new PlaygroundServer(StaticPage, StaticPageAgent);
41+
42+
// Restore config after server creation in case it was cleared
43+
if (!(globalThis as any).midsceneGlobalConfig ||
44+
Object.keys((globalThis as any).midsceneGlobalConfig || {}).length === 0) {
45+
(globalThis as any).midsceneGlobalConfig = currentConfig || allConfigFromEnv();
46+
}
47+
2748
await server.launch();
2849

2950
const port = server.port;
@@ -46,7 +67,6 @@ describe(
4667
const data = await res.json();
4768
expect(data.result).toBeDefined();
4869
expect(data.error).toBeFalsy();
49-
server.close();
5070
});
5171
},
5272
{

packages/web-integration/tests/unit-test/agent.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,38 @@ vi.mock('@/common/plan-builder', () => ({
1111
buildPlans: vi.fn(),
1212
}));
1313

14+
// Mock only the necessary parts to avoid side effects
15+
vi.mock('@midscene/core/utils', () => ({
16+
writeLogFile: vi.fn(() => null),
17+
reportHTMLContent: vi.fn(() => ''),
18+
stringifyDumpData: vi.fn(() => '{}'),
19+
groupedActionDumpFileExt: '.json',
20+
}));
21+
22+
vi.mock('@midscene/shared/logger', () => ({
23+
getDebug: vi.fn(() => vi.fn()),
24+
logMsg: vi.fn(),
25+
}));
26+
27+
vi.mock('@midscene/core', async () => {
28+
const actual = await vi.importActual('@midscene/core');
29+
return {
30+
...actual,
31+
Insight: vi.fn().mockImplementation(() => ({})),
32+
};
33+
});
34+
35+
// Partial mock for utils - only mock the async functions that need mocking
36+
vi.mock('@/common/utils', async () => {
37+
const actual = await vi.importActual('@/common/utils');
38+
return {
39+
...actual,
40+
parseContextFromWebPage: vi.fn().mockResolvedValue({}),
41+
trimContextByViewport: vi.fn((execution) => execution),
42+
printReportMsg: vi.fn(),
43+
};
44+
});
45+
1446
// Mock page implementation
1547
const mockPage = {
1648
pageType: 'puppeteer',
@@ -19,6 +51,8 @@ const mockPage = {
1951
},
2052
screenshotBase64: vi.fn().mockResolvedValue('mock-screenshot'),
2153
evaluateJavaScript: vi.fn(),
54+
size: vi.fn().mockResolvedValue({ dpr: 1 }),
55+
destroy: vi.fn(),
2256
} as unknown as WebPage;
2357

2458
// Mock task executor
@@ -205,3 +239,65 @@ describe('PageAgent logContent', () => {
205239
expect(agent.dump.executions[0].tasks[0].log).toBeDefined();
206240
});
207241
});
242+
243+
describe('PageAgent reportFileName', () => {
244+
beforeEach(() => {
245+
vi.clearAllMocks();
246+
});
247+
248+
it('should use external reportFileName when provided', () => {
249+
const customReportName = 'my-custom-report-name';
250+
const agent = new PageAgent(mockPage, {
251+
reportFileName: customReportName,
252+
});
253+
254+
expect(agent.reportFileName).toBe(customReportName);
255+
});
256+
257+
it('should generate reportFileName when not provided', () => {
258+
const agent = new PageAgent(mockPage);
259+
260+
// The generated name should contain puppeteer and follow the pattern
261+
// Note: uuid() generates base-36 strings (0-9, a-z)
262+
expect(agent.reportFileName).toMatch(
263+
/puppeteer-\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-[a-z0-9]{8}/,
264+
);
265+
});
266+
267+
it('should use testId for generated reportFileName when provided', () => {
268+
const agent = new PageAgent(mockPage, {
269+
testId: 'test-123',
270+
});
271+
272+
// The generated name should contain test-123 and follow the pattern
273+
// Note: uuid() generates base-36 strings (0-9, a-z)
274+
expect(agent.reportFileName).toMatch(
275+
/test-123-\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-[a-z0-9]{8}/,
276+
);
277+
});
278+
279+
it('should prioritize external reportFileName over testId', () => {
280+
const customReportName = 'my-custom-report';
281+
const agent = new PageAgent(mockPage, {
282+
reportFileName: customReportName,
283+
testId: 'test-456',
284+
});
285+
286+
expect(agent.reportFileName).toBe(customReportName);
287+
});
288+
289+
it('should fallback to "web" when pageType is not available', () => {
290+
const mockPageWithoutType = {
291+
...mockPage,
292+
pageType: undefined,
293+
} as unknown as WebPage;
294+
295+
const agent = new PageAgent(mockPageWithoutType);
296+
297+
// The generated name should contain web and follow the pattern
298+
// Note: uuid() generates base-36 strings (0-9, a-z)
299+
expect(agent.reportFileName).toMatch(
300+
/web-\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-[a-z0-9]{8}/,
301+
);
302+
});
303+
});

0 commit comments

Comments
 (0)