Files
YouTubeDL/tests/site.test.mjs
T
DepressedCat 5f2aad00f4
Check extension and helper / check (push) Canceled after 0s
Publish YouTubeDL 1.4.5 and website on Gitea
2026-10-10 23:53:17 +03:00

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)); }