Files
jiapu/tests/album-pages.test.js
T
2026-08-29 19:06:07 +08:00

246 lines
10 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const modulePath = path.join(__dirname, '..', 'public', 'js', 'album-pages.js');
const AlbumPages = fs.existsSync(modulePath) ? require(modulePath) : {};
function fileAccess(name, url) {
return {
fileId: '2060000000000000090',
ossId: '2060000000000000091',
fileName: name,
mediaType: 'image/jpeg',
accessUrl: url
};
}
function requireFunction(name) {
assert.equal(typeof AlbumPages[name], 'function', `缺少 AlbumPages.${name}`);
return AlbumPages[name];
}
test('相册页面只从 URL 读取安全的家谱、相册和照片编号', () => {
const getCurrentGenealogyId = requireFunction('getCurrentGenealogyId');
const getCurrentAlbumId = requireFunction('getCurrentAlbumId');
assert.equal(getCurrentGenealogyId('?genealogyId=2060000000000000001'), '2060000000000000001');
assert.equal(getCurrentAlbumId('?albumId=2060000000000000002'), '2060000000000000002');
assert.equal(getCurrentAlbumId('?albumId=unsafe'), '');
});
test('相册写入只构造 AlbumBody 并固定正常状态', () => {
const buildAlbumBody = requireFunction('buildAlbumBody');
assert.deepEqual(buildAlbumBody({
albumName: ' 祠堂旧影 ',
albumDesc: ' 历史照片 ',
coverOssId: '2060000000000000003',
sortOrder: '2',
status: '1',
albumId: 'must-drop',
photoCount: 99
}), {
albumName: '祠堂旧影',
albumDesc: '历史照片',
coverOssId: '2060000000000000003',
sortOrder: 2,
status: '0'
});
});
test('照片写入只构造 AlbumPhotoBody 并转换拍摄时间', () => {
const buildAlbumPhotoBody = requireFunction('buildAlbumPhotoBody');
assert.deepEqual(buildAlbumPhotoBody({
ossId: '2060000000000000004',
photoTitle: ' 合影 ',
photoDesc: ' 清明祭祖 ',
photographer: ' 宗亲 ',
shootTime: '2026-07-29T10:30',
sortOrder: '3',
status: '1',
albumId: 'must-drop'
}), {
ossId: '2060000000000000004',
photoTitle: '合影',
photoDesc: '清明祭祖',
photographer: '宗亲',
shootTime: '2026-07-29 10:30:00',
sortOrder: 3,
status: '0'
});
});
test('相册和照片校验上传派生 ID、日期、排序和停用状态', () => {
const buildAlbumBody = requireFunction('buildAlbumBody');
const buildAlbumPhotoBody = requireFunction('buildAlbumPhotoBody');
const validateAlbumBody = requireFunction('validateAlbumBody');
const validateAlbumPhotoBody = requireFunction('validateAlbumPhotoBody');
assert.equal(validateAlbumBody({ albumName: '', status: '0' }), '请填写相册名称');
assert.equal(
validateAlbumBody(buildAlbumBody({ albumName: '相册', coverOssId: Number.MAX_SAFE_INTEGER + 1 })),
'封面文件编号无效,请重新选择文件'
);
assert.equal(validateAlbumBody({ albumName: '相册', status: '1' }), '当前 PC 无法重新读取停用相册,暂不开放停用');
assert.equal(validateAlbumPhotoBody({ ossId: '', status: '0' }), '请选择并上传照片');
assert.equal(
validateAlbumPhotoBody(buildAlbumPhotoBody({ ossId: '1', shootTime: '2026-02-30T10:00' })),
'拍摄时间格式无效'
);
assert.equal(
validateAlbumPhotoBody(buildAlbumPhotoBody({ ossId: '1', sortOrder: '1.5' })),
'排序值必须是安全整数'
);
});
test('相册和照片响应使用完整 PC Vo 并拒绝不安全 ID', () => {
const normalizeAlbum = requireFunction('normalizeAlbum');
const normalizeAlbumPhoto = requireFunction('normalizeAlbumPhoto');
const album = normalizeAlbum({
albumId: '2060000000000000001',
genealogyId: '2060000000000000002',
genealogyName: '叶氏家谱',
albumName: '祠堂旧影',
albumDesc: '历史照片',
coverOssId: '2060000000000000003',
photoCount: 2,
sortOrder: 1,
status: '0',
remark: '旧影'
});
const photo = normalizeAlbumPhoto({
photoId: '2060000000000000004',
genealogyId: '2060000000000000002',
albumId: '2060000000000000001',
albumName: '祠堂旧影',
photoFile: fileAccess('photo.jpg', 'https://files.example/photo.jpg'),
photoTitle: '合影',
photoDesc: '清明祭祖',
photographer: '宗亲',
shootTime: '2026-07-29 10:30:00',
sortOrder: 1,
status: '0',
remark: ''
});
assert.equal(album.albumId, '2060000000000000001');
assert.equal(album.photoCount, 2);
assert.equal(photo.photoId, '2060000000000000004');
assert.equal(photo.photoFile.accessUrl, 'https://files.example/photo.jpg');
assert.equal(normalizeAlbum({
albumId: Number('2060000000000000001'),
genealogyId: '2',
albumName: '相册',
status: '0'
}), null);
});
test('相册详情通过列表中的稳定 albumId 重读并匹配', () => {
const findAlbumById = requireFunction('findAlbumById');
const albums = [{
albumId: '1',
genealogyId: '2',
albumName: '旧影',
photoCount: 0,
status: '0'
}];
assert.equal(findAlbumById(albums, '1').albumName, '旧影');
assert.equal(findAlbumById(albums, '9'), null);
assert.equal(findAlbumById({ rows: albums }, '1'), null);
});
test('相册和照片展示转义内容并隐藏 OSS ID 与内部 ID', () => {
const normalizeAlbum = requireFunction('normalizeAlbum');
const normalizeAlbumPhoto = requireFunction('normalizeAlbumPhoto');
const renderAlbumDetail = requireFunction('renderAlbumDetail');
const renderPhotoList = requireFunction('renderPhotoList');
const albumHtml = renderAlbumDetail(normalizeAlbum({
albumId: '1',
genealogyId: '2',
albumName: '<img src=x>旧影',
albumDesc: '<script>alert(1)</script>历史',
coverOssId: '3',
photoCount: 1,
status: '0'
}));
const photoHtml = renderPhotoList([normalizeAlbumPhoto({
photoId: '4',
genealogyId: '2',
albumId: '1',
ossId: '5',
photoTitle: '<img src=x>合影',
photoDesc: '祭祖',
status: '0'
})]);
assert.doesNotMatch(albumHtml + photoHtml, /<script|<img/);
assert.doesNotMatch(albumHtml + photoHtml, />2<|>3<|>4<|>5</);
assert.match(albumHtml + photoHtml, /旧影|历史|合影|祭祖/);
});
test('相册页面拆分列表、编辑和照片详情且不允许手填 OSS ID 或状态', () => {
const projectRoot = path.join(__dirname, '..');
const listPage = fs.readFileSync(path.join(projectRoot, 'profile-album.html'), 'utf8');
const editPage = fs.readFileSync(path.join(projectRoot, 'profile-album-edit.html'), 'utf8');
const detailPage = fs.readFileSync(path.join(projectRoot, 'profile-album-detail.html'), 'utf8');
[listPage, editPage, detailPage].forEach((source) => {
assert.doesNotMatch(source, /data-feature-status="pending"|pending-pages\.js/);
assert.match(source, /public\/js\/album-pages\.js/);
});
assert.match(listPage, /data-album-list/);
assert.match(editPage, /name="albumName"/);
assert.match(editPage, /data-album-form[^>]+novalidate/);
assert.match(editPage, /class="field-required"/);
assert.match(editPage, /<div class="api-status" data-album-form-status/);
assert.match(editPage, /name="coverOssId" type="hidden"/);
assert.match(editPage, /name="status" type="hidden" value="0"/);
assert.match(detailPage, /data-album-detail/);
assert.match(detailPage, /data-album-photo-list/);
assert.match(detailPage, /name="ossId" type="hidden"/);
assert.match(detailPage, /name="shootTime" type="datetime-local"/);
assert.match(detailPage, /name="status" type="hidden" value="0"/);
assert.match(detailPage, /data-album-photo-form novalidate/);
assert.match(detailPage, /<div class="api-status" data-album-photo-status/);
assert.doesNotMatch(editPage + detailPage, /type="text"[^>]+(?:coverOssId|ossId)|name="albumId"|name="photoId"/);
});
test('相册列表、详情和表单将加载、空数据、403 与请求失败写入统一状态组件', () => {
const projectRoot = path.join(__dirname, '..');
const listPage = fs.readFileSync(path.join(projectRoot, 'profile-album.html'), 'utf8');
const detailPage = fs.readFileSync(path.join(projectRoot, 'profile-album-detail.html'), 'utf8');
const source = fs.readFileSync(modulePath, 'utf8');
assert.match(listPage, /data-album-list[^>]*>[\s\S]*data-api-state="loading"/);
assert.match(detailPage, /data-album-detail[^>]*>[\s\S]*data-api-state="loading"/);
assert.match(detailPage, /data-album-photo-list[^>]*>[\s\S]*data-api-state="loading"/);
assert.match(source, /root\.ProfileUI && root\.ProfileUI\.setApiState/);
assert.match(source, /setStatus\('\[data-album-list\]', '正在加载相册…', 'loading'\)/);
assert.match(source, /setStatus\('\[data-album-list\]', '暂无相册', 'empty'\)/);
assert.match(source, /setStatus\('\[data-album-photo-list\]', '正在加载照片…', 'loading'\)/);
assert.match(source, /setStatus\('\[data-album-photo-list\]', '暂无照片', 'empty'\)/);
assert.match(source, /当前账号无权查看该相册照片。/);
assert.match(source, /正在读取家谱权限…', 'loading'/);
assert.match(source, /setStatus\('\[data-album-form-status\]', validation, 'error'\)/);
assert.match(source, /setStatus\('\[data-album-photo-status\]', validation, 'error'\)/);
assert.match(source, /getApiStateType\(error\)/);
});
test('相册与照片只接受后端布尔能力字段,并在所有写路径上门控', () => {
const source = fs.readFileSync(modulePath, 'utf8');
const canEditAlbums = requireFunction('canEditAlbums');
assert.equal(canEditAlbums({ canEditContent: true }), true);
assert.equal(canEditAlbums({ canManage: true }), true);
assert.equal(canEditAlbums({ canEditContent: 'true', canManage: false }), false);
assert.match(source, /canEditContent = canEditAlbums\(detail\)/);
assert.match(source, /if \(writePending \|\| !canEditContent \|\| !genealogyId \|\| redirectUnauthorized\(api\)\) return;/);
assert.match(source, /if \(writePending \|\| !canEditContent \|\| !genealogyId \|\| !albumId \|\| redirectUnauthorized\(api\)\) return;/);
assert.match(source, /if \(writePending \|\| !canEditContent \|\| !genealogyId \|\| !normalizeId\(albumId\) \|\| redirectUnauthorized\(api\)\) return;/);
assert.match(source, /if \(writePending \|\| !canEditContent \|\| !genealogyId \|\| !albumId \|\| !normalizeId\(photoId\) \|\| redirectUnauthorized\(api\)\) return;/);
});