210 lines
9.4 KiB
JavaScript
210 lines
9.4 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
const VideoPages = require('../public/js/video-pages.js');
|
|
|
|
function fileAccess(name, mediaType, url) {
|
|
return {
|
|
fileId: '2060000000000000090',
|
|
ossId: '2060000000000000091',
|
|
fileName: name,
|
|
mediaType,
|
|
accessUrl: url
|
|
};
|
|
}
|
|
|
|
test('视频页只从 URL 读取真实家谱和视频编号', () => {
|
|
assert.equal(VideoPages.getCurrentGenealogyId('?genealogyId=2060000000000000001&videoId=2060000000000000002'), '2060000000000000001');
|
|
assert.equal(VideoPages.getCurrentVideoId('?genealogyId=2060000000000000001&videoId=2060000000000000002'), '2060000000000000002');
|
|
assert.equal(VideoPages.getCurrentGenealogyId(''), '');
|
|
assert.equal(VideoPages.getCurrentVideoId('?videoId=unsafe'), '');
|
|
});
|
|
|
|
test('视频写入只构造 YAML VideoBody 字段并保持 OSS ID 为字符串', () => {
|
|
assert.deepEqual(VideoPages.buildVideoBody({
|
|
videoTitle: ' 家族活动记录 ',
|
|
videoDesc: ' 清明祭祖活动视频 ',
|
|
coverOssId: '2060000000000000001',
|
|
videoOssId: '2060000000000000002',
|
|
durationSeconds: '181',
|
|
sortOrder: '3',
|
|
status: '0',
|
|
publisherUserId: 'should-not-send',
|
|
viewCount: 99
|
|
}), {
|
|
videoTitle: '家族活动记录',
|
|
videoDesc: '清明祭祖活动视频',
|
|
coverOssId: '2060000000000000001',
|
|
videoOssId: '2060000000000000002',
|
|
durationSeconds: 181,
|
|
sortOrder: 3,
|
|
status: '0'
|
|
});
|
|
});
|
|
|
|
test('视频校验标题、上传文件、时长、排序和状态', () => {
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '', videoOssId: '1' }), '请填写视频标题');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题' }), '请先选择并上传视频文件');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '0' }), '视频文件上传结果无效');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '1', coverOssId: 'abc' }), '封面上传结果无效');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '1', durationSeconds: -1 }), '视频时长不能小于 0');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '1', sortOrder: undefined, invalidSortOrder: true }), '排序值必须是安全整数');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '1', status: '1' }), '当前 PC 无法重新读取停用视频,暂不开放停用');
|
|
assert.equal(VideoPages.validateVideoBody({ videoTitle: '标题', videoOssId: '1', status: '0' }), '');
|
|
});
|
|
|
|
test('视频响应使用 PC VideoVo 字段并拒绝不安全长 ID', () => {
|
|
assert.deepEqual(VideoPages.normalizeVideo({
|
|
videoId: '2060000000000000001',
|
|
genealogyId: '2060000000000000002',
|
|
genealogyNo: 'G20260729001',
|
|
genealogyName: '叶氏家谱',
|
|
surname: '叶',
|
|
videoTitle: '祭祖活动记录',
|
|
videoDesc: '活动影像',
|
|
coverFile: fileAccess('cover.jpg', 'image/jpeg', 'https://files.example/video-cover.jpg'),
|
|
videoFile: fileAccess('video.mp4', 'video/mp4', 'https://files.example/video.mp4'),
|
|
durationSeconds: 180,
|
|
publisherUserId: '2060000000000000005',
|
|
publisherNickName: '叶子',
|
|
publisherPhone: '19100000000',
|
|
publishTime: '2026-07-29 10:00:00',
|
|
viewCount: 12,
|
|
sortOrder: 1,
|
|
status: '0',
|
|
remark: '归档'
|
|
}), {
|
|
videoId: '2060000000000000001',
|
|
genealogyId: '2060000000000000002',
|
|
genealogyNo: 'G20260729001',
|
|
genealogyName: '叶氏家谱',
|
|
surname: '叶',
|
|
videoTitle: '祭祖活动记录',
|
|
videoDesc: '活动影像',
|
|
coverFile: {
|
|
fileId: '2060000000000000090',
|
|
fileName: 'cover.jpg',
|
|
mediaType: 'image/jpeg',
|
|
accessUrl: 'https://files.example/video-cover.jpg'
|
|
},
|
|
videoFile: {
|
|
fileId: '2060000000000000090',
|
|
fileName: 'video.mp4',
|
|
mediaType: 'video/mp4',
|
|
accessUrl: 'https://files.example/video.mp4'
|
|
},
|
|
durationSeconds: 180,
|
|
publisherUserId: '2060000000000000005',
|
|
publisherNickName: '叶子',
|
|
publisherPhone: '19100000000',
|
|
publishTime: '2026-07-29 10:00:00',
|
|
viewCount: 12,
|
|
sortOrder: 1,
|
|
status: '0',
|
|
remark: '归档'
|
|
});
|
|
assert.equal(VideoPages.normalizeVideo({
|
|
videoId: Number('2060000000000000001'),
|
|
genealogyId: '2',
|
|
videoTitle: '标题',
|
|
videoFile: fileAccess('video.mp4', 'video/mp4', 'https://files.example/video.mp4'),
|
|
status: '0'
|
|
}), null);
|
|
});
|
|
|
|
test('视频渲染展示业务信息但不直接暴露 OSS ID 和发布人手机号', () => {
|
|
const video = VideoPages.normalizeVideo({
|
|
videoId: '1',
|
|
genealogyId: '2',
|
|
videoTitle: '<script>alert(1)</script>',
|
|
videoDesc: '活动影像',
|
|
coverFile: fileAccess('cover.jpg', 'image/jpeg', 'https://files.example/video-cover.jpg'),
|
|
videoFile: fileAccess('video.mp4', 'video/mp4', 'https://files.example/video.mp4'),
|
|
durationSeconds: 180,
|
|
publisherUserId: '5',
|
|
publisherNickName: '叶子',
|
|
publisherPhone: '19100000000',
|
|
publishTime: '2026-07-29 10:00:00',
|
|
viewCount: 12,
|
|
sortOrder: 1,
|
|
status: '0'
|
|
});
|
|
const html = VideoPages.renderVideoDetail(video, true);
|
|
|
|
assert.doesNotMatch(html, /<script>/);
|
|
assert.match(html, /活动影像/);
|
|
assert.match(html, /叶子/);
|
|
assert.match(html, /已上传/);
|
|
assert.doesNotMatch(html, />3</);
|
|
assert.doesNotMatch(html, />4</);
|
|
assert.doesNotMatch(html, /19100000000/);
|
|
});
|
|
|
|
test('视频文件元数据时长按秒取整且拒绝无效值', () => {
|
|
assert.equal(VideoPages.normalizeDurationSeconds(180.2), 181);
|
|
assert.equal(VideoPages.normalizeDurationSeconds(0), 0);
|
|
assert.equal(VideoPages.normalizeDurationSeconds(-1), undefined);
|
|
assert.equal(VideoPages.normalizeDurationSeconds(Infinity), undefined);
|
|
});
|
|
|
|
test('视频维护资格只接受服务端返回的内容或管理布尔能力', () => {
|
|
const script = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'video-pages.js'), 'utf8');
|
|
|
|
assert.equal(VideoPages.canEditVideos({ canEditContent: true }), true);
|
|
assert.equal(VideoPages.canEditVideos({ canManage: true }), true);
|
|
assert.equal(VideoPages.canEditVideos({ canEditContent: 'true', canManage: false }), false);
|
|
assert.match(script, /setManagementAccess\(canEditVideos\(result\[0\]\)\)/);
|
|
});
|
|
|
|
test('视频列表和编辑页开放真实 PC 功能且不允许手填 OSS ID', () => {
|
|
const root = path.join(__dirname, '..');
|
|
const listPage = fs.readFileSync(path.join(root, 'profile-video.html'), 'utf8');
|
|
const editPage = fs.readFileSync(path.join(root, 'profile-video-edit.html'), 'utf8');
|
|
const styles = fs.readFileSync(path.join(root, 'public', 'css', 'profile-module.css'), 'utf8');
|
|
|
|
assert.doesNotMatch(listPage, /data-feature-status="pending"/);
|
|
assert.match(listPage, /data-video-list/);
|
|
assert.match(listPage, /data-video-detail/);
|
|
assert.match(listPage, /data-video-management hidden/);
|
|
assert.match(listPage, /src="public\/js\/video-pages\.js"/);
|
|
assert.match(editPage, /data-video-form data-video-management hidden/);
|
|
assert.match(editPage, /data-video-form data-video-management hidden novalidate/);
|
|
assert.match(editPage, /class="field-required"/);
|
|
assert.match(editPage, /<div class="form-status" data-video-form-status/);
|
|
assert.match(editPage, /name="videoTitle"/);
|
|
assert.match(editPage, /name="videoDesc"/);
|
|
assert.match(editPage, /name="coverOssId" type="hidden"/);
|
|
assert.match(editPage, /name="videoOssId" type="hidden"/);
|
|
assert.match(editPage, /name="durationSeconds"[^>]+readonly/);
|
|
assert.doesNotMatch(editPage, /name="(?:coverOssId|videoOssId)"[^>]*type="text"/);
|
|
assert.match(editPage, /public\/js\/md5\.js/);
|
|
assert.match(editPage, /public\/js\/upload-pages\.js/);
|
|
assert.match(editPage, /public\/js\/video-pages\.js/);
|
|
assert.match(styles, /\[data-video-management\]\[hidden\]\s*\{[^}]*display:\s*none\s*!important/s);
|
|
});
|
|
|
|
test('视频编辑将权限读取、校验、保存中和失败状态接入公共状态组件', () => {
|
|
const root = path.join(__dirname, '..');
|
|
const listPage = fs.readFileSync(path.join(root, 'profile-video.html'), 'utf8');
|
|
const editPage = fs.readFileSync(path.join(root, 'profile-video-edit.html'), 'utf8');
|
|
const script = fs.readFileSync(path.join(root, 'public', 'js', 'video-pages.js'), 'utf8');
|
|
|
|
assert.match(listPage, /data-video-list[^>]*>[\s\S]*data-api-state="loading"/);
|
|
assert.match(listPage, /data-video-detail[^>]*>[\s\S]*data-api-state="empty"/);
|
|
assert.match(editPage, /data-api-state="loading"[^>]*data-video-permission-placeholder/);
|
|
assert.match(script, /root\.ProfileUI && root\.ProfileUI\.setApiState/);
|
|
assert.match(script, /function setPermissionPlaceholder\(type, message\)/);
|
|
assert.match(script, /setVideoListState\('loading', '正在加载视频…'\)/);
|
|
assert.match(script, /setVideoDetailState\('loading', '正在加载视频详情…'\)/);
|
|
assert.match(script, /setVideoListState\('empty', '暂无家族视频'\)/);
|
|
assert.match(script, /setVideoListState\('forbidden', '当前账号无权查看该家谱视频。'\)/);
|
|
assert.match(script, /setVideoDetailState\(\n\s*getApiStateType\(error\)/);
|
|
assert.match(script, /正在读取家谱权限…', 'loading'/);
|
|
assert.match(script, /setPermissionPlaceholder\('forbidden', '当前账号没有视频维护权限。'\)/);
|
|
assert.match(script, /setFormStatus\(validation, 'error'\)/);
|
|
assert.match(script, /setWritePending\(true, '正在保存…'\)/);
|
|
assert.match(script, /getApiStateType\(error\)/);
|
|
});
|