chore: add CI, lint config, login rate limit and split large modules
- Rate-limit failed logins (8 per 15 min per IP or username, 429 + Retry-After) - Add ruff/mypy config and GitHub Actions workflow (ruff, pytest, tsc, tests, build) - Apply ruff autofixes (import order, pyupgrade) - Extract rpy_helpers.py, engine-util.ts, TranslationParts.tsx and translation-types.ts from oversized modules - Add dependency-free React UI tests - Refresh README and ARCHITECTURE (Naninovel/Tomie, status and plans) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
57a4edcbfd
commit
bbbcc6a5d8
42 files changed
+678
-391
No files matched your search
@@ -0,0 +1,42 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { build } from "esbuild";
|
||||
|
||||
// Bundle React together with the code under test so that both share one React instance.
|
||||
async function load(entry) {
|
||||
const result = await build({
|
||||
stdin: { contents: entry, resolveDir: fileURLToPath(new URL("../src/", import.meta.url)), loader: "tsx" },
|
||||
bundle: true, write: false, format: "esm", platform: "node", jsx: "automatic", logLevel: "silent",
|
||||
banner: { js: "import { createRequire } from \"node:module\"; const require = createRequire(process.cwd() + \"/package.json\");" },
|
||||
});
|
||||
const code = Buffer.from(result.outputFiles[0].text).toString("base64");
|
||||
return import(`data:text/javascript;base64,${code}`);
|
||||
}
|
||||
|
||||
const { renderToStaticMarkup, h, ProgressBar, highlight } = await load(`
|
||||
export { renderToStaticMarkup } from "react-dom/server";
|
||||
export { createElement as h } from "react";
|
||||
export { ProgressBar, highlight } from "./pages/TranslationParts";
|
||||
`);
|
||||
|
||||
const stats = {
|
||||
lines: 10, translated: 5, progress: 0.5, strings: 4, stringsTranslated: 2,
|
||||
states: { untranslated: 5, outdated: 1, translated: 3, fuzzy: 0, machine: 0, approved: 1 },
|
||||
};
|
||||
|
||||
test("ProgressBar renders counts and only non-empty segments", () => {
|
||||
const html = renderToStaticMarkup(h(ProgressBar, { stats }));
|
||||
assert.match(html, /5 из 10 реплик \(50%\)/);
|
||||
assert.match(html, /устарело 1/);
|
||||
assert.match(html, /seg-approved/);
|
||||
assert.doesNotMatch(html, /seg-warn/);
|
||||
});
|
||||
|
||||
test("highlight wraps glossary terms case-insensitively and escapes regex characters", () => {
|
||||
const terms = [{ src: "Eileen", t: "Эйлин", note: "" }, { src: "a+b", t: "x", note: "" }];
|
||||
const html = renderToStaticMarkup(h("p", null, highlight("eileen met a+b", terms)));
|
||||
assert.match(html, /<mark title="Эйлин">eileen<\/mark>/);
|
||||
assert.match(html, /<mark title="x">a\+b<\/mark>/);
|
||||
assert.equal(highlight("plain", []), "plain");
|
||||
});
|
||||
Reference in new issue
Block a user