import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { convertGenericPageHtmlToWechatArticle, convertPageHtmlToWechatArticle, convertRichMindSpacePageHtmlToWechatArticle, isNewsHotspotFormat, mindspaceWechatPageDraftInternals, resolvePagePublicUrl, resolveWechatDraftProfile, resolveWechatDraftThumbPath, validatePageForWechatDraft, } from './mindspace-wechat-page-draft.mjs'; import { buildDailyNewsWechatDraftArticle } from './wechat-news-morning-draft.mjs'; import { applyWechatDraftArticleLayout } from './wechat-draft-article-layout.mjs'; const SAMPLE_HTML = ` 测试页面

标题

正文内容

`; const POEM_HTML = `八月午后

八月午后

甜是慢的

蝉声把正午拉得很长

声音的长度

蝉鸣没有起伏

八月的午后很难写。

可正是这种什么都没有
`; const PROSE_HTML = `秋庭小憩 · 散文

秋庭小憩

一篇百字散文 · 秋日慢时光

午后阳光斜洒庭院石阶,桂花香气随微风四处飘散。

墙外梧桐叶纷纷飘落,金黄铺满青石板路,沙沙作响。

🍂 秋日🌼 桂花
`; const BLOG_HTML = `博客文章

2026-09-11 · John

AI 与创作

背景

工具正在改变内容生产。

创作者需要新的工作流。

`; const STARTUP_HTML = `创业路演

让 AI 更懂业务

面向团队的知识助手

愿景

帮助每个团队拥有自己的 AI 大脑。

`; test('convertGenericPageHtmlToWechatArticle extracts title and body', () => { const article = convertGenericPageHtmlToWechatArticle(SAMPLE_HTML, { pageTitle: '测试页面', pageSummary: '页面摘要', }); assert.equal(article.title, '测试页面'); assert.equal(article.digest, '页面摘要'); assert.match(article.content, /正文内容/); }); test('resolvePagePublicUrl prefers publication slug url over empty workspace path', () => { const url = resolvePagePublicUrl( { publicationUrl: null, workspaceRelativePath: null }, { userId: 'user-1', publication: { publicUrl: 'https://m.tkmind.cn/u/john/pages/tkmind-upgrade-sep2026' }, publicBaseUrl: 'https://m.tkmind.cn', }, ); assert.equal(url, 'https://m.tkmind.cn/u/john/pages/tkmind-upgrade-sep2026'); }); test('daily-news draft includes read original when publicUrl is present', () => { const html = fs.readFileSync('public/dev/tkmind-upgrade-sep2026.html', 'utf8'); const article = buildDailyNewsWechatDraftArticle({ html, publicUrl: 'https://m.tkmind.cn/u/john/pages/tkmind-upgrade-sep2026', qrcodeImageUrl: 'https://mmbiz.qpic.cn/qrcode.png', }); const readIdx = article.content.indexOf('阅读原文'); const followIdx = article.content.indexOf('欢迎关注 TKMind 智趣'); assert.ok(readIdx >= 0, '缺少阅读原文'); assert.ok(readIdx < followIdx, '阅读原文应在关注区之前'); assert.match(article.content, /href="https:\/\/m\.tkmind\.cn\/u\/john\/pages\/tkmind-upgrade-sep2026"/); assert.equal(article.contentSourceUrl, 'https://m.tkmind.cn/u/john/pages/tkmind-upgrade-sep2026'); }); test('applyWechatDraftArticleLayout adds read original and follow footer', () => { const content = applyWechatDraftArticleLayout('

正文

', { publicUrl: 'https://m.tkmind.cn/u/demo/pages/test.html', qrcodeImageUrl: 'https://mmbiz.qpic.cn/qrcode.png', }); assert.match(content, /阅读原文/); assert.match(content, /欢迎关注 TKMind 智趣/); assert.match(content, /qrcode\.png/); assert.doesNotMatch(content, /点击阅读原文/); assert.ok(content.indexOf('正文') < content.indexOf('阅读原文')); assert.ok(content.indexOf('阅读原文') < content.indexOf('欢迎关注')); }); test('convertPageHtmlToWechatArticle uses rich mindspace converter', () => { const article = convertPageHtmlToWechatArticle(POEM_HTML, { pageTitle: '测试页面', pageSummary: '页面摘要', }); assert.equal(article.title, '八月午后'); assert.match(article.content, /蝉声把正午拉得很长/); assert.match(article.content, /创作手记/); assert.match(article.content, /意象/); }); test('isNewsHotspotFormat ignores mindspace imagery cards', () => { assert.equal(isNewsHotspotFormat(POEM_HTML), false); assert.equal(resolveWechatDraftProfile(POEM_HTML), 'mindspace-page'); }); test('validatePageForWechatDraft warns for mindspace pages', () => { const result = validatePageForWechatDraft(SAMPLE_HTML, { pageTitle: '测试页面', pageSummary: '页面摘要', publicUrl: 'https://example.com/page.html', }); assert.equal(result.ok, true); assert.equal(result.profile, 'generic'); }); test('convertRichMindSpacePageHtmlToWechatArticle preserves poem sections', () => { const article = convertRichMindSpacePageHtmlToWechatArticle(POEM_HTML, { pageTitle: '八月午后', imageUrlMap: new Map([['hero.webp', 'https://mmbiz.qpic.cn/hero.png']]), }); assert.match(article.content, /蝉声把正午拉得很长/); assert.match(article.content, /声音的长度/); assert.match(article.content, /https:\/\/mmbiz\.qpic\.cn\/hero\.png/); }); test('convertRichMindSpacePageHtmlToWechatArticle preserves prose essay sections', () => { const article = convertRichMindSpacePageHtmlToWechatArticle(PROSE_HTML, { pageTitle: '秋庭小憩 · 散文', pageSummary: '午后庭院,桂花飘香', imageUrlMap: new Map([['qiuting-xiaoxi.thumbnail.svg', 'https://mmbiz.qpic.cn/thumb.png']]), }); assert.equal(article.title, '秋庭小憩'); assert.match(article.content, /一篇百字散文 · 秋日慢时光/); assert.match(article.content, /午后阳光斜洒庭院石阶/); assert.match(article.content, /墙外梧桐叶纷纷飘落/); assert.match(article.content, /🍂 秋日/); assert.match(article.content, /thumb\.png/); assert.doesNotMatch(article.content, /data-mindspace-public-share/); }); test('resolveWechatDraftProfile treats mindspace-cover pages as mindspace-page', () => { assert.equal(resolveWechatDraftProfile(BLOG_HTML), 'mindspace-page'); assert.equal(resolveWechatDraftProfile(STARTUP_HTML), 'mindspace-page'); }); test('convertRichMindSpacePageHtmlToWechatArticle extracts blog and startup templates via fallback', () => { const blog = convertRichMindSpacePageHtmlToWechatArticle(BLOG_HTML, { pageTitle: 'AI 与创作', pageSummary: '产品观察', }); assert.equal(blog.title, 'AI 与创作'); assert.match(blog.content, /工具正在改变内容生产/); assert.match(blog.content, /创作者需要新的工作流/); assert.doesNotMatch(blog.content, /data-mindspace-public-share/); const startup = convertRichMindSpacePageHtmlToWechatArticle(STARTUP_HTML, { pageTitle: '创业路演', }); assert.equal(startup.title, '让 AI 更懂业务'); assert.match(startup.content, /面向团队的知识助手/); assert.match(startup.content, /帮助每个团队拥有自己的 AI 大脑/); }); test('collectPageImageCandidates adds sidecar thumbnail when hero image missing', () => { const publishDir = path.join(process.cwd(), 'MindSpace', '1c99b83b-0454-474f-a5d2-129d34506a32'); const html = fs.readFileSync(path.join(publishDir, 'public/qiuting-xiaoxi.html'), 'utf8'); const candidates = mindspaceWechatPageDraftInternals.collectPageImageCandidates(html, { publishDir, htmlRelativePath: 'public/qiuting-xiaoxi.html', }); assert.ok(candidates.some((item) => /\.thumbnail\.(png|svg)$/i.test(item.ref ?? ''))); }); test('convertGenericPageHtmlToWechatArticle truncates long content', () => { const longBody = `

${'很长'.repeat(12000)}

`; const article = mindspaceWechatPageDraftInternals.convertGenericPageHtmlToWechatArticle( `${longBody}`, { pageTitle: '长文' }, ); assert.ok(article.content.length <= 20000); }); test('resolveWechatDraftThumbPath prefers hero image over thumbnail sidecar', () => { const userId = '1c99b83b-0454-474f-a5d2-129d34506a32'; const html = fs.readFileSync(`MindSpace/${userId}/public/august-afternoon.html`, 'utf8'); const resolved = resolveWechatDraftThumbPath(html, { h5Root: process.cwd(), userId, workspaceRelativePath: 'public/august-afternoon.html', publishDir: path.join(process.cwd(), 'MindSpace', userId), }); assert.equal(resolved.source, 'hero'); assert.match(resolved.path ?? '', /hero-ca5a45b835d8\.webp$/); }); test('resolveWechatDraftThumbPath falls back to raster sidecar when hero missing', () => { const userId = '1c99b83b-0454-474f-a5d2-129d34506a32'; const html = `无 hero

标题

`; const resolved = resolveWechatDraftThumbPath(html, { h5Root: process.cwd(), userId, workspaceRelativePath: 'public/august-afternoon.html', publishDir: path.join(process.cwd(), 'MindSpace', userId), }); assert.equal(resolved.source, 'thumbnail-sidecar'); assert.match(resolved.path ?? '', /august-afternoon\.thumbnail\.png$/); });