const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const { ensureUserConfigInitialized, ensureUserSiteYmlExists, upsertBookmarksNavInSiteYml, parseBookmarks, generateBookmarksYaml, } = require('../src/bookmark-processor.js'); function stripYamlComments(yamlText) { return yamlText .split(/\r?\n/) .filter((line) => !/^\s*#/.test(line)) .join('\n') .trim(); } test('parseBookmarks:解析书签栏、根目录书签与图标映射', () => { const html = ` Bookmarks

Bookmarks

书签栏

GitHub

工具

Google

`; const bookmarks = parseBookmarks(html); assert.ok(bookmarks); assert.ok(Array.isArray(bookmarks.categories)); assert.ok(bookmarks.categories.length >= 2); // 根目录书签应该插入到首位 assert.equal(bookmarks.categories[0].name, '根目录书签'); assert.ok(Array.isArray(bookmarks.categories[0].sites)); assert.equal(bookmarks.categories[0].sites[0].name, 'GitHub'); assert.equal(bookmarks.categories[0].sites[0].icon, 'fab fa-github'); const tools = bookmarks.categories.find((c) => c.name === '工具'); assert.ok(tools, '应解析出“工具”分类'); assert.ok(Array.isArray(tools.sites)); assert.equal(tools.sites[0].name, 'Google'); }); test('templates:subgroups(第4层)应可渲染到页面', () => { const Handlebars = require('handlebars'); const { registerAllHelpers } = require('../src/helpers'); const hbs = Handlebars.create(); registerAllHelpers(hbs); const category = fs.readFileSync( path.join(__dirname, '..', 'templates', 'components', 'category.hbs'), 'utf8' ); const group = fs.readFileSync( path.join(__dirname, '..', 'templates', 'components', 'group.hbs'), 'utf8' ); const pageHeader = fs.readFileSync( path.join(__dirname, '..', 'templates', 'components', 'page-header.hbs'), 'utf8' ); const siteCard = fs.readFileSync( path.join(__dirname, '..', 'templates', 'components', 'site-card.hbs'), 'utf8' ); const page = fs.readFileSync( path.join(__dirname, '..', 'templates', 'pages', 'bookmarks.hbs'), 'utf8' ); hbs.registerPartial('category', category); hbs.registerPartial('group', group); hbs.registerPartial('page-header', pageHeader); hbs.registerPartial('site-card', siteCard); const tpl = hbs.compile(page); const html = tpl({ title: '我的书签', subtitle: '测试 subgroups 渲染', icons: { mode: 'manual' }, categories: [ { name: '技术', icon: 'fas fa-code', subcategories: [ { name: '前端', icon: 'fas fa-laptop-code', groups: [ { name: '框架', icon: 'fas fa-cubes', subgroups: [ { name: 'React生态', icon: 'fab fa-react', sites: [ { name: 'React', url: 'https://reactjs.org/', icon: 'fab fa-react', description: 'React官方', }, ], }, ], }, ], }, ], }, ], }); assert.ok(html.includes('subgroups-container'), '应输出 subgroups-container 容器'); assert.ok(html.includes('group-level-4'), '应输出 level=4 的 group 样式类'); assert.ok(html.includes('React生态'), '应渲染子分组标题文本'); }); test('generateBookmarksYaml:生成 YAML 且可被解析', () => { const bookmarks = { categories: [ { name: '示例分类', icon: 'fas fa-folder', sites: [ { name: 'Example', url: 'https://example.com', icon: 'fas fa-link', description: '' }, ], }, ], }; const yamlText = generateBookmarksYaml(bookmarks); assert.ok(typeof yamlText === 'string'); assert.ok(yamlText.includes('# 自动生成的书签配置文件')); assert.ok(yamlText.includes('categories:')); const yaml = require('js-yaml'); const parsed = yaml.load(stripYamlComments(yamlText)); assert.equal(parsed.title, '我的书签'); assert.ok(Array.isArray(parsed.categories)); assert.equal(parsed.categories[0].name, '示例分类'); }); test('upsertBookmarksNavInSiteYml:无 navigation 时追加并幂等', () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'menav-test-')); const filePath = path.join(tmp, 'site.yml'); fs.writeFileSync(filePath, `title: Test Site\n`, 'utf8'); const r1 = upsertBookmarksNavInSiteYml(filePath); assert.equal(r1.updated, true); const updated1 = fs.readFileSync(filePath, 'utf8'); assert.ok(updated1.includes('navigation:')); assert.ok(updated1.includes('- name: 书签')); assert.ok(updated1.includes('id: bookmarks')); const r2 = upsertBookmarksNavInSiteYml(filePath); assert.equal(r2.updated, false); assert.equal(r2.reason, 'already_present'); }); test('ensureUserConfigInitialized/ensureUserSiteYmlExists:可在空目录初始化用户配置', () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'menav-test-')); const originalCwd = process.cwd(); process.chdir(tmp); try { fs.mkdirSync('config/_default/pages', { recursive: true }); fs.writeFileSync('config/_default/site.yml', 'title: Default\n', 'utf8'); fs.writeFileSync('config/_default/pages/common.yml', 'categories: []\n', 'utf8'); const init = ensureUserConfigInitialized(); assert.equal(init.initialized, true); assert.ok(fs.existsSync('config/user/site.yml')); assert.ok(fs.existsSync('config/user/pages/common.yml')); // 若 site.yml 已存在,应直接返回 true assert.equal(ensureUserSiteYmlExists(), true); } finally { process.chdir(originalCwd); } });