fix(qa-lab): keep bounded web snapshots UTF-16 safe (#104249)

* fix(qa-lab): keep bounded web snapshots UTF-16 safe

* fix(qa-lab): keep diagnostic truncation UTF-16 safe

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
xingzhou
2026-07-11 04:49:00 -07:00
committed by GitHub
co-authored by Peter Steinberger
parent d85e8a5f23
commit af80736057
2 changed files with 27 additions and 3 deletions
+24 -1
View File
@@ -13,6 +13,7 @@ const {
locatorFill,
locatorPress,
locatorWaitFor,
pageOn,
pageEvaluate,
pageTitle,
pageUrl,
@@ -33,6 +34,7 @@ const {
locatorFill: vi.fn(async () => undefined),
locatorPress: vi.fn(async () => undefined),
locatorWaitFor: vi.fn(async () => undefined),
pageOn: vi.fn(),
pageEvaluate: vi.fn(async () => "ok"),
pageTitle: vi.fn(async () => "QA"),
pageUrl: vi.fn(() => "http://127.0.0.1:3000/chat"),
@@ -67,7 +69,7 @@ import {
beforeEach(async () => {
const page = {
on: vi.fn(),
on: pageOn,
goto,
title: pageTitle,
url: pageUrl,
@@ -157,6 +159,27 @@ describe("qa web runtime", () => {
expect(browserClose).toHaveBeenCalledTimes(1);
});
it("keeps bounded web text on UTF-16 boundaries", async () => {
bodyLocator.textContent.mockResolvedValueOnce(`${"a".repeat(1999)}😀tail`);
const opened = await qaWebOpenPage({ url: "http://127.0.0.1:3000/chat" });
const consoleHandler = pageOn.mock.calls.find(([event]) => event === "console")?.[1] as
| ((message: { type: () => string; text: () => string }) => void)
| undefined;
if (!consoleHandler) {
throw new Error("expected console handler");
}
consoleHandler({
type: () => "log",
text: () => `${"a".repeat(1993)}😀tail`,
});
const snapshot = await qaWebSnapshot({ pageId: opened.pageId, maxChars: 2000 });
expect(snapshot.text).toBe("a".repeat(1999));
expect(snapshot.diagnostics).toEqual([{ kind: "console", text: `[log] ${"a".repeat(1993)}` }]);
await closeQaWebSessions();
});
it("launches an explicit Chromium executable override when configured", async () => {
vi.stubEnv("PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH", "/custom/chromium");
existsSync.mockImplementation((candidate) => candidate === "/custom/chromium");
+3 -2
View File
@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
import { randomUUID } from "node:crypto";
import { existsSync } from "node:fs";
import { resolvePositiveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import { chromium, type Browser, type BrowserContext, type Page } from "playwright-core";
type QaWebSession = {
@@ -69,7 +70,7 @@ const SYSTEM_CHROMIUM_EXECUTABLE_CANDIDATES = [
function appendDiagnostic(diagnostics: QaWebDiagnosticEntry[], entry: QaWebDiagnosticEntry): void {
diagnostics.push({
kind: entry.kind,
text: entry.text.slice(0, MAX_DIAGNOSTIC_TEXT_CHARS),
text: truncateUtf16Safe(entry.text, MAX_DIAGNOSTIC_TEXT_CHARS),
});
if (diagnostics.length > MAX_DIAGNOSTIC_ENTRIES) {
diagnostics.splice(0, diagnostics.length - MAX_DIAGNOSTIC_ENTRIES);
@@ -225,7 +226,7 @@ export async function qaWebSnapshot(params: QaWebSnapshotParams) {
return {
url: session.page.url(),
title: await session.page.title().catch(() => ""),
text: maxChars ? text.slice(0, maxChars) : text,
text: maxChars ? truncateUtf16Safe(text, maxChars) : text,
diagnostics: [...session.diagnostics],
};
}