feat:作业纸
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user