mirror of
https://github.com/web-infra-dev/midscene.git
synced 2025-07-09 01:51:24 +00:00

* docs(core): update blog * docs(blog): update workflow blog * docs(site): add details and pic * docs(site): update pic * docs(site): update pic 2 --------- Co-authored-by: quanruzhuoxiu <quanruzhuoxiu@gmail.com>
182 lines
4.9 KiB
TypeScript
182 lines
4.9 KiB
TypeScript
import { writeFileSync } from 'node:fs';
|
|
import {
|
|
MIDSCENE_MODEL_NAME,
|
|
type PlanningAIResponse,
|
|
type Rect,
|
|
getAIConfig,
|
|
plan,
|
|
} from '@midscene/core';
|
|
import { adaptBboxToRect } from '@midscene/core/ai-model';
|
|
import { sleep } from '@midscene/core/utils';
|
|
import { vlLocateMode } from '@midscene/shared/env';
|
|
import { saveBase64Image } from '@midscene/shared/img';
|
|
import dotenv from 'dotenv';
|
|
import { afterEach, describe, expect, test } from 'vitest';
|
|
import { TestResultCollector } from '../src/test-analyzer';
|
|
import { annotateRects, buildContext, getCases } from './util';
|
|
dotenv.config({
|
|
debug: true,
|
|
override: true,
|
|
});
|
|
|
|
if (process.env.MIDSCENE_EVALUATION_EXPECT_VL) {
|
|
expect(vlLocateMode()).toBeTruthy();
|
|
}
|
|
|
|
const failCaseThreshold = process.env.CI ? 2 : 0;
|
|
const testSources = ['todo'];
|
|
|
|
const vlMode = vlLocateMode();
|
|
|
|
describe.skipIf(vlMode)('ai planning - by element', () => {
|
|
testSources.forEach((source) => {
|
|
test(
|
|
`${source}: planning`,
|
|
async () => {
|
|
const { path: aiDataPath, content: cases } = await getCases(
|
|
source,
|
|
'planning',
|
|
);
|
|
|
|
const caseGroupName = aiDataPath.split('/').pop() || '';
|
|
|
|
const resultCollector = new TestResultCollector(
|
|
`${caseGroupName}-planning`,
|
|
getAIConfig(MIDSCENE_MODEL_NAME) || 'unspecified',
|
|
);
|
|
|
|
for (const [, testCase] of cases.testCases.entries()) {
|
|
const context = await buildContext(source);
|
|
|
|
const prompt = testCase.prompt;
|
|
const startTime = Date.now();
|
|
|
|
const res = await plan(prompt, {
|
|
context,
|
|
pageType: 'puppeteer',
|
|
});
|
|
|
|
if (process.env.UPDATE_ANSWER_DATA) {
|
|
testCase.response_planning = res;
|
|
writeFileSync(aiDataPath, JSON.stringify(cases, null, 2));
|
|
}
|
|
|
|
resultCollector.addResult(
|
|
caseGroupName,
|
|
testCase,
|
|
res,
|
|
Date.now() - startTime,
|
|
);
|
|
}
|
|
|
|
await resultCollector.printSummary();
|
|
await resultCollector.analyze(caseGroupName, failCaseThreshold);
|
|
await sleep(3 * 1000);
|
|
},
|
|
240 * 1000,
|
|
);
|
|
});
|
|
});
|
|
|
|
const vlCases = [
|
|
'todo-vl',
|
|
'aweme-login-vl',
|
|
'antd-form-vl',
|
|
'antd-tooltip-vl',
|
|
];
|
|
|
|
const resultCollector = new TestResultCollector(
|
|
'planning',
|
|
getAIConfig(MIDSCENE_MODEL_NAME) || 'unspecified',
|
|
);
|
|
|
|
afterEach(async () => {
|
|
await resultCollector.printSummary();
|
|
});
|
|
|
|
describe.skipIf(!vlMode)('ai planning - by coordinates', () => {
|
|
vlCases.forEach((source) => {
|
|
test(
|
|
`${source}: planning`,
|
|
async () => {
|
|
const { path: aiDataPath, content: cases } = await getCases(
|
|
source,
|
|
'planning',
|
|
);
|
|
|
|
const caseGroupName = aiDataPath.split('/').pop() || '';
|
|
|
|
const annotations: Array<{
|
|
indexId: number;
|
|
rect: Rect;
|
|
}> = [];
|
|
|
|
for (const [index, testCase] of cases.testCases.entries()) {
|
|
const context = await buildContext(source.replace('-vl', ''));
|
|
|
|
const prompt = testCase.prompt;
|
|
const startTime = Date.now();
|
|
|
|
let res: PlanningAIResponse | Error;
|
|
try {
|
|
res = await plan(prompt, {
|
|
log: testCase.log,
|
|
context,
|
|
actionContext: testCase.action_context,
|
|
pageType: 'puppeteer',
|
|
});
|
|
} catch (error) {
|
|
res = error as Error;
|
|
}
|
|
|
|
if (process.env.UPDATE_ANSWER_DATA) {
|
|
if (res instanceof Error) {
|
|
testCase.response_planning = {
|
|
error: res.message,
|
|
} as any;
|
|
} else {
|
|
testCase.response_planning = res;
|
|
if (res.action?.locate?.bbox) {
|
|
const indexId = index + 1;
|
|
testCase.response_rect = adaptBboxToRect(
|
|
res.action.locate.bbox,
|
|
context.size.width,
|
|
context.size.height,
|
|
);
|
|
testCase.annotation_index_id = indexId;
|
|
annotations.push({
|
|
indexId,
|
|
rect: testCase.response_rect,
|
|
});
|
|
}
|
|
}
|
|
writeFileSync(aiDataPath, JSON.stringify(cases, null, 2));
|
|
}
|
|
|
|
if (annotations.length > 0) {
|
|
const markedImage = await annotateRects(
|
|
context.screenshotBase64,
|
|
annotations.map((item) => item.rect),
|
|
);
|
|
await saveBase64Image({
|
|
base64Data: markedImage,
|
|
outputPath: `${aiDataPath}-planning-coordinates-annotated.png`,
|
|
});
|
|
}
|
|
|
|
resultCollector.addResult(
|
|
caseGroupName,
|
|
testCase,
|
|
res,
|
|
Date.now() - startTime,
|
|
);
|
|
}
|
|
|
|
await resultCollector.analyze(caseGroupName, failCaseThreshold);
|
|
await sleep(3 * 1000);
|
|
},
|
|
240 * 1000,
|
|
);
|
|
});
|
|
});
|