6d76618c0b
Push daily-news pages to WeChat drafts as inline-styled HTML instead of full-page long images, with automatic trimming to stay within the 20k limit. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
6.0 KiB
JavaScript
172 lines
6.0 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import {
|
|
convertNewsPageHtmlToWechatArticle,
|
|
createWechatNewsMorningDraftService,
|
|
findLatestNewsMorningPage,
|
|
NEWS_MORNING_TEMPLATE_VERSION,
|
|
wechatNewsMorningDraftInternals,
|
|
} from './wechat-news-morning-draft.mjs';
|
|
import { PUBLIC_ZONE_DIR, PUBLISH_ROOT_DIR } from './user-publish.mjs';
|
|
|
|
const SAMPLE_HTML = `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<title>每日新闻早报 · 2026年9月10日</title>
|
|
<meta name="description" content="2026年9月10日新闻热点摘要">
|
|
<style>.hero{background:#b71c1c;color:#fff;}</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="hero">
|
|
<h1>📰 每日新闻早报</h1>
|
|
<div class="date-badge">2026年9月10日 · 星期三</div>
|
|
<div class="subtitle">综合多家权威媒体梳理全网核心热点。</div>
|
|
</div>
|
|
<div class="stats">
|
|
<div class="stat s1"><b>4 大</b><span>核心热点</span></div>
|
|
</div>
|
|
<div class="section" id="headlines">
|
|
<div class="section-title">📰 今日要闻</div>
|
|
<div class="card">
|
|
<span class="tag tag-red">要闻</span>
|
|
<h3>示例热点</h3>
|
|
<p>这是示例正文。</p>
|
|
<div class="source">来源:示例媒体</div>
|
|
</div>
|
|
</div>
|
|
<div class="trending">
|
|
<h3>热榜</h3>
|
|
<div class="chips"><span class="chip"><span class="emoji">🔥</span>话题 A</span></div>
|
|
<p>今日热榜整体偏科技。</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>`;
|
|
|
|
function createPool(seedRow = null) {
|
|
const state = { row: seedRow, runs: [] };
|
|
return {
|
|
async query(sql, params) {
|
|
if (sql.includes('CREATE TABLE')) return [[], []];
|
|
if (sql.includes('SELECT config_json')) return [state.row ? [state.row] : [], []];
|
|
if (sql.includes('INSERT INTO h5_wechat_admin_config')) {
|
|
state.row = {
|
|
config_json: params[1],
|
|
updated_by: params[2],
|
|
updated_at: params[3],
|
|
};
|
|
return [[], []];
|
|
}
|
|
if (sql.includes('INSERT INTO h5_wechat_news_draft_runs')) {
|
|
state.runs.unshift({
|
|
id: params[0],
|
|
status: params[1],
|
|
pageSlug: params[2],
|
|
pageUrl: params[3],
|
|
draftMediaId: params[4],
|
|
errorMessage: params[5],
|
|
triggeredBy: params[6],
|
|
createdAt: params[7],
|
|
});
|
|
return [[], []];
|
|
}
|
|
if (sql.includes('FROM h5_wechat_news_draft_runs')) {
|
|
return [state.runs, []];
|
|
}
|
|
throw new Error(`Unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
test('convertNewsPageHtmlToWechatArticle converts daily-news to inline html', () => {
|
|
const article = convertNewsPageHtmlToWechatArticle(SAMPLE_HTML, {
|
|
publicUrl: 'https://m.tkmind.cn/MindSpace/demo/public/daily-news-0910.html',
|
|
});
|
|
assert.match(article.title, /2026年9月10日/u);
|
|
assert.equal(article.contentMode, 'inline_html');
|
|
assert.match(article.content, /每日新闻早报/u);
|
|
assert.match(article.content, /示例热点/u);
|
|
assert.match(article.content, /点击阅读原文/u);
|
|
});
|
|
|
|
test('buildDailyNewsWechatHtmlContent preserves style and body markup', () => {
|
|
const content = wechatNewsMorningDraftInternals.buildDailyNewsWechatHtmlContent(SAMPLE_HTML, {
|
|
publicUrl: 'https://m.tkmind.cn/MindSpace/demo/public/daily-news-0910.html',
|
|
publicBaseUrl: 'https://m.tkmind.cn',
|
|
userId: 'demo-user',
|
|
});
|
|
assert.match(content, /<style>/u);
|
|
assert.match(content, /每日新闻早报/u);
|
|
assert.match(content, /class="hero"/u);
|
|
assert.match(content, /示例热点/u);
|
|
});
|
|
|
|
test('findLatestNewsMorningPage prefers dated slug for today', async () => {
|
|
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'news-draft-'));
|
|
const userId = 'user-1';
|
|
const publicDir = path.join(root, PUBLISH_ROOT_DIR, userId, PUBLIC_ZONE_DIR);
|
|
await fs.promises.mkdir(publicDir, { recursive: true });
|
|
const oldPath = path.join(publicDir, 'news-hotspots-2026-08-28.html');
|
|
const todayPath = path.join(publicDir, 'news-hotspots-2026-09-10.html');
|
|
await fs.promises.writeFile(oldPath, SAMPLE_HTML);
|
|
await fs.promises.writeFile(todayPath, SAMPLE_HTML);
|
|
await fs.promises.utimes(oldPath, new Date('2026-09-09'), new Date('2026-09-09'));
|
|
await fs.promises.utimes(todayPath, new Date('2026-09-10'), new Date('2026-09-10'));
|
|
|
|
const page = findLatestNewsMorningPage({
|
|
h5Root: root,
|
|
userId,
|
|
slugPattern: 'news-hotspots-*',
|
|
date: new Date('2026-09-10T01:00:00+08:00'),
|
|
});
|
|
assert.equal(page.slug, 'news-hotspots-2026-09-10');
|
|
});
|
|
|
|
test('news morning draft service persists config and records failed push', async () => {
|
|
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'news-draft-service-'));
|
|
const userId = 'user-2';
|
|
const publicDir = path.join(root, PUBLISH_ROOT_DIR, userId, PUBLIC_ZONE_DIR);
|
|
await fs.promises.mkdir(publicDir, { recursive: true });
|
|
await fs.promises.writeFile(path.join(publicDir, 'daily-news-0910.html'), SAMPLE_HTML);
|
|
|
|
const service = createWechatNewsMorningDraftService(createPool(), {
|
|
mpConfig: { enabled: false },
|
|
h5Root: root,
|
|
env: {},
|
|
});
|
|
|
|
const updated = await service.updateConfig(
|
|
{
|
|
enabled: true,
|
|
sourceUserId: userId,
|
|
pushHour: 6,
|
|
pushMinute: 15,
|
|
author: 'TKMind',
|
|
},
|
|
{ updatedBy: 'admin-1' },
|
|
);
|
|
assert.equal(updated.enabled, true);
|
|
assert.equal(updated.sourceUserId, userId);
|
|
assert.equal(updated.templateVersion, NEWS_MORNING_TEMPLATE_VERSION);
|
|
|
|
const preview = await service.preview();
|
|
assert.equal(preview.page.slug, 'daily-news-0910');
|
|
assert.match(preview.template.spec, /0910|2026-09-10/u);
|
|
|
|
await assert.rejects(
|
|
() => service.pushDraft({ triggeredBy: 'admin-1' }),
|
|
/微信服务号未启用/u,
|
|
);
|
|
const runs = await service.listRuns();
|
|
assert.equal(runs[0].status, 'failed');
|
|
});
|
|
|
|
test('internals normalize booleans consistently', () => {
|
|
assert.equal(wechatNewsMorningDraftInternals.normalizeBoolean('1', false), true);
|
|
assert.equal(wechatNewsMorningDraftInternals.normalizeBoolean('off', true), false);
|
|
});
|