174 lines
6.6 KiB
TypeScript
174 lines
6.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
function readSource(path: string) {
|
|
return readFileSync(resolve(process.cwd(), path), "utf8");
|
|
}
|
|
|
|
function readFunctionBlock(source: string, start: string, end: string) {
|
|
const startIndex = source.indexOf(start);
|
|
const endIndex = source.indexOf(end, startIndex + start.length);
|
|
|
|
expect(startIndex, `Missing ${start}`).toBeGreaterThanOrEqual(0);
|
|
expect(endIndex, `Missing ${end}`).toBeGreaterThan(startIndex);
|
|
return source.slice(startIndex, endIndex);
|
|
}
|
|
|
|
test.describe("home to canvas entry performance", () => {
|
|
test("navigates after project creation without waiting for a list refresh", () => {
|
|
const source = readSource("components/home/lovart-home.tsx");
|
|
const composerFlow = readFunctionBlock(
|
|
source,
|
|
"const createCanvasFromComposer = async () =>",
|
|
"const openNewCanvas = async () =>"
|
|
);
|
|
const blankCanvasFlow = readFunctionBlock(
|
|
source,
|
|
"const openNewCanvas = async () =>",
|
|
"const prepareCanvasProject = useCallback"
|
|
);
|
|
|
|
expect(composerFlow).toContain("prepareCanvasNavigation(canvasId)");
|
|
expect(composerFlow).toMatch(/router\.push\(`\/canvas\/\$\{canvasId\}`\)/);
|
|
expect(composerFlow).not.toContain("await mutateCanvas()");
|
|
|
|
expect(blankCanvasFlow).toContain('fetch("/api/canvas"');
|
|
expect(blankCanvasFlow).toMatch(/router\.push\(`\/canvas\/\$\{canvasId\}`\)/);
|
|
expect(blankCanvasFlow).not.toContain('router.push("/canvas?new=1")');
|
|
});
|
|
|
|
test("starts editor and chat downloads before the user opens the panel", () => {
|
|
const homeSource = readSource("components/home/lovart-home.tsx");
|
|
const editorSource = readSource(
|
|
"app/canvas/[id]/CanvasEditorLegacyPageClient.tsx"
|
|
);
|
|
const workspacePreloadSource = readSource(
|
|
"lib/client/canvas-workspace-preload.ts"
|
|
);
|
|
|
|
expect(homeSource).toContain("requestIdleCallback(preloadCanvasWorkspace");
|
|
expect(homeSource).toContain("timeout: 1200");
|
|
expect(homeSource).toContain(
|
|
"window.setTimeout(preloadCanvasWorkspace, 450)"
|
|
);
|
|
expect(workspacePreloadSource).toContain("preloadCanvasEditor()");
|
|
expect(workspacePreloadSource).toContain("preloadCanvasChatPanel()");
|
|
expect(editorSource).toContain("preloadCanvasChatPanel();");
|
|
expect(editorSource).toContain("preloadCanvasChatData(id);");
|
|
|
|
const entryWarmup = readFunctionBlock(
|
|
editorSource,
|
|
"useEffect(() => {\n preloadCanvasChatPanel();",
|
|
"const hydrateCanvasProject"
|
|
);
|
|
expect(entryWarmup).toContain(
|
|
'scheduleCanvasIdleTask(() => {\n void import("@/components/canvas/CanvasFileListPanel");'
|
|
);
|
|
expect(entryWarmup).not.toContain(
|
|
'import("@/components/canvas/CanvasChatPanel")'
|
|
);
|
|
});
|
|
|
|
test("reuses in-flight editor and chat module downloads", () => {
|
|
const editorLoader = readSource(
|
|
"app/canvas/[id]/canvas-editor-loader.ts"
|
|
);
|
|
const chatLoader = readSource(
|
|
"components/canvas/canvas-chat-panel-loader.ts"
|
|
);
|
|
|
|
expect(editorLoader).toContain(
|
|
'canvasEditorModulePromise ??= import("./CanvasEditorLegacyPageClient")'
|
|
);
|
|
expect(chatLoader).toContain(
|
|
'canvasChatPanelModulePromise ??= import("./CanvasChatPanel")'
|
|
);
|
|
});
|
|
|
|
test("keeps heavy 3d renderers out of the normal canvas workspace chunk", () => {
|
|
const workspaceSource = readSource("components/canvas/CanvasWorkspace.tsx");
|
|
|
|
expect(workspaceSource).not.toContain(
|
|
'import { Model3DNode } from "./nodes/Model3DNode"'
|
|
);
|
|
expect(workspaceSource).not.toContain(
|
|
'import { World3DNode } from "./nodes/World3DNode"'
|
|
);
|
|
expect(workspaceSource).toContain(
|
|
'model3DNodeModulePromise ??= import("./nodes/Model3DNode")'
|
|
);
|
|
expect(workspaceSource).toContain(
|
|
'world3DNodeModulePromise ??= import("./nodes/World3DNode")'
|
|
);
|
|
expect(workspaceSource).toContain("window.setTimeout(() => {");
|
|
expect(workspaceSource).toContain('document.visibilityState !== "visible"');
|
|
expect(workspaceSource).toContain("timeout: 15000");
|
|
expect(workspaceSource).toContain("}, 12000)");
|
|
});
|
|
|
|
test("keeps image editing tools out of the home and chat first-open path", () => {
|
|
const homeSource = readSource("components/home/lovart-home.tsx");
|
|
const imagePreviewSource = readSource("components/image-preview.tsx");
|
|
const imageAnnotatorSource = readSource("components/image-annotator.tsx");
|
|
const workspaceSource = readSource("components/canvas/CanvasWorkspace.tsx");
|
|
|
|
expect(homeSource).not.toContain(
|
|
'requestIdleCallback(preloadImageAnnotator'
|
|
);
|
|
expect(homeSource).not.toContain(
|
|
'window.setTimeout(preloadImageAnnotator'
|
|
);
|
|
expect(homeSource).toContain(
|
|
'import("@/components/image-annotator").then((module) => module.ImageAnnotator)'
|
|
);
|
|
|
|
expect(imagePreviewSource).not.toContain(
|
|
'import { ImageAnnotator } from "./image-annotator"'
|
|
);
|
|
expect(imagePreviewSource).not.toContain(
|
|
'import { AngleEditDialog } from "./angle-edit-dialog"'
|
|
);
|
|
expect(imagePreviewSource).toContain(
|
|
'import("./image-annotator").then((module) => module.ImageAnnotator)'
|
|
);
|
|
expect(imagePreviewSource).toContain(
|
|
'import("./angle-edit-dialog").then((module) => module.AngleEditDialog)'
|
|
);
|
|
|
|
expect(imageAnnotatorSource).not.toContain(
|
|
'import { AngleEditDialog } from "./angle-edit-dialog"'
|
|
);
|
|
expect(imageAnnotatorSource).not.toContain(
|
|
'import { SceneFusionView } from "./scene-fusion-view"'
|
|
);
|
|
expect(imageAnnotatorSource).toContain(
|
|
'import("./angle-edit-dialog").then((module) => module.AngleEditDialog)'
|
|
);
|
|
expect(imageAnnotatorSource).toContain(
|
|
'import("./scene-fusion-view").then((module) => module.SceneFusionView)'
|
|
);
|
|
|
|
expect(workspaceSource).not.toContain(
|
|
'import { ImageNode } from "./nodes/ImageNode"'
|
|
);
|
|
expect(workspaceSource).toContain(
|
|
'imageNodeModulePromise ??= import("./nodes/ImageNode")'
|
|
);
|
|
expect(workspaceSource).toContain("preloadCanvasImageNodeRenderer");
|
|
});
|
|
|
|
test("loads markdown response rendering only when message content needs it", () => {
|
|
const chatPanelSource = readSource("components/canvas/CanvasChatPanel.tsx");
|
|
|
|
expect(chatPanelSource).not.toContain(
|
|
'import { Response } from "@/components/elements/response"'
|
|
);
|
|
expect(chatPanelSource).toContain(
|
|
'await import("@/components/elements/response")'
|
|
);
|
|
expect(chatPanelSource).toContain("function MarkdownResponseFallback");
|
|
expect(chatPanelSource).toContain("<MarkdownResponse {...props} />");
|
|
});
|
|
});
|