import { describe, expect, it, vi, afterEach } from 'vitest';
import { load } from 'cheerio';
import Parser from 'rss-parser';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import { exportedForTestingOnly, WeChatMpError, fetchArticle, finishArticleItem, fixArticleContent, normalizeUrl } from '@/utils/wechat-mp';
const { toggleWerror, ExtractMetadata, showTypeMapReverse } = exportedForTestingOnly;
vi.mock('@/utils/request-rewriter', () => ({ default: null }));
const { default: app } = await import('@/app');
const parser = new Parser();
afterEach(() => toggleWerror(false));
const expectedItem: {
title: string;
summary: string;
author: string;
mpName: string;
link: string;
} = {
title: 'title',
summary: 'summary',
author: 'author',
mpName: 'mpName',
link: '', // to be filled
};
// date from the cache will be an ISO8601 string, so we need to use this function
const compareDate = (date1, date2) => {
date1 = typeof date1 === 'string' ? new Date(date1) : date1;
date2 = typeof date2 === 'string' ? new Date(date2) : date2;
return date1.getTime() === date2.getTime();
};
const genScriptHtmlStr = (script: string) => `
`;
const testFetchArticleFinishArticleItem = async (path: string, { setMpNameAsAuthor = false, skipLink = false } = {}) => {
const ct = 1_636_626_300;
const httpsUrl = `https://mp.weixin.qq.com/rsshub_test${path}`;
const httpUrl = 'http' + httpsUrl.slice(5);
const expectedDate = new Date(ct * 1000);
const expectedItem_ = {
...expectedItem,
link: httpsUrl,
};
const fetchArticleItem = await fetchArticle(httpUrl);
expect(compareDate(fetchArticleItem.pubDate, expectedDate)).toBe(true);
expect(fetchArticleItem).toMatchObject(expectedItem_);
const ToBeFinishedArticleItem = { link: httpUrl };
const expectedFinishedArticleItem = { ...fetchArticleItem };
expectedFinishedArticleItem.author = setMpNameAsAuthor ? ' +
'' +
'' +
'
' +
'' +
'Line1 {' +
'Line2' +
'Line3 }' +
'' +
'Line1 {' +
'
' +
'Line2' +
'
' +
'Line3 }' +
'
' +
'
test
test
test
' + 'test
test
' + 'test
test
test
test
' + 'test
' + '
';
const expectedHtmlImg = Array.from({ length: 3 + 1 }).join('
');
$ = load(divHeader + htmlImg + divFooter);
expect(fixArticleContent(htmlImg)).toBe(expectedHtmlImg);
expect(fixArticleContent($('div#js_content.rich_media_content'))).toBe(expectedHtmlImg);
expect(fixArticleContent(htmlImg, true)).toBe(htmlImg);
expect(fixArticleContent($('div#js_content.rich_media_content'), true)).toBe(htmlImg);
expect(fixArticleContent('')).toBe('');
expect(fixArticleContent()).toBe('');
expect(fixArticleContent($('div#something_not_in.the_document_tree'))).toBe('');
});
it('normalizeUrl', () => {
const mpRoot = 'https://mp.weixin.qq.com';
const mpArticleRoot = mpRoot + '/s';
const shortUrl = mpArticleRoot + '/-rwvHhqYbKGCVFeXRNknYQ';
const shortUrlWithQueryAndHash = shortUrl + '?foo=bar#baz';
expect(normalizeUrl(shortUrlWithQueryAndHash)).toBe(shortUrl);
const longUrlShortened = mpArticleRoot + '?__biz=MzA4MjQxNjQzMA==' + '&mid=2768628484' + '&idx=1' + '&sn=93dcc54ce807f7793739ee2fd2377056';
const longUrl = longUrlShortened + '&chksm=bf774d458800c453c94cae866093680e6cac6a1f02cab7e82683f82f35f7f487e2daa1dcde20' + '&scene=75' + '#wechat_redirect';
expect(normalizeUrl(longUrl)).toBe(longUrlShortened);
const temporaryUrlShortened =
mpArticleRoot + '?src=11' + '×tamp=1620536401' + '&ver=3057' + '&signature=vCDI8FQcumnNGv4ScvFP-swQRlirdQSqTfjS8m-oFzgHMkqlNM3ljzjSevcjXLC-z-n0RzzMkNt-lwKMUaskfaqFFrpYZNq4ZCKkFFGj8L*KvH780aEUBJFvWTGmMGLC';
const temporaryUrl = temporaryUrlShortened + '&new=1#foo';
expect(normalizeUrl(temporaryUrl)).toBe(temporaryUrlShortened);
const somethingElse = mpRoot + '/something/else?__biz=foo&mid=bar&idx=baz&sn=qux';
const somethingElseWithHash = somethingElse + '#foo';
expect(normalizeUrl(somethingElseWithHash.replace('https://', 'http://'))).toBe(somethingElse);
const notWechatMp = 'https://im.not.wechat.mp/and/an/error/is/expected';
expect(() => normalizeUrl(notWechatMp)).toThrow('URL host must be "mp.weixin.qq.com"');
expect(normalizeUrl(notWechatMp, true)).toBe(notWechatMp);
const unknownSearchParam = mpArticleRoot + '?unknown=param';
toggleWerror(false);
expect(normalizeUrl(unknownSearchParam)).toBe(unknownSearchParam);
toggleWerror(true);
expect(() => normalizeUrl(unknownSearchParam)).toThrow('WarningAsError: unknown URL search parameters');
const unknownPath = mpRoot + '/unknown/path';
toggleWerror(false);
expect(normalizeUrl(unknownPath)).toBe(unknownPath);
toggleWerror(true);
expect(() => normalizeUrl(unknownPath, true)).toThrow('WarningAsError: unknown URL path');
const ampEscapedUrl = longUrl.replaceAll('&', '&');
expect(normalizeUrl(ampEscapedUrl)).toBe(longUrlShortened);
});
it('fetchArticle_&_finishArticleItem_appMsg', async () => {
const fetchArticleItem = await testFetchArticleFinishArticleItem('/appMsg');
const $ = load(fetchArticleItem.description);
expect($('iframe').attr()).toMatchObject({
src:
'https://v.qq.com/txp/iframe/player.html?origin=https%3A%2F%2Fmp.weixin.qq.com' +
'&containerId=js_tx_video_container_0.3863487104715233&vid=fake&width=677&height=380.8125' +
'&autoplay=false&allowFullScreen=true&chid=17&full=true&show1080p=false&isDebugIframe=false',
width: '677',
height: '380.8125',
});
expect($('audio').attr()).toMatchObject({
src: 'https://res.wx.qq.com/voice/getvoice?mediaid=rsshub_test',
title: 'title',
});
expect($('a').attr()).toMatchObject({
href: 'https://mp.weixin.qq.com/rsshub_test/fake',
});
expect(fetchArticleItem.description).toContain('description');
expect(fetchArticleItem.description).toContain('📍发表于:中国 福建');
expect(fetchArticleItem.description).toContain('🔗️ 阅读原文');
});
it('fetchArticle_&_finishArticleItem_img', async () => {
const fetchArticleItem = await testFetchArticleFinishArticleItem('/img');
const $ = load(fetchArticleItem.description);
expect($.text()).toBe('summary');
expect($('img:nth-of-type(1)').attr()).toMatchObject({
src: 'https://mmbiz.qpic.cn/rsshub_test/fake_img_1/0?wx_fmt=jpeg',
});
expect($('img:nth-of-type(2)').attr()).toMatchObject({
src: 'https://mmbiz.qpic.cn/rsshub_test/fake_img_2/0?wx_fmt=jpeg',
});
});
it('fetchArticle_&_finishArticleItem_audio', async () => {
const fetchArticleItem = await testFetchArticleFinishArticleItem('/audio');
const $ = load(fetchArticleItem.description);
expect($.text()).toBe('summary');
expect($('audio').attr()).toMatchObject({
controls: '',
src: 'https://res.wx.qq.com/voice/getvoice?mediaid=rsshub_test_voiceid_1',
style: 'width:100%',
title: 'title',
});
expect(fetchArticleItem).toMatchObject({
enclosure_type: 'audio/mp3',
enclosure_url: 'https://res.wx.qq.com/voice/getvoice?mediaid=rsshub_test_voiceid_1',
itunes_duration: '6567',
});
});
it('fetchArticle_&_finishArticleItem_video', async () => {
const fetchArticleItem = await testFetchArticleFinishArticleItem('/video');
const $ = load(fetchArticleItem.description);
expect($.text()).toBe('summary');
expect($('img').attr()).toMatchObject({
src: 'https://mmbiz.qpic.cn/rsshub_test/og_img_1/0?wx_fmt=jpeg',
});
});
it('fetchArticle_&_finishArticleItem_fallback', async () => {
const fetchArticleItem = await testFetchArticleFinishArticleItem('/fallback');
const $ = load(fetchArticleItem.description);
expect($.text()).toBe('summary');
expect($('img').attr()).toMatchObject({
src: 'https://mmbiz.qpic.cn/rsshub_test/og_img_1/0?wx_fmt=jpeg',
});
});
it('finishArticleItem_param', async () => {
await testFetchArticleFinishArticleItem('/fallback', { setMpNameAsAuthor: false, skipLink: false });
await testFetchArticleFinishArticleItem('/fallback', { setMpNameAsAuthor: true, skipLink: false });
await testFetchArticleFinishArticleItem('/fallback', { setMpNameAsAuthor: false, skipLink: true });
await testFetchArticleFinishArticleItem('/fallback', { setMpNameAsAuthor: true, skipLink: true });
});
it('hit_waf', async () => {
try {
await fetchArticle('https://mp.weixin.qq.com/s/rsshub_test_hit_waf');
expect.unreachable('Should throw an error');
} catch (error) {
expect(error).toBeInstanceOf(WeChatMpError);
expect((