Files
doodle-mini/tests/paper-sheet.test.mjs
2026-10-23 16:53:22 +08:00

309 lines
11 KiB
JavaScript

import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { test } from 'node:test';
import vm from 'node:vm';
import ts from 'typescript';
import * as config from '../miniprogram/papersPages/paperSheet/paperSheet.config.ts';
import { PAPER_SHEET_WORKSHEET_DEFINITIONS } from '../miniprogram/config/worksheets/papers.ts';
import * as debugPublish from '../miniprogram/utils/debugPublish.ts';
import { ApiRequestError } from '../miniprogram/services/api-transport.ts';
const route = 'papersPages/paperSheet/paperSheet';
const deferred = () => {
let resolve;
const promise = new Promise((done) => {
resolve = done;
});
return { promise, resolve };
};
/** 使用真实页面和公共分享/导出方法,仅隔离微信、网络、绘图与埋点。 */
function createPage({
getWorksheet,
favorite = async () => true,
check = async () => ({}),
} = {}) {
const toasts = [],
downloads = [],
favorites = [];
const wx = {
getAccountInfoSync: () => ({ miniProgram: { envVersion: 'release' } }),
setNavigationBarTitle() {},
showToast: (options) => toasts.push(options.title),
};
const load = (file, modules) => {
const exports = {};
const source = readFileSync(new URL(file, import.meta.url), 'utf8');
vm.runInNewContext(
ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2020,
},
}).outputText,
{
exports,
wx,
console,
Error,
require(id) {
assert.ok(id in modules, id);
return modules[id];
},
},
);
return exports;
};
const defaultShareConfig = {
title: '分享',
path: '/pages/index/index',
imageUrl: '',
};
const mixin = load('../miniprogram/base/pageMixin.ts', {
'../services/member-api': {},
'../services/api-request': {},
'../constants/colors': {},
'../core/draw/baseDraw': {},
'../utils/downloadPrint': {
downloadPrint: async (_canvas, options) => downloads.push(options),
},
'../utils/shareGuide': {
shouldUnlockWithAd: () => false,
shouldShowShareGuide: () => false,
},
'../utils/tracker': { default: new Proxy({}, { get: () => () => {} }) },
'../config/config': { defaultShareConfig },
'../utils/debugPublish': {
...debugPublish,
isDebugPublishEnabled: () =>
wx.getAccountInfoSync().miniProgram.envVersion === 'develop',
},
});
let options;
load('../miniprogram/papersPages/paperSheet/paperSheet.ts', {
'./draw/paperDrawService': {},
'./paperSheet.config': config,
'../../base/pageMixin': {
...mixin,
createPage(value, settings) {
options = mixin.applyPageMixin(value, settings);
},
},
'../../config/config': { defaultShareConfig },
'../../services/api-request': { ApiRequestError },
'../../services/content-api': {
getWorksheet:
getWorksheet ||
(async (id) => ({
id,
rendering: {
rendererKey: 'paper-sheet',
paperType: config.resolvePaperEntry({ id }).paperType,
},
})),
},
'../../utils/favorites': {
addFavorite: async (id) => {
favorites.push(['add', id]);
return favorite(id);
},
removeFavorite: async (id) => {
favorites.push(['remove', id]);
return favorite(id);
},
batchCheckFavorited: check,
},
});
const page = {
...options,
route,
data: structuredClone(options.data),
setData(value, callback) {
Object.assign(this.data, value);
callback?.();
},
drawService: { async draw() {} },
canvas: {},
};
return { page, toasts, downloads, favorites, wx };
}
test('十种独立条目复用绘制器,旧入口/type 分享兼容,独立 ID 不受额外 type 干扰', () => {
assert.equal(PAPER_SHEET_WORKSHEET_DEFINITIONS.length, 11);
for (const paper of config.PAPER_TYPE_OPTIONS) {
const id = config.getWorksheetIdForPaperType(paper.id);
const { entry, paperType } = config.resolvePaperEntry({
id,
type: 'unknown',
});
assert.equal(paperType, paper.id);
assert.equal(entry.rendererKey, 'paper-sheet');
assert.equal(
config.getPublishMetaByMode(id).path,
`/${route}?id=${id}`,
);
}
assert.equal(config.resolvePaperEntry({}).entry.id, 'paper-sheet');
assert.equal(
config.resolvePaperEntry({ id: 'paper-sheet', type: 'mi' }).paperType,
'mi',
);
assert.equal(
config.resolvePaperEntry({ id: 'paper-sheet', mode: 'vertical' })
.paperType,
'vertical',
);
assert.throws(() => config.resolvePaperEntry({ id: 'paper-unknown' }));
});
test('切换后页面身份、收藏、分享、发布和实际导出统计均属于当前条目', async () => {
const { page, downloads, favorites } = createPage();
await page.onLoad({ id: 'paper-mi' });
assert.equal(page.data.pageTitle, '米字格');
assert.equal(page.data.functionId, 'paper-mi');
assert.equal(page.data.selectedPaperType, 'mi');
assert.equal(page.onShareAppMessage().path, `/${route}?id=paper-mi`);
await page.onPreviewFavorite();
assert.deepEqual(favorites, [['add', 'paper-mi']]);
await page.exportToPrint();
assert.equal(downloads[0].worksheetId, 'paper-mi');
page.setData({
debugPublishVisible: true,
debugPublishMeta: page.getPublishMeta(),
});
await page.onSelectPaperType({
currentTarget: { dataset: { id: 'tian' } },
});
assert.equal(page.data.debugPublishVisible, false);
assert.equal(page.data.debugPublishMeta, null);
assert.equal(page.data.functionId, 'paper-tian');
assert.equal(page.data.isPreviewFavorite, false);
assert.equal(page.getPublishMeta().id, 'paper-tian');
assert.equal(page.onShareTimeline().query, 'id=paper-tian');
await page.onSelectPaperType({ currentTarget: { dataset: { id: 'mi' } } });
assert.equal(page.data.isPreviewFavorite, true);
await page.onPreviewFavorite();
assert.deepEqual(favorites[1], ['remove', 'paper-mi']);
await page.onLoad({ id: 'paper-sheet', type: 'vertical' });
assert.equal(
page.onShareAppMessage().path,
`/${route}?id=paper-sheet&mode=vertical`,
);
});
test('快速切换丢弃旧响应,加载期间禁止导出,未知条目/下架/配置错误不生成其他纸张', async () => {
const old = deferred();
const { page, downloads, toasts } = createPage({
getWorksheet: (id) =>
id === 'paper-mi'
? old.promise
: Promise.resolve({
rendering: {
rendererKey: 'paper-sheet',
paperType: 'tian',
},
}),
});
const first = page.onLoad({ id: 'paper-mi' });
await page.exportToPrint();
assert.equal(downloads.length, 0);
await page.onSelectPaperType({
currentTarget: { dataset: { id: 'tian' } },
});
old.resolve({ rendering: { rendererKey: 'paper-sheet', paperType: 'mi' } });
await first;
assert.equal(page.data.worksheetId, 'paper-tian');
await page.openEntry({ id: 'paper-unknown' });
assert.equal(page.data.hasContent, false);
assert.ok(toasts.length);
for (const getWorksheet of [
async () => {
throw new ApiRequestError('已下架', 404);
},
async () => ({
rendering: { rendererKey: 'paper-sheet', paperType: 'tian' },
}),
]) {
const blocked = createPage({ getWorksheet }).page;
await blocked.onLoad({ id: 'paper-mi' });
assert.equal(blocked.data.hasContent, false);
}
});
test('收藏失败不报成功,慢速查询不能覆盖新收藏,切换时收藏响应不写错条目', async () => {
const pending = deferred();
const { page, toasts } = createPage({ favorite: async () => false });
await page.onLoad({ id: 'paper-mi' });
await page.onPreviewFavorite();
assert.equal(page.data.isPreviewFavorite, false);
assert.ok(toasts.some((title) => title.includes('失败')));
const active = createPage({ check: () => pending.promise }).page;
await active.onLoad({ id: 'paper-mi' });
await active.onPreviewFavorite();
pending.resolve({ 'paper-mi': false });
await new Promise((resolve) => setImmediate(resolve));
assert.equal(active.data.isPreviewFavorite, true);
const write = deferred();
const switching = createPage({ favorite: () => write.promise }).page;
await switching.onLoad({ id: 'paper-mi' });
const saving = switching.onPreviewFavorite();
await switching.openEntry({ id: 'paper-tian' });
write.resolve(true);
await saving;
assert.equal(switching.data.isPreviewFavorite, false);
assert.equal(switching._favoritedMap['paper-mi'], true);
});
test('开发版离线预览十种已注册纸张,切换不请求公开详情,未知 ID 仍不可用', async () => {
let requests = 0;
const { page, wx, toasts } = createPage({
getWorksheet: async () => {
requests++;
throw new Error('开发预览不应请求公开详情');
},
});
wx.getAccountInfoSync = () => ({ miniProgram: { envVersion: 'develop' } });
await page.onLoad({ id: 'paper-sheet' });
for (const paper of config.PAPER_TYPE_OPTIONS) {
await page.onSelectPaperType({
currentTarget: { dataset: { id: paper.id } },
});
assert.equal(page.data.hasContent, true);
assert.equal(page.data.selectedPaperType, paper.id);
assert.equal(
page.getPublishMeta().id,
config.getWorksheetIdForPaperType(paper.id),
);
}
assert.equal(requests, 0);
assert.equal(toasts.length, 0);
await page.openEntry({ id: 'paper-unknown' });
assert.equal(page.data.hasContent, false);
assert.equal(requests, 0);
assert.equal(toasts.length, 1);
});
test('体验版与正式版均校验公开详情,404 和网络故障不能降级成本地预览', async () => {
for (const version of ['trial', 'release']) {
for (const failure of [
new ApiRequestError('未发布', 404),
new ApiRequestError('网络故障', 0),
]) {
let requests = 0;
const { page, wx } = createPage({
getWorksheet: async () => {
requests++;
throw failure;
},
});
wx.getAccountInfoSync = () => ({
miniProgram: { envVersion: version },
});
await page.onLoad({ id: 'paper-square' });
assert.equal(requests, 1);
assert.equal(page.data.hasContent, false);
assert.equal(page.data.isWorksheetReady, false);
}
}
});