Files
memind/mindspace-wechat-page-draft.test.mjs
T
john add36efa07
Memind CI / Test, build, and release guards (push) Failing after 3m19s
test(release): fix fast-release guards after 143 migration
Use inline daily-news HTML fixture instead of missing dev page file,
and assert WireGuard upstream 10.10.0.2 in canary release script test.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 12:39:38 +08:00

228 lines
11 KiB
JavaScript

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 = `<!DOCTYPE html>
<html><head><title>测试页面</title>
<meta name="description" content="页面摘要"></head>
<body><h1>标题</h1><p>正文内容</p></body></html>`;
const POEM_HTML = `<!DOCTYPE html><html><head><title>八月午后</title></head><body>
<header class="hero"><h1>八月午后</h1><p class="lead">甜是慢的</p></header>
<main><div class="stanza"><span class="num">壹</span><p>蝉声把正午拉得很长</p></div>
<div class="card"><span class="glyph">蝉</span><h3>声音的长度</h3><p>蝉鸣没有起伏</p></div>
<section class="note"><p>八月的午后很难写。</p><div class="quote">可正是这种什么都没有</div></section>
</body></html>`;
const PROSE_HTML = `<!DOCTYPE html><html><head><title>秋庭小憩 · 散文</title>
<meta name="description" content="午后庭院,桂花飘香">
<meta name="mindspace-cover" content='{"tag":"旅行","subtitle":"桂花"}'></head><body>
<div class="page"><div class="hero"></div><div class="content"><div class="title-block">
<h1 class="title">秋庭小憩</h1><p class="subtitle">一篇百字散文 · 秋日慢时光</p></div>
<div class="prose"><p>午后阳光斜洒庭院石阶,桂花香气随微风四处飘散。</p>
<p>墙外梧桐叶纷纷飘落,金黄铺满青石板路,沙沙作响。</p></div>
<div class="tags"><span class="tag">🍂 秋日</span><span class="tag">🌼 桂花</span></div>
</div></div><footer><p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p></footer></body></html>`;
const BLOG_HTML = `<!DOCTYPE html><html><head><title>博客文章</title>
<meta name="mindspace-cover" content='{"tag":"博客","subtitle":"产品观察"}'></head><body>
<article><p class="meta">2026-09-11 · John</p><h1>AI 与创作</h1>
<div class="content"><h2>背景</h2><p>工具正在改变内容生产。</p><p>创作者需要新的工作流。</p></div>
</article><footer><p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p></footer></body></html>`;
const STARTUP_HTML = `<!DOCTYPE html><html><head><title>创业路演</title>
<meta name="mindspace-cover" content='{"tag":"创业","subtitle":"AI 助手"}'></head><body>
<header class="hero"><h1>让 AI 更懂业务</h1><p class="pitch-sub">面向团队的知识助手</p></header>
<main><div class="section"><h2>愿景</h2><p>帮助每个团队拥有自己的 AI 大脑。</p></div></main>
<footer><p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p></footer></body></html>`;
const DAILY_NEWS_HTML = `<!DOCTYPE html><html lang="zh-CN"><head>
<title>每日新闻早报 · 2026年9月10日</title>
<meta name="description" content="2026年9月10日新闻热点摘要"></head><body>
<div class="container"><div class="hero"><h1>📰 每日新闻早报</h1>
<div class="date-badge">2026年9月10日 · 星期三</div></div>
<div class="section" id="headlines"><div class="card"><h3>示例热点</h3><p>这是示例正文。</p></div></div>
</div></body></html>`;
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 article = buildDailyNewsWechatDraftArticle({
html: DAILY_NEWS_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('<p>正文</p>', {
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 = `<p>${'很长'.repeat(12000)}</p>`;
const article = mindspaceWechatPageDraftInternals.convertGenericPageHtmlToWechatArticle(
`<html><body>${longBody}</body></html>`,
{ 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 = `<!DOCTYPE html><html><head><title>无 hero</title></head><body><h1>标题</h1></body></html>`;
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$/);
});