fdm/scripts/verify-nozzle.js

165 lines
5.6 KiB
JavaScript

import { chromium } from "@playwright/test";
import assert from "node:assert/strict";
import { writeFile } from "node:fs/promises";
const url = process.env.FDM_URL || "http://127.0.0.1:4178";
assert.ok((await fetch(url)).ok);
const browser = await chromium.launch({
executablePath:
"C:/Users/ikun/AppData/Local/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-win64/chrome-headless-shell.exe",
args: [
"--enable-webgl",
"--use-angle=swiftshader",
"--enable-unsafe-swiftshader",
],
});
const errors = [],
reports = [];
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
});
page.on("pageerror", (error) => errors.push(error.message));
const cdp = await page.context().newCDPSession(page);
await cdp.send("Network.enable");
await cdp.send("Network.setCacheDisabled", { cacheDisabled: true });
await page.goto(url);
await page.waitForSelector("#loading[hidden]", {
state: "attached",
timeout: 60000,
});
await page.locator('[data-technology="nozzle"]').click();
const pixels = () => {
const canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext("2d");
ctx.drawImage(document.querySelector("#scene > canvas"), 0, 0, 100, 100);
return [...ctx.getImageData(0, 0, 100, 100).data];
};
await page.waitForTimeout(300);
const assembled = await page.evaluate(pixels);
await page.locator("#disassemble").click();
await page.waitForFunction(
() =>
Number(document.querySelector("#scene").dataset.nozzleExplode) > 0.995,
);
assert.ok(
(await page.evaluate(pixels)).filter((value, i) => value !== assembled[i])
.length > 100,
);
assert.equal(await page.locator(".nozzle-label").count(), 12);
for (let i = 0; i < 12; i++) {
await page.locator(".nozzle-label").nth(i).click();
assert.equal(await page.locator("#nozzle-module").inputValue(), String(i));
assert.equal(
await page.locator("#scene").getAttribute("data-nozzle-selected"),
String(i),
);
assert.ok(
(await page.locator("#nozzle-part-detail").textContent()).length > 20,
);
}
await page.locator("#nozzle-module").selectOption("4");
for (const [width, height] of [
[1440, 900],
[1280, 720],
[1920, 1080],
[2048, 990],
[2560, 1238],
[1366, 768],
[390, 844],
]) {
await page.setViewportSize({ width, height });
await page.waitForTimeout(500);
const layout = await page.evaluate(() => {
const rect = (el) => el.getBoundingClientRect().toJSON();
return {
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight,
scene: rect(document.querySelector("#scene")),
parts: JSON.parse(document.querySelector("#scene").dataset.nozzleBounds),
labels: [...document.querySelectorAll(".nozzle-label")].map(rect),
panel: rect(document.querySelector('[data-panel="nozzle"]')),
foot: rect(
document.querySelector('[data-panel="nozzle"] .tech-footnote'),
),
};
});
assert.ok(layout.width <= width);
assert.equal(layout.parts.length, 12);
layout.parts.forEach((part, index) => {
assert.ok(part.top >= 20 && part.bottom <= layout.scene.height - 28,
`${width} 部件 ${index + 1} 上下未裁切:${JSON.stringify(part)}`);
const inset = (layout.scene.width < 550 ? 90 : 144) + 8;
assert.ok(part.left >= inset && part.right <= layout.scene.width - inset,
`${width} 部件 ${index + 1} 不被标签遮挡`);
});
if (width >= 1100) {
assert.ok(layout.height <= height);
assert.ok(layout.foot.bottom <= layout.panel.bottom + 1);
}
layout.labels.forEach((r, i) => {
assert.ok(r.top >= layout.scene.top && r.bottom <= layout.scene.bottom);
layout.labels
.slice(i + 1)
.forEach((other) =>
assert.ok(
r.right <= other.left ||
r.left >= other.right ||
r.bottom <= other.top ||
r.top >= other.bottom,
),
);
});
const image = await page.evaluate(pixels);
assert.ok(new Set(image).size > 40);
await page.screenshot({
path: `artifacts/nozzle-detailed-${width}.png`,
fullPage: true,
});
reports.push({
width,
height,
scene: layout.scene,
labels: layout.labels.length,
parts: layout.parts,
});
}
await page.setViewportSize({ width: 1440, height: 900 });
const sceneRect = await page.locator("#scene").boundingBox();
const beforeDrag = await page.evaluate(pixels);
await page.mouse.move(
sceneRect.x + sceneRect.width / 2,
sceneRect.y + sceneRect.height / 2,
);
await page.mouse.down();
await page.mouse.move(
sceneRect.x + sceneRect.width / 2 + 80,
sceneRect.y + sceneRect.height / 2 + 15,
{ steps: 12 },
);
await page.mouse.up();
await page.waitForTimeout(600);
assert.ok(
(await page.evaluate(pixels)).some((value, i) => value !== beforeDrag[i]),
);
await page.locator("#assemble").click();
await page.waitForFunction(
() =>
Number(document.querySelector("#scene").dataset.nozzleExplode) < 0.005,
);
await page.screenshot({ path: "artifacts/nozzle-assembled.png" });
await page.locator('[data-technology="model"]').click();
assert.ok(await page.locator(".nozzle-annotations").isHidden());
assert.ok(await page.locator(".temperature").isVisible());
assert.deepEqual(errors, []);
await writeFile(
"artifacts/nozzle-report.json",
JSON.stringify({ reports, errors }, null, 2),
);
console.log(JSON.stringify({ reports, errors }, null, 2));
} finally {
await browser.close();
}