Files
jiang13-bbs/frontend/lib/bookSearch.test.ts
freefire ff2ab286fb feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等
新增:
- 书库导入导出(library_import/library_export)及测试
- 图片变体生成(image_variants)与响应式图片(responsiveImage)
- 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件
- 小组件运行时(widgetRuntime)与静态检查(widgetLint)
- 上传缓存中间件(upload_cache)与字体 CSS 提取脚本

其它:
- 后端 handlers/services 全量调整
- 前端页面、组件、库函数与配置更新
2026-10-01 03:06:05 +08:00

93 lines
3.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { markdownToSearchText, searchBook, type BookChapterIndex } from "./bookSearch.ts";
describe("markdownToSearchText", () => {
it("条目卡指令保留值、剥离标记符号", () => {
const md = `[entries]
## 安全气囊
@tags 汽车 | 安全 | 主:被动
@summary 气囊需配合安全带使用。
备注 :: 详见召回公告
@source NHTSA 报告 | https://a.gov/x
[/entries]`;
const text = markdownToSearchText(md);
assert.ok(text.includes("安全气囊"));
assert.ok(text.includes("汽车"));
assert.ok(text.includes("主:被动"));
assert.ok(text.includes("气囊需配合安全带使用。"));
assert.ok(text.includes("备注 详见召回公告"));
assert.ok(text.includes("NHTSA 报告"));
assert.ok(!text.includes("@tags"));
assert.ok(!text.includes("@summary"));
assert.ok(!text.includes("@source"));
assert.ok(!text.includes("::"));
assert.ok(!text.includes("[entries"));
assert.ok(!text.includes("https://a.gov/x"));
});
it("锁定的 hide 块整体不可搜,未锁块保留正文", () => {
const md = `公开段落内容。
[hide login]
登录后可见的秘密
[/hide]
[hide points=10 locked]
积分秘密内容
[/hide]`;
const text = markdownToSearchText(md);
assert.ok(text.includes("公开段落内容"));
assert.ok(text.includes("登录后可见的秘密"));
assert.ok(!text.includes("积分秘密内容"));
});
it("剥离代码围栏与行内 markdown 语法但保留代码与锚文本", () => {
const md = "见 [官网说明](https://x.com) 与 `const x = 1`,\n\n```ts\ncodeInside();\n```";
const text = markdownToSearchText(md);
assert.ok(text.includes("官网说明"));
assert.ok(!text.includes("https://x.com"));
assert.ok(text.includes("const x = 1"));
assert.ok(text.includes("codeInside();"));
assert.ok(!text.includes("```"));
});
});
describe("searchBook", () => {
const chapters: BookChapterIndex[] = [
{ no: 1, title: "第一章", plain: "今天天气很好,适合出门散步。路边有一朵花。" },
{ no: 2, title: "第二章", plain: "第二章没有关键词。" },
{ no: 3, title: "第三章", plain: "再次提到天气,天气真的不错。" },
];
it("按章聚合命中数与片段,大小写不敏感", () => {
const hits = searchBook(chapters, "天气");
assert.equal(hits.length, 2);
assert.deepEqual(
hits.map((h) => [h.no, h.count]),
[
[1, 1],
[3, 2],
]
);
assert.equal(hits[1].snippets[0].match, "天气");
assert.ok(hits[1].snippets[0].after.includes(",天气真的"));
});
it("片段最多生成 SNIPPET_LIMIT 个,但 count 为真实总数", () => {
const cs: BookChapterIndex[] = [{ no: 1, title: "t", plain: "天气 天气 天气 天气" }];
const hits = searchBook(cs, "天气");
assert.equal(hits[0].count, 4);
assert.equal(hits[0].snippets.length, 3);
assert.deepEqual(hits[0].snippets.map((s) => s.ordinal), [0, 1, 2]);
});
it("空关键词返回空", () => {
assert.deepEqual(searchBook(chapters, " "), []);
});
it("无命中返回空数组", () => {
assert.deepEqual(searchBook(chapters, "航天飞机"), []);
});
});