Files
adspower__adspower-browser/packages/local-api-mcp/tests/e2e-tool-validation/assertions/roundtrip/jsonExtractors.ts
T
Robin d0e643b283 fix(e2e): 放宽真实环境断言并修复 opened-browser 标识匹配
对真实 AdsPower 回包差异调整 E2E 校验口径,支持 get-opened-browser 使用 user_id 识别 profile,并将 Rule B 的 skipped/回读缺失场景从硬失败中剥离,确保全量高影响用例在 1 秒节流下稳定通过。
2026-04-16 16:40:08 +08:00

74 lines
2.1 KiB
TypeScript

function findJsonBoundary(text: string, start: number): number {
const open = text[start];
const close = open === '{' ? '}' : open === '[' ? ']' : '';
if (!close) {
throw new Error(`Unsupported JSON start token at index ${start}`);
}
let depth = 0;
let inString = false;
let escaped = false;
for (let i = start; i < text.length; i += 1) {
const ch = text[i];
if (inString) {
if (escaped) {
escaped = false;
continue;
}
if (ch === '\\') {
escaped = true;
continue;
}
if (ch === '"') {
inString = false;
}
continue;
}
if (ch === '"') {
inString = true;
continue;
}
if (ch === open) {
depth += 1;
continue;
}
if (ch === close) {
depth -= 1;
if (depth === 0) {
return i + 1;
}
}
}
throw new Error('JSON payload is incomplete or not closed');
}
export function extractJsonPayloadFromToolText(text: string): unknown {
const firstObject = text.indexOf('{');
const firstArray = text.indexOf('[');
const candidates = [firstObject, firstArray].filter((x) => x >= 0);
if (candidates.length === 0) {
throw new Error('No JSON payload found in tool text');
}
const start = Math.min(...candidates);
const end = findJsonBoundary(text, start);
const jsonText = text.slice(start, end).trim();
try {
return JSON.parse(jsonText);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to parse JSON payload: ${message}`);
}
}
export function extractJsonPayloadAfterPrefix(text: string, prefix: string): unknown {
const i = text.indexOf(prefix);
if (i < 0) {
throw new Error(`Prefix not found: ${prefix}`);
}
const sliced = text.slice(i + prefix.length).trim();
return extractJsonPayloadFromToolText(sliced);
}