#!/usr/bin/env node
/**
* Plain-node test suite for src/pack-shared.js (no DOM, no deps).
* Usage: node tools/test-pack-shared.js
*/
const assert = require("assert");
const pack = require("../src/pack-shared.js");
const detect = require("../src/field-detect.js");
let passed = 0;
let failed = 0;
function check(name, actual, expected) {
try {
assert.deepStrictEqual(actual, expected);
passed++;
} catch (e) {
failed++;
console.error(`FAIL ${name}\n expected: ${JSON.stringify(e.expected)}\n actual: ${JSON.stringify(e.actual)}`);
}
}
// --- resolveConfig: whitelist + clamps --------------------------------------
{
const cfg = pack.resolveConfig({
timings: { scanDebounceMs: 900, cardExitMs: 10, unknown: 1 },
strings: { use: " Apply ", dismiss: "", selectAccount: "Чей аккаунт?" },
unknownTop: "junk",
});
check("timings clamped", cfg.timings, { scanDebounceMs: 900, cardExitMs: 50 });
check("strings sanitized", cfg.strings, {
use: "Apply", dismiss: "Dismiss", selectAccount: "Чей аккаунт?",
});
check("defaults on junk config", pack.resolveConfig("not an object").timings, { scanDebounceMs: 300, cardExitMs: 280 });
check("junk string falls back", pack.resolveConfig({ strings: { use: 42 } }).strings.use, "Use");
check("oversized string falls back", pack.resolveConfig({ strings: { use: "x".repeat(61) } }).strings.use.length, "Use".length);
}
// --- resolveTables: additive merge over bundled defaults ---------------------
{
const bundled = detect.keywordTables;
const merged = pack.resolveTables({
usernameKeywords: [["user_name", 9], ["brandlogin", 7], [null, 1], ["x*y", 3], ["brandlogin", 6]],
passwordKeywords: [["пароль2", 8]],
antiKeywords: ["brandotp"],
autocompleteTokens: { brandlogin: { username: 9, password: 0 }, "cc_": { username: 5 } },
unknownKey: true,
}, detect.keywordTables);
const username = Object.fromEntries(merged.usernameKeywords);
check("existing keyword reweighted", username["user_name"], 9);
check("new keyword added", username["brandlogin"], 6);
check("invalid keyword dropped", username["x*y"], undefined);
check("new russian keyword added", Object.fromEntries(merged.passwordKeywords)["пароль2"], 8);
check("anti set-union", merged.antiKeywords.includes("brandotp") && merged.antiKeywords.includes("cc"), true);
check("token replaced", merged.autocompleteTokens.brandlogin, { username: 9, password: 0 });
check("token with partial weights completed", merged.autocompleteTokens["cc_"], { username: 5, password: 0 });
check("bundled password rows preserved", Object.fromEntries(merged.passwordKeywords)["password"], 8);
const snapshot = detect.keywordTables;
pack.resolveTables({ usernameKeywords: [["zz", 1]] });
check("bundled tables stay frozen-ish", JSON.stringify(detect.keywordTables), JSON.stringify(snapshot));
}
// --- isCompatible ------------------------------------------------------------
check("own newer than pack min", pack.isCompatible("0.1.1", "0.2.0"), true);
check("own equals pack min", pack.isCompatible("0.1.1", "0.1.1"), true);
check("own older than pack min", pack.isCompatible("0.2.0", "0.1.1"), false);
check("partial versions compare", pack.isCompatible("0.2", "0.1.9"), false);
check("junk stays incompatible", pack.isCompatible("latest", "0.2.0"), false);
// --- validateStoredPack ------------------------------------------------------
const validStore = {
packVersion: 2,
minExtensionVersion: "0.1.1",
maxExtensionVersion: null,
files: { "content.css": { sha256: "a".repeat(64), size: 100 } },
texts: { "content.css": ".a{color:red}" },
};
check("valid store accepted", (pack.validateStoredPack(validStore, "0.1.1") || {}).packVersion, 2);
check("older extension rejected", pack.validateStoredPack(validStore, "0.1.0"), null);
check("unknown file name rejected", pack.validateStoredPack(
{ ...validStore, files: { "evil.js": { sha256: "a".repeat(64) } }, texts: { "evil.js": "alert(1)" } },
"0.1.1"), null);
check("oversized file rejected", pack.validateStoredPack(
{ ...validStore, texts: { "content.css": "a".repeat(300000) } },
"0.1.1"), null);
// --- load(): no storage access -> bundled behavior ---------------------------
{
global.chrome = undefined;
pack.load().then((loaded) => {
check("load without storage returns no pack", loaded.pack, null);
check("load config = defaults", loaded.config, pack.DEFAULT_CONFIG);
console.log(`\n${passed} passed, ${failed} failed`);
process.exit(failed ? 1 : 0);
});
}