Files
memind/wechat-daily-news-inline.test.mjs
T
john b8bcbcb764 feat(wechat): 早报草稿改用内联模板并去掉正文大封面
让定时任务按新版式推送草稿:热门旅游城市天气、无 Hero logo,并约束生成页必须写城市天气卡。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 15:20:44 +08:00

146 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. 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 test from 'node:test';
import {
convertDailyNewsHtmlToWechatInlineArticle,
wechatDailyNewsInlineInternals,
} from './wechat-daily-news-inline.mjs';
const WEATHER_HTML = `<!DOCTYPE html><html><body><div class="container">
<div class="hero"><h1>📰 每日新闻早报</h1></div>
<div class="quick-links"><a href="#weather">🌤️ 天气</a><a href="#world">🌍 国际</a></div>
<div class="section" id="headlines"><div class="section-title">要闻</div><div class="card"><h3>头条</h3><p>正文</p></div></div>
<div class="section" id="weather">
<div class="section-title">🌤️ 今日天气</div>
<div class="weather-today">
<div class="wt-row"><div><div class="wt-city">📍 北京</div><div class="wt-detail">☀️ 晴 · 温差大</div></div><div><div class="wt-temp-big">30°</div></div></div>
<div style="display:flex;gap:12px;"><span>🌧️ 华南有雨</span><span>☀️ 华北晴好</span></div>
<div style="background:rgba(255,255,255,0.5);padding:8px;"><strong>中央气象台:</strong>未来三天降水减弱。</div>
</div>
<div class="weather-grid">
<div class="weather-card w-hot"><div class="w-left"><span class="w-city">北京</span><span class="w-cond">☀️ 晴</span></div><div class="w-right"><span class="w-temp">30°</span></div></div>
<div class="weather-card w-rain"><div class="w-left"><span class="w-city">上海</span><span class="w-cond">🌧️ 小雨</span></div><div class="w-right"><span class="w-temp">26°</span></div></div>
</div>
<p style="font-size:12px;color:#a0aec0;">数据来源:中央气象台</p>
</div>
</div></body></html>`;
test('splitSections preserves section ids after opening tag split', () => {
const sections = wechatDailyNewsInlineInternals.splitSections(WEATHER_HTML);
assert.deepEqual(sections.map((item) => item.id), ['headlines', 'weather']);
});
test('renderWeatherSection uses tourist city title and two-column city cards', () => {
const sections = wechatDailyNewsInlineInternals.splitSections(WEATHER_HTML);
const weather = sections.find((item) => item.id === 'weather');
const rendered = wechatDailyNewsInlineInternals.renderWeatherSection(weather.html);
assert.match(rendered, /热门旅游城市天气/u);
assert.match(rendered, /📍 北京/u);
assert.match(rendered, /☀️ 晴 · 温差大/u);
assert.match(rendered, /width:50%;box-sizing:border-box/u);
assert.match(rendered, /display:inline-block/u);
assert.doesNotMatch(rendered, /display:flex/u);
assert.match(rendered, /30°/u);
assert.match(rendered, /26°/u);
assert.doesNotMatch(rendered, /中央气象台/u);
assert.doesNotMatch(rendered, /今日天气/u);
});
test('renderWeatherSection skips regional fallback cards and national summary', () => {
const regionalWeatherHtml = `<div class="section" id="weather">
<div class="section-title">🌤️ 今日天气 · 9月16日</div>
<div class="weather-today">
<div class="wt-row"><div><div class="wt-city">📍 全国概况 · 江汉西南阴雨开场</div><div class="wt-detail">🌧️ 湖北西部局地暴雨</div></div></div>
</div>
<div class="weather-grid">
<div class="weather-card"><div class="w-left"><span class="w-city">湖北西部</span><span class="w-cond">🌧️ 暴雨</span></div><div class="w-right"><span class="w-temp">雨</span></div></div>
<div class="weather-card"><div class="w-left"><span class="w-city">北京</span><span class="w-cond">☀️ 晴</span></div><div class="w-right"><span class="w-temp">30°</span></div></div>
<div class="weather-card"><div class="w-left"><span class="w-city">上海</span><span class="w-cond">🌧️ 小雨</span></div><div class="w-right"><span class="w-temp">26°</span></div></div>
</div>
</div>`;
const rendered = wechatDailyNewsInlineInternals.renderWeatherSection(regionalWeatherHtml);
assert.match(rendered, /热门旅游城市天气 · 9月16日/u);
assert.match(rendered, /北京/u);
assert.match(rendered, /上海/u);
assert.doesNotMatch(rendered, /湖北西部/u);
assert.doesNotMatch(rendered, /全国概况/u);
});
test('buildWechatInlineContent renders weather and optional quick links', () => {
const built = wechatDailyNewsInlineInternals.buildWechatInlineContent(WEATHER_HTML, {
includeFixedLayout: false,
trimProfile: { skipHistory: true, skipQuickLinks: false },
});
assert.match(built.content, /🌤️ 天气/u);
assert.match(built.content, /📍 北京/u);
assert.match(built.content, /30°/u);
});
test('convertDailyNewsHtmlToWechatInlineArticle keeps weather when trimming', () => {
const article = convertDailyNewsHtmlToWechatInlineArticle(WEATHER_HTML, {
includeFixedLayout: false,
fixedLayoutLength: 1200,
});
assert.match(article.content, /📍 北京/u);
assert.match(article.content, /30°/u);
});
test('trimWechatInlineBodyByRemovingCards drops tail cards instead of hard slicing', () => {
const html = `${WEATHER_HTML.slice(0, -24)}<div class="section" id="world"><div class="section-title">国际</div><div class="card"><h3>国际一</h3><p>正文一</p></div><div class="card"><h3>国际二</h3><p>正文二</p></div></div></div></body></html>`;
const built = wechatDailyNewsInlineInternals.buildWechatInlineContent(html, {
includeFixedLayout: false,
});
const beforeCards = wechatDailyNewsInlineInternals.countInlineCards(built.content);
const trimmed = wechatDailyNewsInlineInternals.trimWechatInlineBodyByRemovingCards(
built.content,
built.content.length - 260,
{ minCards: 1 },
);
const afterCards = wechatDailyNewsInlineInternals.countInlineCards(trimmed);
assert.ok(beforeCards >= 3);
assert.ok(afterCards < beforeCards);
assert.ok(trimmed.length < built.content.length);
assert.match(trimmed, /<\/section>\s*$/u);
});
test('section list numbering starts at 01 after feature cards', () => {
const html = `<!doctype html><html><body>
<div class="hero"><h1>每日新闻早报</h1><div class="subtitle">头条一 · 头条二</div></div>
<div class="section" id="headlines">
<div class="section-title">今日要闻</div>
<div class="card highlight-box"><h3>头条卡</h3><p>重点摘要</p></div>
<div class="card"><h3>普通要闻</h3><p>普通摘要</p></div>
</div>
<div class="section" id="world">
<div class="section-title">国际热点</div>
<div class="card"><h3>国际一</h3><p>国际摘要一</p></div>
</div>
</body></html>`;
const built = wechatDailyNewsInlineInternals.buildWechatInlineContent(html, {
includeFixedLayout: false,
});
assert.match(built.content, /头条卡[\s\S]*?01<\/span> <strong>普通要闻/u);
assert.match(built.content, /WORLD[\s\S]*?01<\/span> <strong>国际一/u);
assert.doesNotMatch(built.content, /02<\/span> <strong>普通要闻/u);
});
test('hero highlights render as a separate readable digest', () => {
const digest = wechatDailyNewsInlineInternals.renderTodayDigest(
'<div class="subtitle">平陆运河通航 · 海上火箭发射 · 全球市场聚焦议息</div>',
);
assert.match(digest, /今日导读/u);
assert.match(digest, /平陆运河通航<br>/u);
assert.match(digest, /海上火箭发射<br>/u);
});
test('renderHero does not insert the cover image as a large logo', () => {
const html = `<div class="hero"><h1>每日新闻早报</h1><div class="date-badge">9月16日</div></div>
<meta name="mindspace-cover" content='{"cover":"https://mmbiz.qpic.cn/logo.png"}'>`;
const rendered = wechatDailyNewsInlineInternals.renderHero(html, new Map([
['https://mmbiz.qpic.cn/logo.png', 'https://mmbiz.qpic.cn/logo.png'],
]));
assert.match(rendered, /每日新闻早报/u);
assert.doesNotMatch(rendered, /<img\b/u);
assert.doesNotMatch(rendered, /TKMIND · DAILY BRIEF/u);
});