Files
CloakBrowser/js/tests/update.test.ts
T
Cloak-HQ 0719f750ef test: add comprehensive unit tests for all public APIs
Python (75 new tests):
- launch_context(): viewport, timezone bypass, geoip, close cleanup, error cleanup
- launch_persistent_context(): sync + async, args, proxy, close/pw.stop()
- config: binary paths, archive names, cache dir, stealth args profiles
- extract: tar/zip with path traversal protection, .app bundle preservation
- ensure_binary(), clear_cache(), check_for_update(), version markers
- geoip: private IP detection

JavaScript (26 new tests):
- puppeteer wrapper: stealth args, proxy string/dict, auth monkey-patch
- launchContext/launchPersistentContext: viewport, timezone, proxy, close
- ensureBinary, clearCache, checkForUpdate, archive helpers

Total: 169 Python + 88 JS tests (was 59 + 47)
2026-03-05 03:02:46 +01:00

328 lines
9.7 KiB
TypeScript

import { describe, it, expect, vi, afterEach, beforeEach } from "vitest";
import {
CHROMIUM_VERSION,
getChromiumVersion,
getDownloadUrl,
getEffectiveVersion,
getPlatformTag,
parseVersion,
versionNewer,
} from "../src/config.js";
import {
binaryInfo,
checkForUpdate,
checkWrapperUpdate,
clearCache,
ensureBinary,
getLatestChromiumVersion,
parseChecksums,
resetWrapperUpdateChecked,
} from "../src/download.js";
describe("version comparison", () => {
it("parseVersion handles 4-part versions", () => {
expect(parseVersion("145.0.7718.0")).toEqual([145, 0, 7718, 0]);
expect(parseVersion("142.0.7444.175")).toEqual([142, 0, 7444, 175]);
});
it("detects newer version", () => {
expect(versionNewer("145.0.7718.0", "142.0.7444.175")).toBe(true);
});
it("detects older version", () => {
expect(versionNewer("142.0.7444.175", "145.0.7718.0")).toBe(false);
});
it("same version is not newer", () => {
expect(versionNewer("142.0.7444.175", "142.0.7444.175")).toBe(false);
});
it("patch bump detected", () => {
expect(versionNewer("142.0.7444.176", "142.0.7444.175")).toBe(true);
});
it("major bump wins over minor", () => {
expect(versionNewer("143.0.0.0", "142.9.9999.999")).toBe(true);
});
it("parseVersion handles 5-part build numbers", () => {
expect(parseVersion("145.0.7632.109.2")).toEqual([145, 0, 7632, 109, 2]);
});
it("build bump detected", () => {
expect(versionNewer("145.0.7632.109.3", "145.0.7632.109.2")).toBe(true);
});
it("build suffix newer than no suffix", () => {
expect(versionNewer("145.0.7632.109.2", "145.0.7632.109")).toBe(true);
});
it("no suffix older than build suffix", () => {
expect(versionNewer("145.0.7632.109", "145.0.7632.109.2")).toBe(false);
});
});
describe("download URL", () => {
it("uses chromium-v prefix and cloakbrowser repo", () => {
const url = getDownloadUrl();
expect(url).toContain("cloakbrowser.dev");
expect(url).toContain(`chromium-v${getChromiumVersion()}`);
expect(url.endsWith(".tar.gz")).toBe(true);
});
it("accepts custom version", () => {
const url = getDownloadUrl("145.0.7718.0");
expect(url).toContain("chromium-v145.0.7718.0");
});
it("does not reference old repo", () => {
const url = getDownloadUrl();
expect(url).not.toContain("chromium-stealth-builds");
});
});
describe("latest version (platform-aware)", () => {
const platformTarball = `cloakbrowser-${getPlatformTag()}.tar.gz`;
function makeAssets(platforms: string[]) {
return platforms.map((p) => ({ name: `cloakbrowser-${p}.tar.gz` }));
}
function mockFetch(releases: Array<Record<string, unknown>>) {
return vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => releases,
} as Response);
}
afterEach(() => {
vi.restoreAllMocks();
});
it("returns version when release has platform asset", async () => {
mockFetch([
{
tag_name: "chromium-v145.0.7718.0",
draft: false,
assets: makeAssets(["linux-x64", "darwin-arm64", "darwin-x64", "windows-x64"]),
},
]);
expect(await getLatestChromiumVersion()).toBe("145.0.7718.0");
});
it("skips release without platform asset", async () => {
const spy = mockFetch([
{
tag_name: "chromium-v145.0.7718.0",
draft: false,
assets: makeAssets(["linux-x64"]), // Linux only
},
{
tag_name: "chromium-v142.0.7444.175",
draft: false,
assets: makeAssets(["linux-x64", "darwin-arm64", "darwin-x64", "windows-x64"]),
},
]);
const result = await getLatestChromiumVersion();
const tag = getPlatformTag();
if (tag === "linux-x64") {
expect(result).toBe("145.0.7718.0");
} else {
expect(result).toBe("142.0.7444.175");
}
});
it("returns null when no release has platform asset", async () => {
mockFetch([
{
tag_name: "chromium-v145.0.7718.0",
draft: false,
assets: [{ name: "cloakbrowser-freebsd-x64.tar.gz" }],
},
]);
expect(await getLatestChromiumVersion()).toBeNull();
});
it("skips draft releases", async () => {
const all = ["linux-x64", "darwin-arm64", "darwin-x64", "windows-x64"];
mockFetch([
{ tag_name: "chromium-v999.0.0.0", draft: true, assets: makeAssets(all) },
{ tag_name: "chromium-v145.0.7718.0", draft: false, assets: makeAssets(all) },
]);
expect(await getLatestChromiumVersion()).toBe("145.0.7718.0");
});
it("returns null on network error", async () => {
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("timeout"));
expect(await getLatestChromiumVersion()).toBeNull();
});
});
describe("wrapper update check", () => {
beforeEach(() => {
resetWrapperUpdateChecked();
delete process.env.CLOAKBROWSER_AUTO_UPDATE;
delete process.env.CLOAKBROWSER_DOWNLOAD_URL;
});
afterEach(() => {
vi.restoreAllMocks();
delete process.env.CLOAKBROWSER_AUTO_UPDATE;
delete process.env.CLOAKBROWSER_DOWNLOAD_URL;
});
it("warns when newer version available", async () => {
const spy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => ({ version: "99.0.0" }),
} as Response);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
await checkWrapperUpdate();
expect(spy).toHaveBeenCalledOnce();
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Update available"));
});
it("silent when current version", async () => {
const { WRAPPER_VERSION } = await import("../src/config.js");
vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => ({ version: WRAPPER_VERSION }),
} as Response);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
await checkWrapperUpdate();
expect(warnSpy).not.toHaveBeenCalled();
});
it("disabled by CLOAKBROWSER_AUTO_UPDATE=false", async () => {
process.env.CLOAKBROWSER_AUTO_UPDATE = "false";
const spy = vi.spyOn(globalThis, "fetch");
await checkWrapperUpdate();
expect(spy).not.toHaveBeenCalled();
});
it("disabled by CLOAKBROWSER_DOWNLOAD_URL", async () => {
process.env.CLOAKBROWSER_DOWNLOAD_URL = "https://mirror.example.com";
const spy = vi.spyOn(globalThis, "fetch");
await checkWrapperUpdate();
expect(spy).not.toHaveBeenCalled();
});
it("silent on network error", async () => {
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("timeout"));
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
await checkWrapperUpdate();
expect(warnSpy).not.toHaveBeenCalled();
});
it("runs only once per process", async () => {
const spy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => ({ version: "0.0.1" }),
} as Response);
await checkWrapperUpdate();
await checkWrapperUpdate();
expect(spy).toHaveBeenCalledOnce();
});
});
describe("parseChecksums", () => {
// Valid 64-char hex strings for testing
const HASH_A = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
const HASH_B = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
it("parses standard SHA256SUMS format", () => {
const text = [
`${HASH_A} cloakbrowser-linux-x64.tar.gz`,
`${HASH_B} cloakbrowser-darwin-arm64.tar.gz`,
].join("\n");
const result = parseChecksums(text);
expect(result.get("cloakbrowser-linux-x64.tar.gz")).toBe(HASH_A);
expect(result.get("cloakbrowser-darwin-arm64.tar.gz")).toBe(HASH_B);
});
it("handles binary-mode asterisk prefix", () => {
const text = `${HASH_A} *cloakbrowser-linux-x64.tar.gz`;
const result = parseChecksums(text);
expect(result.has("cloakbrowser-linux-x64.tar.gz")).toBe(true);
});
it("skips empty lines", () => {
const text = `\n\n${HASH_A} file.tar.gz\n\n`;
expect(parseChecksums(text).size).toBe(1);
});
it("returns empty map for empty input", () => {
expect(parseChecksums("").size).toBe(0);
expect(parseChecksums(" \n \n").size).toBe(0);
});
});
describe("effective version", () => {
it("returns platform version when no marker exists", () => {
// Default behavior — no marker file in test environment
expect(getEffectiveVersion()).toBe(getChromiumVersion());
});
});
describe("ensureBinary", () => {
afterEach(() => {
delete process.env.CLOAKBROWSER_BINARY_PATH;
});
it("returns local override when set", async () => {
// Use this test file as a "binary" that exists
process.env.CLOAKBROWSER_BINARY_PATH = __filename;
const result = await ensureBinary();
expect(result).toBe(__filename);
});
it("throws when local override path missing", async () => {
process.env.CLOAKBROWSER_BINARY_PATH = "/nonexistent/chrome";
await expect(ensureBinary()).rejects.toThrow("does not exist");
});
});
describe("clearCache", () => {
it("does not throw when cache dir missing", () => {
const orig = process.env.CLOAKBROWSER_CACHE_DIR;
process.env.CLOAKBROWSER_CACHE_DIR = "/tmp/cloakbrowser-test-nonexistent";
expect(() => clearCache()).not.toThrow();
if (orig) {
process.env.CLOAKBROWSER_CACHE_DIR = orig;
} else {
delete process.env.CLOAKBROWSER_CACHE_DIR;
}
});
});
describe("checkForUpdate", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("returns null when no newer version", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => [],
} as Response);
expect(await checkForUpdate()).toBeNull();
});
it("returns null on network error", async () => {
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("timeout"));
expect(await checkForUpdate()).toBeNull();
});
});