"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const { createHash } = require("node:crypto"); const { mkdtemp, readFile, rm } = require("node:fs/promises"); const os = require("node:os"); const path = require("node:path"); const { inflateRawSync } = require("node:zlib"); const { ARCHIVE_FILES, normalizeVersion, packageExtension } = require("../scripts/package-extension.js"); function readZipEntries(archive) { const entries = new Map(); let offset = 0; while (archive.readUInt32LE(offset) === 0x04034b50) { const method = archive.readUInt16LE(offset + 8); const compressedSize = archive.readUInt32LE(offset + 18); const nameLength = archive.readUInt16LE(offset + 26); const extraLength = archive.readUInt16LE(offset + 28); const nameStart = offset + 30; const dataStart = nameStart + nameLength + extraLength; const name = archive.subarray(nameStart, nameStart + nameLength).toString("utf8"); const compressed = archive.subarray(dataStart, dataStart + compressedSize); assert.equal(method, 8, `${name} should use DEFLATE compression`); entries.set(name, inflateRawSync(compressed)); offset = dataStart + compressedSize; } assert.equal(archive.readUInt32LE(offset), 0x02014b50, "central directory is present"); return entries; } test("normalizes release tags and rejects versions Firefox cannot publish", () => { assert.equal(normalizeVersion("1.2.3"), "1.2.3"); assert.equal(normalizeVersion("v2.0"), "2.0"); assert.equal(normalizeVersion("V3"), "3"); for (const version of ["", "v", "1.2.3.4.5", "1.02", "1.0-beta", "1000000000"]) { assert.throws(() => normalizeVersion(version), /Invalid Mozilla extension version|required/); } }); test("builds a deterministic unsigned XPI with the release version and runtime files only", async (context) => { const rootDirectory = path.resolve(__dirname, ".."); const firstOutput = await mkdtemp(path.join(os.tmpdir(), "glagolify-package-")); const secondOutput = await mkdtemp(path.join(os.tmpdir(), "glagolify-package-")); context.after(async () => { await Promise.all([ rm(firstOutput, { recursive: true, force: true }), rm(secondOutput, { recursive: true, force: true }) ]); }); const first = await packageExtension({ rootDirectory, outputDirectory: firstOutput, version: "v9.8.7" }); const second = await packageExtension({ rootDirectory, outputDirectory: secondOutput, version: "9.8.7" }); const firstArchive = await readFile(first.archivePath); const secondArchive = await readFile(second.archivePath); const entries = readZipEntries(firstArchive); assert.equal(first.archiveName, "glagolify-9.8.7.xpi"); assert.deepEqual([...entries.keys()], [...ARCHIVE_FILES]); assert.equal(JSON.parse(entries.get("manifest.json")).version, "9.8.7"); assert.equal(entries.has("package.json"), false); assert.equal(entries.has("tests/package-extension.test.js"), false); assert.deepEqual(firstArchive, secondArchive); const digest = createHash("sha256").update(firstArchive).digest("hex"); assert.equal( await readFile(first.checksumPath, "utf8"), `${digest} glagolify-9.8.7.xpi\n` ); });