362 lines
20 KiB
JavaScript
362 lines
20 KiB
JavaScript
#!/usr/bin/env node
|
|||
|
|
"use strict";
|
||
|
|
|
||
|
|
const assert = require("node:assert/strict");
|
||
|
|
const fs = require("node:fs");
|
||
|
|
const fsp = require("node:fs/promises");
|
||
|
|
const os = require("node:os");
|
||
|
|
const path = require("node:path");
|
||
|
|
const JSZip = require("jszip");
|
||
|
|
const XLSX = require("@e965/xlsx");
|
||
|
|
const { chromium } = require("playwright");
|
||
|
|
|
||
|
|
const ROOT = path.resolve(__dirname, "..");
|
||
|
|
const BASE_URL = (process.env.MNOTE_UI_BASE_URL || process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
|
||
|
|
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||
|
|
const OUT_DIR = path.join(ROOT, "tmp", "task501-office-preview-light-viewer-smoke");
|
||
|
|
const TEST_ACCOUNT = "mnote-e2e";
|
||
|
|
const TEST_EMAIL = "mnote.e2e@example.com";
|
||
|
|
const TEST_PASSWORD = process.env.MNOTE_E2E_PASSWORD || "MnoteE2E123!";
|
||
|
|
const WORKSPACE_ID = "local-ws:mnote-e2e:office-preview-smoke";
|
||
|
|
const DOCUMENT_ID = "local-md:office-preview-smoke.md";
|
||
|
|
|
||
|
|
function resolveChromiumExecutablePath() {
|
||
|
|
const explicit = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || "";
|
||
|
|
if (explicit && fs.existsSync(explicit)) return explicit;
|
||
|
|
return [
|
||
|
|
"/usr/bin/google-chrome-stable",
|
||
|
|
"/usr/bin/google-chrome",
|
||
|
|
"/snap/bin/chromium",
|
||
|
|
"/usr/bin/chromium",
|
||
|
|
"/usr/bin/chromium-browser",
|
||
|
|
].find((candidate) => fs.existsSync(candidate)) || "";
|
||
|
|
}
|
||
|
|
|
||
|
|
function officePreviewUrl(root, fileName, fileType) {
|
||
|
|
const fileUrl = new URL("/api/local-folder/files/open", BASE_URL);
|
||
|
|
fileUrl.searchParams.set("rootUri", `file://${root}`);
|
||
|
|
fileUrl.searchParams.set("path", fileName);
|
||
|
|
|
||
|
|
const url = new URL("/office-preview", BASE_URL);
|
||
|
|
url.searchParams.set("fileUrl", `${fileUrl.pathname}${fileUrl.search}`);
|
||
|
|
url.searchParams.set("fileName", fileName);
|
||
|
|
url.searchParams.set("fileType", fileType);
|
||
|
|
url.searchParams.set("workspaceId", WORKSPACE_ID);
|
||
|
|
url.searchParams.set("sourceKind", "local_folder");
|
||
|
|
url.searchParams.set("rootUri", `file://${root}`);
|
||
|
|
url.searchParams.set("documentId", DOCUMENT_ID);
|
||
|
|
return url.toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
async function writeDocx(filePath) {
|
||
|
|
const zip = new JSZip();
|
||
|
|
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
||
|
|
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
||
|
|
</Types>`);
|
||
|
|
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("word").file("document.xml", `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||
|
|
<w:body>
|
||
|
|
<w:p><w:r><w:t>MNote DOCX light preview works</w:t></w:r></w:p>
|
||
|
|
<w:p><w:r><w:t>第二行测试内容</w:t></w:r></w:p>
|
||
|
|
<w:sectPr/>
|
||
|
|
</w:body>
|
||
|
|
</w:document>`);
|
||
|
|
await fsp.writeFile(filePath, await zip.generateAsync({ type: "nodebuffer" }));
|
||
|
|
}
|
||
|
|
|
||
|
|
function writeXlsx(filePath) {
|
||
|
|
const workbook = XLSX.utils.book_new();
|
||
|
|
const sheet = XLSX.utils.aoa_to_sheet([
|
||
|
|
["Name", "Value"],
|
||
|
|
["MNote XLSX light preview works", "ok"],
|
||
|
|
["中文", "正常"],
|
||
|
|
]);
|
||
|
|
XLSX.utils.book_append_sheet(workbook, sheet, "Sheet1");
|
||
|
|
XLSX.writeFile(workbook, filePath);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function writePptx(filePath) {
|
||
|
|
const zip = new JSZip();
|
||
|
|
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
||
|
|
<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
||
|
|
<Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
|
||
|
|
<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
|
||
|
|
<Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
|
||
|
|
<Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
|
||
|
|
</Types>`);
|
||
|
|
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("ppt").file("presentation.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
||
|
|
<p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
|
||
|
|
<p:sldIdLst><p:sldId id="256" r:id="rId2"/></p:sldIdLst>
|
||
|
|
<p:sldSz cx="12192000" cy="6858000" type="wide"/>
|
||
|
|
<p:notesSz cx="6858000" cy="9144000"/>
|
||
|
|
<p:defaultTextStyle>
|
||
|
|
<a:defPPr><a:defRPr lang="zh-CN"/></a:defPPr>
|
||
|
|
<a:lvl1pPr marL="0" algn="l"><a:defRPr sz="1800" lang="zh-CN"/></a:lvl1pPr>
|
||
|
|
</p:defaultTextStyle>
|
||
|
|
</p:presentation>`);
|
||
|
|
zip.folder("ppt").folder("_rels").file("presentation.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
|
||
|
|
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>
|
||
|
|
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("ppt").folder("slideMasters").file("slideMaster1.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
||
|
|
<p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
|
||
|
|
<p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
|
||
|
|
</p:sldMaster>`);
|
||
|
|
zip.folder("ppt").folder("slideMasters").folder("_rels").file("slideMaster1.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("ppt").folder("slideLayouts").file("slideLayout1.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank">
|
||
|
|
<p:cSld name="Blank"><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
|
||
|
|
</p:sldLayout>`);
|
||
|
|
zip.folder("ppt").folder("slideLayouts").folder("_rels").file("slideLayout1.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("ppt").folder("slides").file("slide1.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
||
|
|
<p:cSld>
|
||
|
|
<p:spTree>
|
||
|
|
<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
||
|
|
<p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>
|
||
|
|
<p:sp>
|
||
|
|
<p:nvSpPr><p:cNvPr id="2" name="Title"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr>
|
||
|
|
<p:spPr><a:xfrm><a:off x="914400" y="914400"/><a:ext cx="10363200" cy="1371600"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr>
|
||
|
|
<p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="zh-CN" sz="4400"/><a:t>MNote PPTX light preview works</a:t></a:r></a:p></p:txBody>
|
||
|
|
</p:sp>
|
||
|
|
</p:spTree>
|
||
|
|
</p:cSld>
|
||
|
|
</p:sld>`);
|
||
|
|
zip.folder("ppt").folder("slides").folder("_rels").file("slide1.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
||
|
|
</Relationships>`);
|
||
|
|
zip.folder("ppt").folder("theme").file("theme1.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
|
|
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="MNote">
|
||
|
|
<a:themeElements>
|
||
|
|
<a:clrScheme name="MNote"><a:dk1><a:srgbClr val="000000"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F2937"/></a:dk2><a:lt2><a:srgbClr val="F8FAFC"/></a:lt2><a:accent1><a:srgbClr val="2563EB"/></a:accent1><a:accent2><a:srgbClr val="22A559"/></a:accent2><a:accent3><a:srgbClr val="E0525B"/></a:accent3><a:accent4><a:srgbClr val="F59E0B"/></a:accent4><a:accent5><a:srgbClr val="8B5CF6"/></a:accent5><a:accent6><a:srgbClr val="14B8A6"/></a:accent6><a:hlink><a:srgbClr val="2563EB"/></a:hlink><a:folHlink><a:srgbClr val="7C3AED"/></a:folHlink></a:clrScheme>
|
||
|
|
<a:fontScheme name="MNote"><a:majorFont><a:latin typeface="Arial"/></a:majorFont><a:minorFont><a:latin typeface="Arial"/></a:minorFont></a:fontScheme>
|
||
|
|
<a:fmtScheme name="MNote"><a:fillStyleLst/><a:lnStyleLst/><a:effectStyleLst/><a:bgFillStyleLst/></a:fmtScheme>
|
||
|
|
</a:themeElements>
|
||
|
|
</a:theme>`);
|
||
|
|
await fsp.writeFile(filePath, await zip.generateAsync({ type: "nodebuffer" }));
|
||
|
|
}
|
||
|
|
|
||
|
|
async function signIn(context) {
|
||
|
|
const payload = (params) => ({
|
||
|
|
action: "auth:signIn",
|
||
|
|
args: {
|
||
|
|
provider: "password",
|
||
|
|
params,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
let response = await context.request.fetch(`${BASE_URL}/api/auth`, {
|
||
|
|
method: "POST",
|
||
|
|
data: payload({
|
||
|
|
account: TEST_ACCOUNT,
|
||
|
|
password: TEST_PASSWORD,
|
||
|
|
flow: "signIn",
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
if (response.ok()) return;
|
||
|
|
response = await context.request.fetch(`${BASE_URL}/api/auth`, {
|
||
|
|
method: "POST",
|
||
|
|
data: payload({
|
||
|
|
email: TEST_EMAIL,
|
||
|
|
username: TEST_ACCOUNT,
|
||
|
|
name: TEST_ACCOUNT,
|
||
|
|
password: TEST_PASSWORD,
|
||
|
|
flow: "signUp",
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
assert(response.ok(), `测试账号登录失败: ${response.status()} ${await response.text()}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function setPageWidthPreferences(context, root, updates) {
|
||
|
|
const response = await context.request.fetch(`${BASE_URL}/api/ui/preferences`, {
|
||
|
|
method: "PUT",
|
||
|
|
data: {
|
||
|
|
documentId: DOCUMENT_ID,
|
||
|
|
workspaceId: WORKSPACE_ID,
|
||
|
|
sourceKind: "local_folder",
|
||
|
|
rootUri: `file://${root}`,
|
||
|
|
updates,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
assert(response.ok(), `页面宽度偏好写入失败: ${response.status()} ${await response.text()}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function verifyPreview(page, root, item) {
|
||
|
|
const consoleMessages = [];
|
||
|
|
page.on("console", (message) => {
|
||
|
|
consoleMessages.push(`${message.type()}: ${message.text()}`);
|
||
|
|
});
|
||
|
|
page.on("pageerror", (error) => {
|
||
|
|
consoleMessages.push(`pageerror: ${error && error.stack ? error.stack : error.message}`);
|
||
|
|
});
|
||
|
|
|
||
|
|
await page.goto(officePreviewUrl(root, item.fileName, item.fileType), {
|
||
|
|
waitUntil: "domcontentloaded",
|
||
|
|
timeout: UI_TIMEOUT_MS,
|
||
|
|
});
|
||
|
|
await page.waitForFunction(
|
||
|
|
() => {
|
||
|
|
const status = document.documentElement.getAttribute("data-mnote-office-preview-status");
|
||
|
|
return status && status !== "加载中";
|
||
|
|
},
|
||
|
|
null,
|
||
|
|
{ timeout: UI_TIMEOUT_MS },
|
||
|
|
);
|
||
|
|
const status = await page.locator("html").getAttribute("data-mnote-office-preview-status");
|
||
|
|
const text = await page.locator("#mnote-office-viewer").innerText({ timeout: UI_TIMEOUT_MS });
|
||
|
|
const layout = await page.evaluate(() => {
|
||
|
|
const viewer = document.querySelector("#mnote-office-viewer");
|
||
|
|
const tableWrap = document.querySelector(".mnote-office-table-wrap");
|
||
|
|
const docxPage = document.querySelector(".docx-wrapper > section.docx, .mnote-docx-wrapper > section.mnote-docx");
|
||
|
|
const pptxStage = document.querySelector(".mnote-office-pptx-stage");
|
||
|
|
const pptxSlide = document.querySelector(".pptx-preview-slide-wrapper");
|
||
|
|
const rectOf = (node) => {
|
||
|
|
if (!(node instanceof Element)) return null;
|
||
|
|
const rect = node.getBoundingClientRect();
|
||
|
|
return {
|
||
|
|
left: rect.left,
|
||
|
|
width: rect.width,
|
||
|
|
};
|
||
|
|
};
|
||
|
|
return {
|
||
|
|
viewportWidth: window.innerWidth,
|
||
|
|
pageWidthContentType: document.documentElement.getAttribute("data-page-width-content-type"),
|
||
|
|
pageWidthMode: document.documentElement.getAttribute("data-page-width-resolved-mode"),
|
||
|
|
toolbarCount: document.querySelectorAll(".mnote-office-toolbar").length,
|
||
|
|
viewer: rectOf(viewer),
|
||
|
|
tableWrap: rectOf(tableWrap),
|
||
|
|
docxPage: rectOf(docxPage),
|
||
|
|
pptxStage: rectOf(pptxStage),
|
||
|
|
pptxSlide: rectOf(pptxSlide),
|
||
|
|
};
|
||
|
|
});
|
||
|
|
const screenshot = path.join(OUT_DIR, `${item.fileType}.png`);
|
||
|
|
await page.screenshot({ path: screenshot, fullPage: true });
|
||
|
|
|
||
|
|
assert.equal(status, "完成", `${item.fileType} 预览状态应完成`);
|
||
|
|
assert(text.includes(item.expected), `${item.fileType} 预览应包含样本文本: ${item.expected}`);
|
||
|
|
assert.equal(consoleMessages.length, 0, `${item.fileType} 预览不应产生控制台错误: ${consoleMessages.join("\n")}`);
|
||
|
|
assert.equal(layout.toolbarCount, 0, "轻量预览页不应显示顶部状态条");
|
||
|
|
if (item.fileType === "docx") {
|
||
|
|
assert.equal(layout.pageWidthContentType, "word", "DOCX 应使用 Word 宽度类型");
|
||
|
|
assert.equal(layout.pageWidthMode, item.expectedMode || "wide", "DOCX 应使用指定页面宽度");
|
||
|
|
assert(layout.viewer && layout.viewer.width >= Math.min(layout.viewportWidth, 1180) - 24, "DOCX 预览容器应接近宽版宽度");
|
||
|
|
assert(layout.viewer.left > 0 && layout.viewer.left < 80, "DOCX 宽版容器应居中而不是左侧小块");
|
||
|
|
assert(layout.docxPage && layout.docxPage.width >= layout.viewportWidth * 0.75, "DOCX 页面应放大到接近可视宽度");
|
||
|
|
} else if (item.fileType === "pptx") {
|
||
|
|
assert.equal(layout.pageWidthContentType, "ppt", "PPTX 应使用 PPT 宽度类型");
|
||
|
|
assert.equal(layout.pageWidthMode, item.expectedMode || "wide", "PPTX 应使用指定页面宽度");
|
||
|
|
if ((item.expectedMode || "wide") === "full") {
|
||
|
|
assert(layout.viewer && layout.viewer.width >= layout.viewportWidth - 2, "PPTX 全宽模式应占满页面宽度");
|
||
|
|
assert(layout.pptxStage && layout.pptxStage.width >= layout.viewportWidth - 40, "PPTX 舞台应随全宽容器放大");
|
||
|
|
} else {
|
||
|
|
assert(layout.viewer && layout.viewer.width >= Math.min(layout.viewportWidth, 1180) - 24, "PPTX 预览容器应接近宽版宽度");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
assert.equal(layout.pageWidthContentType, "excel", `${item.fileType} 应使用 Excel 宽度类型`);
|
||
|
|
assert.equal(layout.pageWidthMode, item.expectedMode || "full", `${item.fileType} 应使用指定页面宽度`);
|
||
|
|
assert(layout.viewer && layout.viewer.left <= 1, `${item.fileType} 预览容器应从页面左侧开始`);
|
||
|
|
assert(layout.viewer.width >= layout.viewportWidth - 2, `${item.fileType} 预览容器应占满页面宽度`);
|
||
|
|
assert(layout.tableWrap && layout.tableWrap.width >= layout.viewportWidth - 40, `${item.fileType} 表格容器应接近全宽`);
|
||
|
|
}
|
||
|
|
return { fileType: item.fileType, status, text: text.slice(0, 300), layout, screenshot };
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
await fsp.mkdir(OUT_DIR, { recursive: true });
|
||
|
|
const root = await fsp.mkdtemp(path.join(os.tmpdir(), "mnote-office-preview-"));
|
||
|
|
await fsp.mkdir(path.join(root, ".mnote"), { recursive: true });
|
||
|
|
await fsp.writeFile(
|
||
|
|
path.join(root, ".mnote", "workspace.json"),
|
||
|
|
`${JSON.stringify({
|
||
|
|
workspaceId: WORKSPACE_ID,
|
||
|
|
ownerId: TEST_ACCOUNT,
|
||
|
|
createdAt: new Date().toISOString(),
|
||
|
|
capabilities: ["local_files", "markdown_edit", "asset_upload"],
|
||
|
|
}, null, 2)}\n`,
|
||
|
|
"utf8",
|
||
|
|
);
|
||
|
|
await writeDocx(path.join(root, "sample.docx"));
|
||
|
|
writeXlsx(path.join(root, "sample.xlsx"));
|
||
|
|
await writePptx(path.join(root, "sample.pptx"));
|
||
|
|
await fsp.writeFile(
|
||
|
|
path.join(root, "sample.csv"),
|
||
|
|
"Name,Value\nMNote CSV light preview works,ok\n中文,正常\n",
|
||
|
|
"utf8",
|
||
|
|
);
|
||
|
|
|
||
|
|
const executablePath = resolveChromiumExecutablePath();
|
||
|
|
const browser = await chromium.launch({
|
||
|
|
headless: process.env.HEADFUL !== "1",
|
||
|
|
...(executablePath ? { executablePath } : {}),
|
||
|
|
});
|
||
|
|
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||
|
|
try {
|
||
|
|
await signIn(context);
|
||
|
|
await setPageWidthPreferences(context, root, {
|
||
|
|
"pageWidth.word": { mode: "wide", custom: null },
|
||
|
|
"pageWidth.excel": { mode: "full", custom: null },
|
||
|
|
"pageWidth.ppt": { mode: "wide", custom: null },
|
||
|
|
});
|
||
|
|
const page = await context.newPage();
|
||
|
|
const results = [];
|
||
|
|
for (const item of [
|
||
|
|
{ fileName: "sample.docx", fileType: "docx", expected: "MNote DOCX light preview works" },
|
||
|
|
{ fileName: "sample.xlsx", fileType: "xlsx", expected: "MNote XLSX light preview works" },
|
||
|
|
{ fileName: "sample.pptx", fileType: "pptx", expected: "MNote PPTX light preview works" },
|
||
|
|
{ fileName: "sample.csv", fileType: "csv", expected: "MNote CSV light preview works" },
|
||
|
|
]) {
|
||
|
|
results.push(await verifyPreview(page, root, item));
|
||
|
|
}
|
||
|
|
await setPageWidthPreferences(context, root, {
|
||
|
|
"pageWidth.ppt": { mode: "full", custom: null },
|
||
|
|
});
|
||
|
|
results.push(await verifyPreview(page, root, {
|
||
|
|
fileName: "sample.pptx",
|
||
|
|
fileType: "pptx",
|
||
|
|
expected: "MNote PPTX light preview works",
|
||
|
|
expectedMode: "full",
|
||
|
|
}));
|
||
|
|
const result = { ok: true, baseUrl: BASE_URL, root, results };
|
||
|
|
await fsp.writeFile(path.join(OUT_DIR, "result.json"), `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
||
|
|
console.log(JSON.stringify(result, null, 2));
|
||
|
|
} finally {
|
||
|
|
await browser.close();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(async (error) => {
|
||
|
|
await fsp.mkdir(OUT_DIR, { recursive: true });
|
||
|
|
await fsp.writeFile(
|
||
|
|
path.join(OUT_DIR, "result.json"),
|
||
|
|
`${JSON.stringify({ ok: false, baseUrl: BASE_URL, error: error && error.stack ? error.stack : String(error) }, null, 2)}\n`,
|
||
|
|
"utf8",
|
||
|
|
);
|
||
|
|
console.error(error && error.stack ? error.stack : error);
|
||
|
|
process.exit(1);
|
||
|
|
});
|