< 模块:赋值
此模块的文档可以在模块:赋值/testcases/doc创建
-- 模块:赋值/testcases
-- 测试覆盖:14个导出函数 × 正常路径 + 边界 + 已知bug
-- 运行:={{#invoke:赋值/testcases|run}}=
-- 调试:=require('模块:赋值/testcases').run()=
local m = require('Module:赋值')
local ScribuntoUnit = require('Module:ScribuntoUnit')
local suite = ScribuntoUnit:new()
-- Mock frame — 模拟 {{#invoke}} 传入,缺失参数返回空串
local function makeFrame(args)
local frame = {}
frame.args = setmetatable({}, {__index = function() return '' end})
if args then for k, v in pairs(args) do frame.args[k] = v end end
function frame:preprocess(text) return text end
return frame
end
-- ============================================================
-- 测试说明映射表(每个 test_xxx → 中文说明)
-- ============================================================
local DESC = {
test_https_re = 'URL协议补全:无协议→https,已有→原样',
test_https_re_edge = 'URL协议边界:大写HTTP、空串、含空格',
test_maxnum = '取数组最大值:正常/单元素/全零',
test_maxnum_edge = '空表边界:返回nil不崩溃',
test_maxnum_empty_returns_nil = '空表确认:返回nil',
test_parsedatetime = '日期格式化:7种精度补零',
test_parsedatetime_edge = '日期边界:越界月/日/时应返回nil',
test_ref_source = 'ref包裹(source):基本/拆分/已有跳过/空',
test_ref_note = 'ref包裹(note):基本/跳过/空',
test_ref_source_edge = 'ref边界:连续分号空段跳过',
test_ref_unknown_mode = 'ref边界:未知mode→空串',
test_ref_mixed_tags = 'ref边界:混合ref分片各自独立判断',
test_id2link_person = '平台字典(person):用户名→URL',
test_id2link_homepage = '平台字典(homepage):域名模式',
test_person_icon = '人物:基本图标显示',
test_person_role_founder = '人物:创始者角色',
test_person_current_holder = '人物:现持有者角色',
test_person_unknown_url = '人物:未知平台URL(SMW仅存数据)',
test_person_with_source_ref = '人物:来源自动包裹ref',
test_person_with_note_ref = '人物:注释自动包裹group=注',
test_person_empty_name = '人物边界:空人名',
test_person_no_url_only_role = '人物边界:有角色无URL',
test_person_unrecognized_role = '人物边界:未识别角色',
test_person_platform_case = '人物边界:平台参数大小写',
test_person_multiple_same_platform = '人物边界:同平台多次出现',
test_person_unrecognized_role_passthrough = '人物边界:未识别角色原文保留',
test_person_is_can_break_loop = '人物边界:IS_CAN_BREAK_LOOP正确跳出',
test_person_source_offset = '人物:链接来源索引正确',
test_name_basic = '名称:基本/类型显示',
test_name_multiple = '名称:多名换行',
test_name_current = '名称:"现"前缀',
test_name_generation = '名称:代数显示',
test_name_empty = '名称边界:空名称',
test_name_unrecognized_set = '名称边界:未识别类型',
test_name_generation_zero = '名称边界:第0代',
test_name_double_algebra = '名称边界:双代数(前+后)',
test_name_partial_algebra = '名称边界:单侧前代数正常',
test_name_partial_algebra_reverse = '名称边界:单侧后代数正常',
test_type_a_class = '类型:A类(平行/准则/正典/续写/扩展)',
test_type_b_class = '类型:B类(世界观/迁移/错位)',
test_type_c_class = '类型:C类(交叉同人-xxx)',
test_type_c_first = '类型:C类首项不重复前缀',
test_type_with_time = '类型:带时间标签',
test_type_empty = '类型边界:空类型',
test_type_unknown = '类型边界:未知类型原文保留',
test_type_mixed_valid_invalid = '类型边界:混合有效无效均保留',
test_status_basic = '状态:基本',
test_status_with_time = '状态:带时间',
test_status_multiple = '状态:多状态换行',
test_status_empty = '状态边界:空状态',
test_status_crash_with_time_only = '状态边界:纯time无[1]不崩溃',
test_style = '风格:基本/时间',
test_style_no_value_with_time = '风格边界:纯time→不崩(有or保护)',
test_backdrop = '背景:基本',
test_backdrop_no_value_with_time = '背景边界:纯time→不崩(有or保护)',
test_homepage_platform_mode = '主页:指定平台名+URL',
test_homepage_auto_detect = '主页:自动识别平台',
test_homepage_empty_url = '主页边界:空URL',
test_homepage_fake_platform = '主页边界:未知平台用args[1]做图标',
test_homepage_no_protocol = '主页边界:无协议→补https',
test_licence_cc_by_sa = '许可:CC BY-SA 4.0图标+版本',
test_licence_cc0 = '许可:CC0',
test_licence_cc_invalid = '许可:非法CC组合报错',
test_licence_copyright = '许可:Copyright模板',
test_licence_unknown = '许可:未知许可原文',
test_licence_lowercase = '许可边界:小写cc',
test_licence_no_version = '许可边界:无版本号',
test_licence_too_many_modifiers = '许可边界:四修饰符',
test_licence_cczero_variants = '许可边界:CC ZERO变体',
test_licence_custom_text = '许可边界:Copyright@UTCWIKi',
test_date_basic = '日期:发布日期→去后缀',
test_date_reset = '日期:重置日期',
test_date_remake = '日期:重制日期',
test_date_custom_suffix = '日期:自定义xx日期→去后缀',
test_date_malformed = '日期边界:非法格式',
test_date_crash_with_no_set = '日期边界:有date无set不崩溃',
}
-- ============================================================
-- 1️⃣ p.https_re — URL 协议自动补全
-- ============================================================
function suite:test_https_re()
self:assertEquals('https://example.com', m.https_re('example.com'), '无协议补https')
self:assertEquals('http://example.com', m.https_re('http://example.com'), 'http原样')
self:assertEquals('https://example.com', m.https_re('https://example.com'), 'https原样')
end
function suite:test_https_re_edge()
self:assertEquals('HTTP://E.X', m.https_re('HTTP://E.X'), '大写HTTP→应识别已有协议')
self:assertEquals('https://', m.https_re(''), '空串补https')
self:assertEquals('https://a b', m.https_re('a b'), '含空格URL')
end
-- ============================================================
-- 2️⃣ p.maxnum — 取数组最大值
-- ============================================================
function suite:test_maxnum()
self:assertEquals(5, m.maxnum({3, 1, 5, 2}), '正常')
self:assertEquals(7, m.maxnum({7}), '单元素')
self:assertEquals(0, m.maxnum({0, 0, 0}), '全零')
end
function suite:test_maxnum_edge()
self:assertEquals(nil, m.maxnum({}), '空表→nil')
end
function suite:test_maxnum_empty_returns_nil()
local result = m.maxnum({})
self:assertEquals(nil, result, '空表→nil(非报错)')
end
-- ============================================================
-- 3️⃣ p.parsedatetime — 日期格式化
-- ============================================================
function suite:test_parsedatetime()
self:assertEquals('2024-01-05', m.parsedatetime('2024-01-05'), 'YYYY-MM-DD')
self:assertEquals('2024-01-05', m.parsedatetime('2024-1-5'), '补零 Y-M-D')
self:assertEquals('2024-01-05 14:30:00', m.parsedatetime('2024-01-05 14:30:00'), '完整datetime')
self:assertEquals('2024-01-05 14:30', m.parsedatetime('2024-01-05 14:30'), 'Y-M-D HH:MM')
self:assertEquals('2024-01-05 14', m.parsedatetime('2024-01-05 14'), 'Y-M-D HH')
self:assertEquals('2024-01', m.parsedatetime('2024-01'), 'YYYY-MM')
self:assertEquals('2024', m.parsedatetime('2024'), 'YYYY')
self:assertEquals(nil, m.parsedatetime('abc-def'), '非法输入→nil')
end
function suite:test_parsedatetime_edge()
self:assertEquals(nil, m.parsedatetime('2024-13-01'), '非法月份→应返回nil')
self:assertEquals(nil, m.parsedatetime('2024-01-32'), '非法日期→应返回nil')
self:assertEquals(nil, m.parsedatetime('2024-01-01 25:00'), '非法小时→应返回nil')
self:assertEquals(nil, m.parsedatetime(''), '空串→nil')
end
-- ============================================================
-- 4️⃣ p.ref — 引用自动包裹
-- ============================================================
function suite:test_ref_source()
self:assertEquals('{{#tag:ref|一段引用}}', m.ref('一段引用', 'source'), '基本包裹')
self:assertEquals('{{#tag:ref|A}}{{#tag:ref|B}}', m.ref('A;B', 'source'), '分号拆分')
self:assertEquals('<ref>已有</ref>', m.ref('<ref>已有</ref>', 'source'), '已有ref跳过')
self:assertEquals('{{#tag:ref|已有内容}}', m.ref('{{#tag:ref|已有内容}}', 'source'), '已有tag:ref跳过')
self:assertEquals('', m.ref('', 'source'), '空输入')
end
function suite:test_ref_note()
self:assertEquals('{{#tag:ref|一条注释|group=注}}', m.ref('一条注释', 'note'), 'note基本')
self:assertEquals("<ref group='注'>已有</ref>", m.ref("<ref group='注'>已有</ref>", 'note'), 'note已有跳过')
self:assertEquals('', m.ref('', 'note'), 'note空输入')
end
function suite:test_ref_source_edge()
self:assertEquals('{{#tag:ref|A}}{{#tag:ref|C}}', m.ref('A;;C', 'source'), '连续分号-空段跳过')
end
function suite:test_ref_unknown_mode()
self:assertEquals('', m.ref('text', 'other'), '未知mode→空串')
end
function suite:test_ref_mixed_tags()
local html = m.ref('A;<ref>B</ref>', 'source')
self:assertStringContains('{{#tag:ref|A}}', html, true, 'A无ref→应包裹')
self:assertStringContains('<ref>B</ref>', html, true, 'B有ref→应跳过')
self:assertFalse(mw.ustring.find(html, '{{#tag:ref|B}}') ~= nil, 'B无双重包裹')
end
-- ============================================================
-- 5️⃣ p.id2link — 平台字典
-- ============================================================
function suite:test_id2link_person()
local d = m.id2link_person({bilibili = '123', twitter = 'abc'})
self:assertEquals('bilibili', d.bilibili[1], 'bilibili图标名')
self:assertEquals('twitter', d.twitter[1], 'twitter图标名')
self:assertTrue(mw.ustring.find(d.bilibili[2][1], '123') ~= nil, 'bilibili URL含123')
self:assertTrue(mw.ustring.find(d.twitter[2][1], 'abc') ~= nil, 'twitter URL含abc')
end
function suite:test_id2link_homepage()
local d = m.id2link_homepage()
self:assertEquals('mspfa', d.mspfa[1], 'mspfa存在')
self:assertEquals('bilibili', d.bilibili[1], 'bilibili存在')
self:assertEquals(2, #d.tumblr[2], 'tumblr有2个URL模式')
end
-- ============================================================
-- 6️⃣ p.person — 人物信息模板
-- ============================================================
function suite:test_person_icon()
local html = m.person(makeFrame{[1] = '测试AU', bilibili = '12345'})
self:assertTrue(mw.ustring.find(html, '测试AU') ~= nil, '人名出现')
self:assertTrue(mw.ustring.find(html, 'bilibili') ~= nil, 'bilibili图标出现')
end
function suite:test_person_role_founder()
local html = m.person(makeFrame{[1] = '测试AU', url = 'https://space.bilibili.com/67890', set = '创始者'})
self:assertStringContains('创始者', html, true, '创始者文案出现')
end
function suite:test_person_current_holder()
local html = m.person(makeFrame{[1] = '测试AU', url = 'https://space.bilibili.com/67890', set = '现持有者'})
self:assertStringContains('现持有者', html, true, '现持有者文案出现')
end
function suite:test_person_unknown_url()
local html = m.person(makeFrame{[1] = '测试AU', url = 'https://example.com/wiki'})
self:assertStringContains('测试AU', html, true, '人名出现(未识别URL→无图标)')
end
function suite:test_person_with_source_ref()
local html = m.person(makeFrame{[1] = '测试AU', url = 'https://example.com/wiki', ['链接来源'] = '来源文字'})
self:assertStringContains('{{#tag:ref|来源文字}}', html, true, '来源自动包裹ref')
end
function suite:test_person_with_note_ref()
local html = m.person(makeFrame{[1] = '测试AU', url = 'https://example.com/wiki', ['链接注释'] = '注释文字'})
self:assertStringContains('group=注', html, true, '注释自动包裹note ref')
end
function suite:test_person_empty_name()
local html = m.person(makeFrame{[1] = '', bilibili = '123'})
self:assertStringContains('<div', html, true, '空人名不崩溃')
end
function suite:test_person_no_url_only_role()
local html = m.person(makeFrame{[1] = 'A', set = '参与制作者'})
self:assertStringContains('参与制作者', html, true, '无URL纯角色→角色显示')
end
function suite:test_person_unrecognized_role()
local html = m.person(makeFrame{[1] = 'A', set = '摸鱼者'})
self:assertStringContains('A', html, true, '未识别角色→不崩溃')
end
function suite:test_person_platform_case()
local html = m.person(makeFrame{[1] = 'A', Bilibili = '123'})
self:assertStringContains('A', html, true, '大写平台名→不崩溃')
end
function suite:test_person_multiple_same_platform()
local html = m.person(makeFrame{[1] = 'A', bilibili = '123', url = 'https://space.bilibili.com/456、https://space.bilibili.com/789'})
self:assertStringContains('A', html, true, '同平台多次→不崩溃')
end
function suite:test_person_unrecognized_role_passthrough()
local html = m.person(makeFrame{[1] = '测试AU', set = '翻译者'})
self:assertStringContains('翻译者', html, true, '未识别角色→原文保留')
end
function suite:test_person_is_can_break_loop()
local html = m.person(makeFrame{
[1] = '测试AU', bilibili = '123', twitter = '456', tumblr = '789',
url = 'https://www.youtube.com/c/test、https://www.pixiv.net/users/111、https://www.reddit.com/user/test',
set = '持有者、参与制作者、创始者',
})
self:assertStringContains('测试AU', html, true, '多平台混合→不崩溃不卡死')
end
function suite:test_person_source_offset()
local html = m.person(makeFrame{
[1] = '测试AU', url = 'https://space.bilibili.com/1、https://twitter.com/user2',
['链接来源'] = '来源一、来源二',
})
local _, count = mw.ustring.gsub(html, '{#tag:ref|来源', '')
self:assertEquals(2, count, '2个来源→应有2个ref包裹')
end
-- ============================================================
-- 7️⃣ p.name — 名称模板
-- ============================================================
function suite:test_name_basic()
local html = m.name(makeFrame{[1] = 'MyName', set = '官方译名'})
self:assertStringContains('MyName', html, true, '名称出现')
self:assertStringContains('官方译名', html, true, '类型出现')
end
function suite:test_name_multiple()
local html = m.name(makeFrame{[1] = 'Name1、Name2', set = '官方译名、民间译名'})
self:assertStringContains('Name1', html, true, '第一个名称出现')
self:assertStringContains('Name2', html, true, '第二个名称出现')
self:assertStringContains('<br>', html, true, '多名称换行分隔')
end
function suite:test_name_current()
local html = m.name(makeFrame{[1] = 'CurrentName', set = '现官方译名'})
self:assertStringContains('CurrentName', html, true, '当前名称出现')
self:assertStringContains('现官方译名', html, true, '"现"前缀保留')
end
function suite:test_name_generation()
local html = m.name(makeFrame{[1] = 'TranslatedName', set = '官方译名', ['代数'] = '3'})
self:assertStringContains('第3代', html, true, '代数显示')
end
function suite:test_name_empty()
local html = m.name(makeFrame{[1] = '', set = '官方译名'})
self:assertStringContains('<div', html, true, '空名称→不崩溃')
end
function suite:test_name_unrecognized_set()
local html = m.name(makeFrame{[1] = 'X', set = '火星译名'})
self:assertStringContains('X', html, true, '未识别名称类型→不崩溃')
end
function suite:test_name_generation_zero()
local html = m.name(makeFrame{[1] = 'X', set = '官方译名', ['代数'] = '0'})
self:assertStringContains('X', html, true, '第0代→不崩溃')
end
function suite:test_name_double_algebra()
local html = m.name(makeFrame{[1] = 'X', set = '民间译名', ['前者代数'] = '1', ['后者代数'] = '2'})
self:assertStringContains('X', html, true, '双代数→不崩溃')
end
function suite:test_name_partial_algebra()
local html = m.name(makeFrame{[1] = 'TestName', set = '民间译名', ['前者代数'] = '2'})
self:assertStringContains('TestName', html, true, '单侧代数→不崩溃')
end
function suite:test_name_partial_algebra_reverse()
local html = m.name(makeFrame{[1] = 'TestName', set = '民间译名', ['后者代数'] = '3'})
self:assertStringContains('TestName', html, true, '单侧后代数→不崩溃')
end
-- ============================================================
-- 8️⃣ p.type — AU 类型分类
-- ============================================================
function suite:test_type_a_class()
self:assertStringContains('平行时间轴', m.type(makeFrame{[1] = '平行时间轴'}), true, 'A类:平行时间轴')
self:assertStringContains('扩展宇宙', m.type(makeFrame{[1] = '扩展宇宙'}), true, 'A类:扩展宇宙')
end
function suite:test_type_b_class()
self:assertStringContains('世界观变动', m.type(makeFrame{[1] = '世界观变动'}), true, 'B类:世界观变动')
self:assertStringContains('角色错位', m.type(makeFrame{[1] = '角色错位'}), true, 'B类:角色错位')
end
function suite:test_type_c_class()
local html = m.type(makeFrame{[1] = '元素代入'})
self:assertStringContains('交叉同人', html, true, 'C类前缀出现')
self:assertStringContains('元素代入', html, true, 'C类具体类型出现')
end
function suite:test_type_c_first()
local html = m.type(makeFrame{[1] = '交叉同人'})
self:assertStringContains('交叉同人', html, true, 'C类首项显示')
self:assertFalse(mw.ustring.find(html, '交叉同人 %-'), 'C类首项不重复前缀')
end
function suite:test_type_with_time()
local html = m.type(makeFrame{[1] = '世界观变动', time = '2024'})
self:assertStringContains('世界观变动', html, true, '类型文本出现')
self:assertStringContains('2024', html, true, '时间标签出现')
end
function suite:test_type_empty()
local html = m.type(makeFrame{[1] = ''})
self:assertStringContains('<div', html, true, '空类型→不崩溃')
end
function suite:test_type_unknown()
local html = m.type(makeFrame{[1] = '精神污染'})
self:assertStringContains('精神污染', html, true, '未知类型→应原文保留')
end
function suite:test_type_mixed_valid_invalid()
local html = m.type(makeFrame{[1] = '平行时间轴、精神污染'})
self:assertStringContains('平行时间轴', html, true, '有效类型出现')
self:assertStringContains('精神污染', html, true, '无效类型→应保留')
end
-- ============================================================
-- 9️⃣ p.status — 状态模板
-- ============================================================
function suite:test_status_basic()
local html = m.status(makeFrame{[1] = '连载中'})
self:assertStringContains('连载中', html, true, '状态文本出现')
end
function suite:test_status_with_time()
local html = m.status(makeFrame{[1] = '连载中', time = '2024-01-01'})
self:assertStringContains('连载中', html, true, '状态文本')
self:assertStringContains('2024-01-01', html, true, '时间文本')
end
function suite:test_status_multiple()
local html = m.status(makeFrame{[1] = '连载中、已完结', time = '2020、2024'})
self:assertStringContains('连载中', html, true, '第一状态')
self:assertStringContains('已完结', html, true, '第二状态')
self:assertStringContains('<br>', html, true, '多状态换行')
end
function suite:test_status_empty()
local html = m.status(makeFrame{[1] = ''})
self:assertStringContains('<div', html, true, '空状态→不崩溃')
end
function suite:test_status_crash_with_time_only()
local ok = pcall(function() m.status(makeFrame{time = '2024'}) end)
self:assertTrue(ok, 'status纯时间→不应崩溃')
end
-- ============================================================
-- 🔟 p.style — 风格模板
-- ============================================================
function suite:test_style()
local html = m.style(makeFrame{[1] = '像素风', time = '2020'})
self:assertStringContains('像素风', html, true, '风格文本')
self:assertStringContains('2020', html, true, '时间文本')
end
function suite:test_style_no_value_with_time()
local html = m.style(makeFrame{time = '2024'})
self:assertStringContains('<div', html, true, 'style纯时间→不崩溃(有or保护)')
end
-- ============================================================
-- 1️⃣1️⃣ p.backdrop — 背景模板
-- ============================================================
function suite:test_backdrop()
local html = m.backdrop(makeFrame{[1] = '现代都市'})
self:assertStringContains('现代都市', html, true, '背景文本')
end
function suite:test_backdrop_no_value_with_time()
local html = m.backdrop(makeFrame{time = '2024'})
self:assertStringContains('<div', html, true, 'backdrop纯时间→不崩溃(有or保护)')
end
-- ============================================================
-- 1️⃣2️⃣ p.homepage — 主页链接模板
-- ============================================================
function suite:test_homepage_platform_mode()
local html = m.homepage(makeFrame{[1] = 'bilibili', [2] = 'https://space.bilibili.com/123'})
self:assertStringContains('bilibili.png', html, true, '平台图标出现')
self:assertStringContains('space.bilibili.com/123', html, true, 'URL出现')
end
function suite:test_homepage_auto_detect()
local html = m.homepage(makeFrame{[1] = 'https://twitter.com/user'})
self:assertStringContains('twitter.png', html, true, '自动识别twitter图标')
end
function suite:test_homepage_empty_url()
local html = m.homepage(makeFrame{[1] = 'bilibili', [2] = ''})
self:assertStringContains('<div', html, true, '空URL→不崩溃')
end
function suite:test_homepage_fake_platform()
local html = m.homepage(makeFrame{[1] = 'fakeplatform', [2] = 'https://example.com'})
self:assertStringContains('fakeplatform.png', html, true, '未知平台→应用args[1]做图标名')
end
function suite:test_homepage_no_protocol()
local html = m.homepage(makeFrame{[1] = 'example.com/wiki'})
self:assertStringContains('https://example.com/wiki', html, true, '无协议→自动补https')
end
-- ============================================================
-- 1️⃣3️⃣ p.licence — 许可协议模板
-- ============================================================
function suite:test_licence_cc_by_sa()
local html = m.licence(makeFrame{[1] = 'CC BY-SA 4.0'})
self:assertStringContains('CC.png', html, true, 'CC基础图标')
self:assertStringContains('BY', html, true, 'BY标识')
self:assertStringContains('SA', html, true, 'SA标识')
self:assertStringContains('4.0', html, true, '版本号')
end
function suite:test_licence_cc0()
local html = m.licence(makeFrame{[1] = 'CC0 1.0'})
self:assertStringContains('CC.png', html, true, 'CC图标')
self:assertStringContains('1.0', html, true, '版本号')
end
function suite:test_licence_cc_invalid()
local html = m.licence(makeFrame{[1] = 'CC BY-ND-SA 4.0'})
self:assertStringContains('搭配不正确', html, true, '非法CC报错')
end
function suite:test_licence_copyright()
local html = m.licence(makeFrame{[1] = 'Copyright'})
self:assertStringContains('Copyright', html, true, 'Copyright模板')
end
function suite:test_licence_unknown()
local html = m.licence(makeFrame{[1] = 'WTFPL'})
self:assertStringContains('WTFPL', html, true, '未知许可原文输出')
end
function suite:test_licence_lowercase()
local html = m.licence(makeFrame{[1] = 'cc by-sa 4.0'})
self:assertStringContains('CC', html, true, '小写cc→识别为CC')
end
function suite:test_licence_no_version()
local html = m.licence(makeFrame{[1] = 'CC BY-NC'})
self:assertStringContains('BY', html, true, '无版本号→仍识别组件')
end
function suite:test_licence_too_many_modifiers()
local html = m.licence(makeFrame{[1] = 'CC BY-NC-ND-SA 4.0'})
self:assertStringContains('搭配不正确', html, true, '四修饰符→报错')
end
function suite:test_licence_cczero_variants()
local html = m.licence(makeFrame{[1] = 'CC ZERO 1.0'})
self:assertStringContains('CC', html, true, 'CC ZERO→识别')
end
function suite:test_licence_custom_text()
local html = m.licence(makeFrame{[1] = 'Copyright @ UTCWIKi'})
self:assertStringContains('Copyright @ UTCWIKi', html, true, 'Copyright@UTCWIKi→识别')
end
-- ============================================================
-- 1️⃣4️⃣ p.date — 日期模板
-- ============================================================
function suite:test_date_basic()
local html = m.date(makeFrame{[1] = '2024-01-05', set = '发布日期'})
self:assertStringContains('发布', html, true, '"日期"后缀被移除')
self:assertStringContains('2024-01-05', html, true, '日期文本')
end
function suite:test_date_reset()
local html = m.date(makeFrame{[1] = '2024-06', set = '重置日期'})
self:assertStringContains('重置', html, true, '"重置"识别')
end
function suite:test_date_remake()
local html = m.date(makeFrame{[1] = '2024', set = '重制日期'})
self:assertStringContains('重制', html, true, '"重制"识别')
end
function suite:test_date_custom_suffix()
local html = m.date(makeFrame{[1] = '2024', set = '上线日期'})
self:assertStringContains('上线', html, true, '自定义"xx日期"→去后缀')
end
function suite:test_date_malformed()
local html = m.date(makeFrame{[1] = 'not-a-date', set = '发布日期'})
self:assertStringContains('<div', html, true, '非法日期→不崩溃')
end
function suite:test_date_crash_with_no_set()
local ok = pcall(function() m.date(makeFrame{[1] = '2024-01-01'}) end)
self:assertTrue(ok, 'date无set→不应崩溃')
end
-- ============================================================
-- 自定义 run():包装原生 ScribuntoUnit,输出表格增加"说明"列
-- 使用闭包绑定 self,与 ScribuntoUnit.new() 模式一致
-- ============================================================
suite.run = function(frame)
local testData = ScribuntoUnit.runSuite(suite, frame)
local cfg = mw.loadData('Module:ScribuntoUnit/config')
if testData.failureCount > 0 then
local out = mw.ustring.gsub(cfg.failureSummary, '$1', tostring(testData.failureCount)) .. '\n'
out = out .. '{| class="wikitable" style="width:100%"\n'
out = out .. '! 状态 !! 测试函数 !! 说明 !! 详情\n'
for _, r in ipairs(testData.results) do
local status = r.error and '❌' or '✅'
local desc = DESC[r.name] or ''
local bg = r.error and 'style="background:#ffe0e0"' or ''
local detail = ''
if r.error then
detail = tostring(r.message or r.error)
detail = mw.ustring.gsub(detail, '\n', ' ')
if #detail > 130 then detail = mw.ustring.sub(detail, 1, 127) .. '...' end
detail = '<br><small style="color:#c00">' .. mw.text.nowiki(detail) .. '</small>'
end
out = out .. '|- ' .. bg .. '\n| ' .. status .. ' || <code>' .. r.name .. '</code> || ' .. desc .. ' || ' .. detail .. '\n'
end
out = out .. '|}\n'
return out
else
local out = cfg.successSummary .. '\n'
out = out .. '{| class="wikitable" style="width:100%"\n'
out = out .. '! 状态 !! 测试函数 !! 说明\n'
for _, r in ipairs(testData.results) do
out = out .. '|-\n| ✅ || <code>' .. r.name .. '</code> || ' .. (DESC[r.name] or '') .. '\n'
end
out = out .. '|}\n'
return out
end
end
return suite
