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", headless: true, args: [ "--enable-webgl", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", ], }); const errors = []; const report = {}; 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="thermal"]').click(); const signal = () => page.locator("#scene").getAttribute("data-thermal").then(JSON.parse); const pixels = () => page.evaluate(() => { const canvas = document.createElement("canvas"); canvas.width = 160; canvas.height = 100; const ctx = canvas.getContext("2d"); ctx.drawImage(document.querySelector("#scene canvas"), 0, 0, 160, 100); return [...ctx.getImageData(0, 0, 160, 100).data]; }); await page.waitForTimeout(300); const before = await pixels(); const start = await signal(); await page.waitForTimeout(1200); const after = await pixels(); report.changedPixels = after.filter((v, i) => v !== before[i]).length; assert.ok(report.changedPixels > 50); assert.notEqual((await signal()).fanAngle, start.fanAngle); await page.screenshot({ path: "artifacts/thermal-animation.png" }); await page.locator("#fan-target").fill("0"); await page.waitForTimeout(300); const stopped = (await signal()).fanAngle; await page.waitForTimeout(500); assert.equal((await signal()).fanAngle, stopped); await page.locator("#thermal-fault").click(); await page.waitForTimeout(300); report.disturbance = await signal(); assert.ok(report.disturbance.power > 80); assert.ok(report.disturbance.melt.width < 0.44); assert.ok(report.disturbance.melt.cold > 0.1); await page.screenshot({ path: "artifacts/melt-cold.png" }); await page.locator("#clear-fault").click(); await page.locator("#overheat-fault").click(); await page.waitForTimeout(400); report.overheat = await signal(); assert.ok(report.overheat.melt.spread > 1.2); assert.equal(report.overheat.power, 0); await page.screenshot({ path: "artifacts/melt-overheat.png" }); await page.locator("#clear-fault").click(); await page.locator("#vision-fault").click(); await page.waitForTimeout(2500); assert.equal((await signal()).vision, true); assert.equal((await signal()).feed, 75); await page.screenshot({ path: "artifacts/thermal-defect.png" }); await page.waitForFunction( () => !JSON.parse(document.querySelector("#scene").dataset.thermal).vision, null, { timeout: 15000 }, ); await page.locator("#pid").uncheck(); await page.locator("#vision-fault").click(); await page.waitForTimeout(8500); assert.equal((await signal()).vision, true); assert.equal((await signal()).power, 0); await page.locator("#clear-fault").click(); await page.locator("#thermal-pause").click(); await page.waitForTimeout(250); const paused = await signal(); await page.waitForTimeout(500); assert.equal((await signal()).travel, paused.travel); await page.locator("#thermal-pause").click(); await page.locator("#reset-thermal").click(); for (const [width, height] of [ [1920, 1080], [1366, 768], [1280, 720], [390, 844], ]) { await page.setViewportSize({ width, height }); await page.waitForTimeout(400); const layout = await page.evaluate(() => ({ width: document.documentElement.scrollWidth, height: document.documentElement.scrollHeight, })); assert.equal(layout.width, width); if (width > 1000) assert.equal(layout.height, height); const bounds = await page.evaluate(() => { const rect = (selector) => document.querySelector(selector).getBoundingClientRect().toJSON(); return { scene: rect("#scene"), analysis: rect(".melt-analysis"), loop: rect(".thermal-loop"), caption: rect(".thermal-mode .scene-mode-caption"), process: rect(".process"), controls: rect(".control-panel"), }; }); assert.ok(bounds.scene.bottom <= bounds.caption.top + 2); if (width > 740) assert.ok(bounds.scene.right <= bounds.analysis.left + 2); else assert.ok(bounds.caption.bottom <= bounds.analysis.top + 2); assert.ok(bounds.analysis.bottom <= bounds.loop.top + 2); assert.ok(bounds.loop.bottom <= bounds.process.bottom); if (width <= 740) assert.ok(bounds.process.bottom <= bounds.controls.top); for (const id of ["melt-section", "melt-inspection"]) { assert.ok( await page .locator(`#${id}`) .evaluate( (canvas) => new Set( canvas .getContext("2d") .getImageData(0, 0, canvas.width, canvas.height).data, ).size > 50, ), ); } assert.ok((await pixels()).some((v, i) => i % 4 !== 3 && v > 150)); await page.screenshot({ path: `artifacts/thermal-animation-${width}.png`, fullPage: true, }); } await page.locator('[data-technology="model"]').click(); assert.ok(await page.locator("#thermal-overlay").isHidden()); assert.deepEqual(errors, []); await writeFile( "artifacts/thermal-report.json", JSON.stringify({ ...report, errors }, null, 2), ); console.log(JSON.stringify({ ...report, errors }, null, 2)); } finally { await browser.close(); }