Import tests: test/content-engine
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { JSDOM } = require("jsdom");
|
||||
|
||||
const source = fs.readFileSync(path.join(__dirname, "../src/content.js"), "utf8");
|
||||
|
||||
function createExtensionDom(body, overrides = {}) {
|
||||
const settings = {
|
||||
enabled: true,
|
||||
fixDirection: true,
|
||||
useFont: true,
|
||||
fixInputs: true,
|
||||
disabledSites: [],
|
||||
...overrides
|
||||
};
|
||||
let onChanged;
|
||||
const dom = new JSDOM(`<!doctype html><html><head></head><body>${body}</body></html>`, {
|
||||
url: "https://chat.example/",
|
||||
runScripts: "dangerously",
|
||||
pretendToBeVisual: true
|
||||
});
|
||||
dom.window.requestIdleCallback = (callback) => callback({ didTimeout: false, timeRemaining: () => 50 });
|
||||
dom.window.chrome = {
|
||||
runtime: {
|
||||
getURL: (file) => `chrome-extension://test/${file}`,
|
||||
lastError: null
|
||||
},
|
||||
storage: {
|
||||
local: { get: (_defaults, callback) => callback(settings) },
|
||||
onChanged: { addListener: (callback) => { onChanged = callback; } }
|
||||
}
|
||||
};
|
||||
const script = dom.window.document.createElement("script");
|
||||
script.textContent = source;
|
||||
dom.window.document.head.appendChild(script);
|
||||
return {
|
||||
dom,
|
||||
changeSettings(patch) {
|
||||
for (const [key, value] of Object.entries(patch)) settings[key] = value;
|
||||
const changes = Object.fromEntries(Object.entries(patch).map(([key, value]) => [key, { newValue: value }]));
|
||||
onChanged(changes, "local");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function element(body, id = "target", overrides) {
|
||||
const fixture = createExtensionDom(body, overrides);
|
||||
return { fixture, target: fixture.dom.window.document.getElementById(id) };
|
||||
}
|
||||
|
||||
const tests = [];
|
||||
function test(name, fn) { tests.push({ name, fn }); }
|
||||
function nextTurn() { return new Promise((resolve) => setTimeout(resolve, 0)); }
|
||||
|
||||
test("real Persian text is corrected to RTL", () => {
|
||||
const { target } = element('<p id="target">این یک متن فارسی است</p>');
|
||||
assert.equal(target.getAttribute("dir"), "rtl");
|
||||
});
|
||||
|
||||
test("Arabic-only text is not treated as Persian", () => {
|
||||
const { target } = element('<p id="target">هذا نص عربي للاختبار</p>');
|
||||
assert.equal(target.hasAttribute("dir"), false);
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("English-leading Persian text uses auto direction", () => {
|
||||
const { target } = element('<p id="target">The answer is متن فارسی برای آزمایش.</p>');
|
||||
assert.equal(target.getAttribute("dir"), "auto");
|
||||
assert.equal(target.hasAttribute("data-rtlx-rtl"), false);
|
||||
});
|
||||
|
||||
test("Latin digits inside Persian text remain byte-identical", () => {
|
||||
const text = "نسخه 1234567890 برای آزمایش";
|
||||
const { target } = element(`<p id="target">${text}</p>`);
|
||||
assert.equal(target.textContent, text);
|
||||
});
|
||||
|
||||
test("a sensible explicit RTL direction is preserved without ownership markers", () => {
|
||||
const { target } = element('<p id="target" dir="rtl">این متن از قبل راستچین است</p>');
|
||||
assert.equal(target.getAttribute("dir"), "rtl");
|
||||
assert.equal(target.hasAttribute("data-rtlx-setdir"), false);
|
||||
});
|
||||
|
||||
test("a sensible explicit auto direction is preserved without ownership markers", () => {
|
||||
const { target } = element('<p id="target" dir="auto">این متن جهت خودکار دارد</p>');
|
||||
assert.equal(target.getAttribute("dir"), "auto");
|
||||
assert.equal(target.hasAttribute("data-rtlx-setdir"), false);
|
||||
});
|
||||
|
||||
test("a clearly wrong explicit LTR direction is corrected reversibly", () => {
|
||||
const { target } = element('<p id="target" dir="ltr">این متن فارسی راستبهچپ است</p>');
|
||||
assert.equal(target.getAttribute("dir"), "rtl");
|
||||
assert.equal(target.getAttribute("data-rtlx-dir"), "ltr");
|
||||
});
|
||||
|
||||
test("an inline author font is never replaced", () => {
|
||||
const { target } = element('<p id="target" style="font-family: Arial">این متن فونت دارد</p>');
|
||||
assert.equal(target.style.fontFamily, "Arial");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("an inherited author font is never replaced", () => {
|
||||
const { target } = element('<div style="font-family: sans-serif"><p id="target">این متن فونت ارثبری دارد</p></div>');
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("a stylesheet font on the Persian element is never replaced", () => {
|
||||
const { target } = element('<style>.authored { font-family: system-ui; }</style><p id="target" class="authored">این متن فونت دارد</p>');
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("an inherited stylesheet font is never replaced", () => {
|
||||
const { target } = element('<style>.chat { font-family: Tahoma; }</style><article class="chat"><p id="target">این پاسخ هوش مصنوعی است</p></article>');
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("unstyled Persian text receives Vazirmatn", () => {
|
||||
const { target } = element('<p id="target">این متن بدون فونت است</p>');
|
||||
assert.match(target.style.fontFamily, /Vazirmatn/i);
|
||||
});
|
||||
|
||||
test("streamed Persian text is corrected and Latin digits are untouched", async () => {
|
||||
const { fixture, target } = element('<p id="target">Waiting</p>');
|
||||
target.textContent = "پاسخ زنده 1234567890";
|
||||
await nextTurn();
|
||||
assert.equal(target.getAttribute("dir"), "rtl");
|
||||
assert.equal(target.textContent, "پاسخ زنده 1234567890");
|
||||
fixture.dom.window.close();
|
||||
});
|
||||
|
||||
test("a streamed block that becomes English is restored", async () => {
|
||||
const { fixture, target } = element('<p id="target">پاسخ فارسی</p>');
|
||||
assert.equal(target.getAttribute("dir"), "rtl");
|
||||
target.textContent = "The final answer is English.";
|
||||
await nextTurn();
|
||||
assert.equal(target.hasAttribute("dir"), false);
|
||||
assert.equal(target.hasAttribute("data-rtlx-rtl"), false);
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
fixture.dom.window.close();
|
||||
});
|
||||
|
||||
test("disabling the extension restores owned direction and font", () => {
|
||||
const { fixture, target } = element('<p id="target">پاسخ فارسی</p>');
|
||||
fixture.changeSettings({ enabled: false });
|
||||
assert.equal(target.hasAttribute("dir"), false);
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
});
|
||||
|
||||
test("unstyled inputs receive Vazirmatn only after Persian is present", () => {
|
||||
const { fixture, target } = element('<input id="target" type="text" />');
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
target.value = "متن فارسی";
|
||||
target.dispatchEvent(new fixture.dom.window.Event("input", { bubbles: true }));
|
||||
assert.match(target.style.fontFamily, /Vazirmatn/i);
|
||||
});
|
||||
|
||||
test("author-styled Persian inputs preserve their font", () => {
|
||||
const { target } = element('<input id="target" type="text" style="font-family: Arial" value="متن فارسی" />');
|
||||
assert.equal(target.style.fontFamily, "Arial");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
test("input listeners do not reapply styling while the extension is disabled", () => {
|
||||
const { fixture, target } = element('<input id="target" type="text" />');
|
||||
fixture.changeSettings({ enabled: false });
|
||||
target.value = "متن فارسی";
|
||||
target.dispatchEvent(new fixture.dom.window.Event("input", { bubbles: true }));
|
||||
assert.equal(target.style.fontFamily, "");
|
||||
assert.equal(target.hasAttribute("data-rtlx-font"), false);
|
||||
});
|
||||
|
||||
(async () => {
|
||||
let passed = 0;
|
||||
for (const { name, fn } of tests) {
|
||||
try {
|
||||
await fn();
|
||||
passed += 1;
|
||||
console.log(`PASS ${name}`);
|
||||
} catch (error) {
|
||||
console.error(`FAIL ${name}`);
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
console.log(`\n${passed}/${tests.length} passed`);
|
||||
process.exitCode = passed === tests.length ? 0 : 1;
|
||||
})();
|
||||
Reference in New Issue
Block a user