74 lines
5.6 KiB
JavaScript
74 lines
5.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { createServer } from "node:http";
|
|
import { chromium } from "playwright";
|
|
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
const version = JSON.parse(await fs.readFile(path.join(root,"extension/manifest.json"),"utf8")).version;
|
|
const artifacts = path.join(root, "test-artifacts");
|
|
await fs.mkdir(artifacts, { recursive: true });
|
|
const server = createServer(async (request, response) => {
|
|
try {
|
|
const url = new URL(request.url, "http://localhost");
|
|
if (!url.pathname.startsWith("/YouTubeDL/")) { response.writeHead(404).end(); return; }
|
|
const relative = decodeURIComponent(url.pathname.slice("/YouTubeDL/".length)) || "index.html";
|
|
const file = path.resolve(root,"site",relative);
|
|
if (!file.startsWith(path.resolve(root,"site") + path.sep)) { response.writeHead(400).end(); return; }
|
|
const type = {".html":"text/html; charset=utf-8",".js":"text/javascript",".css":"text/css",".png":"image/png",".svg":"image/svg+xml"}[path.extname(file)] || "application/octet-stream";
|
|
response.writeHead(200,{"Content-Type":type});response.end(await fs.readFile(file));
|
|
} catch { response.writeHead(404).end(); }
|
|
});
|
|
await new Promise(resolve => server.listen(0,"127.0.0.1",resolve));
|
|
const local = `http://127.0.0.1:${server.address().port}/YouTubeDL/`;
|
|
const target = process.env.YTDL_SITE_URL || local;
|
|
let browser;
|
|
try {
|
|
browser = await chromium.launch({ headless:true, executablePath:process.env.YTDL_TEST_BROWSER || "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" });
|
|
const context = await browser.newContext({ viewport:{width:1440,height:1050}, reducedMotion:"reduce", colorScheme:"light" });
|
|
const page = await context.newPage(), failures=[];
|
|
page.on("pageerror",error => failures.push(error.message));
|
|
page.on("response",response => { if (response.url().startsWith(target) && response.status() >= 400) failures.push(`${response.status()} ${response.url()}`); });
|
|
await page.goto(target,{waitUntil:"networkidle"});
|
|
assert.equal(await page.locator("h1").count(),1);
|
|
await page.locator(".hero .primary").click();
|
|
assert.equal(new URL(page.url()).hash,"#install");
|
|
assert.ok(await page.locator(".download-grid").isVisible());
|
|
assert.equal(await page.locator('[data-download="extension"]').getAttribute("href"),`https://git.lonarpgproxy.space/OwnerLink/YouTubeDL/releases/download/v${version}/YouTubeDL-${version}.zip`);
|
|
assert.equal(await page.locator('[data-download="helper"]').getAttribute("href"),"https://git.lonarpgproxy.space/OwnerLink/YouTubeDL/releases/download/v1.4.5/YouTubeDL-Helper-Setup.exe");
|
|
await page.locator('[data-screenshot="light"]').click();
|
|
await page.locator("#menu-screenshot").evaluate(image => image.decode());
|
|
assert.match(await page.locator("#menu-screenshot").getAttribute("src"), /menu-light\.png$/);
|
|
await page.locator('[data-screenshot="dark"]').click();
|
|
await page.locator("#menu-screenshot").evaluate(image => image.decode());
|
|
await page.locator("#theme-toggle").click();
|
|
assert.equal(await page.locator("html").getAttribute("data-theme"),"dark");
|
|
await page.reload({waitUntil:"networkidle"});
|
|
assert.equal(await page.locator("html").getAttribute("data-theme"),"dark");
|
|
await page.locator("#theme-toggle").click();
|
|
await page.evaluate(() => scrollTo(0,0));
|
|
await page.locator(".hero").screenshot({path:path.join(artifacts,"site-hero.png")});
|
|
await page.locator(".download-grid").screenshot({path:path.join(artifacts,"site-downloads.png")});
|
|
await page.screenshot({path:path.join(artifacts,"site-desktop.png"),fullPage:true});
|
|
for (const width of [320,390,768]) {
|
|
await page.setViewportSize({width,height:844});
|
|
await page.evaluate(() => scrollTo(0,0));
|
|
const overflow = await page.evaluate(() => [...document.querySelectorAll("body *")].filter(node => node.getBoundingClientRect().right > innerWidth + 1 && getComputedStyle(node).position !== "absolute").map(node => ({tag:node.tagName,class:node.className,right:Math.round(node.getBoundingClientRect().right)})));
|
|
if (await page.evaluate(() => document.documentElement.scrollWidth > innerWidth)) await page.screenshot({path:path.join(artifacts,`site-overflow-${width}.png`),fullPage:true});
|
|
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth),`No horizontal overflow at ${width}px: ${JSON.stringify(overflow)}`);
|
|
assert.ok(await page.locator("h1").isVisible());
|
|
await page.locator('nav a[href="#install"]').click();
|
|
assert.equal(new URL(page.url()).hash,"#install");
|
|
const summary = page.locator(".faqs summary").first();await summary.click();
|
|
assert.equal(await page.locator(".faqs details").first().getAttribute("open"),"");
|
|
await summary.click();
|
|
await page.evaluate(() => scrollTo(0,0));
|
|
if(width===390) await page.screenshot({path:path.join(artifacts,"site-mobile.png"),fullPage:true});
|
|
}
|
|
const images=await page.locator("img").evaluateAll(nodes => nodes.map(image=>({src:image.getAttribute("src"),okay:image.complete&&image.naturalWidth>0})));
|
|
assert.ok(images.every(image=>image.okay),JSON.stringify(images));
|
|
assert.deepEqual(failures,[]);
|
|
await fs.writeFile(path.join(artifacts,"site-check.json"),JSON.stringify({url:target,desktop:"passed",widths:[320,390,768],themes:"passed",screenshots:"passed",downloadLinks:"passed",faq:"passed",errors:failures},null,2));
|
|
console.log(`Site passed at ${target}: desktop/mobile, Pages subpath, download links, theme persistence, screenshots, FAQ and no console/HTTP errors.`);
|
|
} finally { if(browser)await browser.close();await new Promise(resolve=>server.close(resolve)); }
|