b8bcbcb764
让定时任务按新版式推送草稿:热门旅游城市天气、无 Hero logo,并约束生成页必须写城市天气卡。 Co-authored-by: Cursor <cursoragent@cursor.com>
146 lines
7.7 KiB
JavaScript
146 lines
7.7 KiB
JavaScript
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);
|
||
});
|