import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import crypto from "node:crypto"; import { canonicalUrl, bytes, duration, progressText, preferenceOptions, stabilizeDownloadSpeed, checkHelperCompatibility } from "../extension/shared.js"; test("URLs accept watch and Shorts but reject arbitrary origins and credentials", () => { assert.equal(canonicalUrl("https://www.youtube.com/shorts/jNQXAC9IVRw"), "https://www.youtube.com/watch?v=jNQXAC9IVRw"); assert.equal(canonicalUrl("https://youtu.be/jNQXAC9IVRw?list=123"), "https://www.youtube.com/watch?v=jNQXAC9IVRw"); for (const url of ["https://youtube.com.evil/watch?v=jNQXAC9IVRw", "http://youtube.com/watch?v=jNQXAC9IVRw", "https://user@youtube.com/watch?v=jNQXAC9IVRw"]) assert.equal(canonicalUrl(url), null); }); test("progress labels distinguish download tracks and GPU conversion", () => { assert.equal(progressText({ stage: "downloading", track: "audio" }), "Скачивание · аудио"); assert.equal(progressText({ stage: "encoding", engine: "nvenc" }), "Перекодирование · NVIDIA NVENC"); assert.equal(progressText({ stage: "encoding", engine: "amf" }), "Перекодирование · AMD AMF"); assert.equal(progressText({ stage: "encoding", engine: "cpu" }), "Перекодирование · CPU"); assert.equal(progressText({ stage: "merging", engine: "copy" }), "Объединение дорожек · без перекодирования"); assert.equal(progressText({ stage: "remuxing", engine: "copy" }), "Подготовка файла · без перекодирования"); assert.equal(progressText({ stage: "finalizing", engine: "copy", message: "Финализация файла" }), "Финализация файла"); assert.equal(progressText({ stage: "audio", engine: "cpu" }), "Обработка аудио · CPU"); assert.equal(duration(3661), "1:01:01"); assert.equal(duration(61), "1:01"); assert.equal(bytes(0), ""); assert.match(bytes(1048576), /1 МБ/); }); test("saved quality adapts to another video's formats and never upscales", () => { const metadata = { videos: [{ id: "new-4k", height: 2160, fps: 60, codec: "av01" }, { id: "new-720", height: 720, fps: 30, codec: "avc1" }], audios: [{ id: "new-audio", language: "en" }] }; const preference = { folder: "D:\\Videos", videoId: "old-format", audioId: "old-audio", quality: { height: 1080, fps: 30, codec: "H.264" }, audioLanguage: "en", codec: "hevc", engine: "nvenc", videoContainer: "mkv" }; const result = preferenceOptions(metadata, preference); assert.equal(result.videoId, "new-720"); assert.equal(result.audioId, "new-audio"); assert.equal(result.codec, "hevc"); assert.equal(result.container, "mkv"); assert.equal(result.folder, "D:\\Videos"); assert.equal(preferenceOptions(metadata, { ...preference, videoId: "best" }).videoId, "best"); assert.equal(preferenceOptions(metadata, { ...preference, mode: "audio", audioContainer: "mp3" }).container, "mp3"); }); test("fixed extension ID matches the native host allowed origin", () => { const manifest = JSON.parse(fs.readFileSync(new URL("../extension/manifest.json", import.meta.url))); const digest = crypto.createHash("sha256").update(Buffer.from(manifest.key, "base64")).digest().subarray(0, 16).toString("hex"); const id = [...digest].map(character => "abcdefghijklmnop"[parseInt(character, 16)]).join(""); assert.equal(id, "ndnoeiaecgmjnjneekaeapeecmmjheli"); assert.ok(manifest.permissions.includes("nativeMessaging")); assert.ok(!manifest.host_permissions.includes("")); }); test("brief missing speed samples do not flicker, expire and reset between tracks", () => { const sample = { id: "speed", status: "running", stage: "downloading", track: "video", speed: 4.2 * 1048576 }; let previous = stabilizeDownloadSpeed(sample, null, 1000); for (const now of [1200, 1600, 2000, 2400]) { previous = stabilizeDownloadSpeed({ ...sample, speed: null }, previous, now); assert.equal(previous.speed, sample.speed); assert.equal(previous.speedUpdatedAt, 1000, 'Missing samples must not extend the lifetime of the last measurement'); } assert.equal(stabilizeDownloadSpeed({ ...sample, speed: null }, previous, 2500).speed, null); assert.equal(stabilizeDownloadSpeed({ ...sample, speed: 0 }, previous, 1200).speed, 0); assert.equal(stabilizeDownloadSpeed({ ...sample, track: "audio", speed: null }, previous, 1200).speed, null); assert.equal(stabilizeDownloadSpeed({ ...sample, stage: "merging", speed: "8x" }, previous, 1200).speedUpdatedAt, null); }); test("helper compatibility detects outdated, newer and unidentifiable versions in both directions", () => { assert.equal(checkHelperCompatibility({ version: "1.3.2" }, "1.4.5").compatible, true); assert.equal(checkHelperCompatibility({ bundle: { version: "1.3.2" } }, "1.4.5").compatible, true); assert.equal(checkHelperCompatibility({ version: "1.3.2.0" }, "1.4.5").compatible, true); for (const version of ["1.3.0", "1.2.10", undefined, "unknown"]) { const result = checkHelperCompatibility({ version }, "1.4.5"); assert.equal(result.compatible, false); assert.equal(result.update, "helper"); } for (const version of ["1.3.3", "1.3.20", "2.0.0"]) { const result = checkHelperCompatibility({ version }, "1.4.5"); assert.equal(result.compatible, false); assert.equal(result.update, "extension"); } assert.equal(checkHelperCompatibility({ version: "1.3.2" }, "1.4.2").update, "extension"); assert.equal(checkHelperCompatibility({ version: "1.3.2", minimumExtensionVersion: "1.4.6" }, "1.4.5").update, "extension"); });