Difference between revisions of "Module:Protection banner/testcases"

From blackwiki
Jump to navigation Jump to search
blackwiki>Jackmcbarn
(reflect legitimate changes to the module)
m (92 revisions imported)
 
(10 intermediate revisions by one other user not shown)
Line 52: Line 52:
 
end
 
end
  
local function makeTitleObject(page, action, level)
+
local function makeTitleObject(options)
 +
local title = mw.title.new(options.page)
 +
 
 +
-- Set protection levels
 
local levels = {
 
local levels = {
 
edit = {},
 
edit = {},
Line 58: Line 61:
 
autoreview = {}
 
autoreview = {}
 
}
 
}
levels[action][1] = level
+
for _, action in ipairs{'edit', 'move', 'autoreview'} do
local title = mw.title.new(page)
+
local level = options[action]
return rawset(title, 'protectionLevels', levels)
+
if level then
 +
levels[action][1] = level
 +
end
 +
end
 +
rawset(title, 'protectionLevels', levels)
 +
 
 +
-- Set content model
 +
rawset(title, 'contentModel', options.contentModel or 'wikitext')
 +
 
 +
return title
 
end
 
end
  
 
local function makeDefaultTitleObject()
 
local function makeDefaultTitleObject()
return makeTitleObject(d.page, d.action, d.level)
+
return makeTitleObject{page = d.page, [d.action] = d.level}
 
end
 
end
  
Line 82: Line 94:
 
pagetypes = {},
 
pagetypes = {},
 
indefStrings = {},
 
indefStrings = {},
 +
hierarchy = {
 +
sysop = {},
 +
reviewer = {'sysop'},
 +
filemover = {'sysop'},
 +
templateeditor = {'sysop'},
 +
extendedconfirmed = {'sysop'},
 +
autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'},
 +
user = {'autoconfirmed'},
 +
['*'] = {'user'}
 +
},
 
wrappers = {},
 
wrappers = {},
 
msg = {}
 
msg = {}
Line 197: Line 219:
 
function suite:assertIsPadlock(s, msg)
 
function suite:assertIsPadlock(s, msg)
 
self:assertStringContains(
 
self:assertStringContains(
'^%cUNIQ.-QINU%c$',
+
'\127[^\127]*UNIQ%-%-indicator%-%x+%-QINU[^\127]*\127',
 
s,
 
s,
 
false,
 
false,
Line 303: Line 325:
 
{action = 'edit'},
 
{action = 'edit'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Foo', 'edit', 'autoconfirmed')
+
makeTitleObject{page = 'Foo', edit = 'autoconfirmed'}
 
)
 
)
 
self:assertEquals('autoconfirmed', obj.level)
 
self:assertEquals('autoconfirmed', obj.level)
Line 312: Line 334:
 
{action = 'edit'},
 
{action = 'edit'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Foo', 'edit', 'sysop')
+
makeTitleObject{page = 'Foo', edit = 'sysop'}
 
)
 
)
 
self:assertEquals('sysop', obj.level)
 
self:assertEquals('sysop', obj.level)
Line 321: Line 343:
 
{action = 'edit'},
 
{action = 'edit'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Foo', 'edit', nil)
+
makeTitleObject{page = 'Foo', edit = nil}
 
)
 
)
 
self:assertEquals('*', obj.level)
 
self:assertEquals('*', obj.level)
Line 330: Line 352:
 
{action = 'move'},
 
{action = 'move'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Foo', 'move', 'autoconfirmed')
+
makeTitleObject{page = 'Foo', move = 'autoconfirmed'}
 
)
 
)
 
self:assertEquals('*', obj.level)
 
self:assertEquals('*', obj.level)
Line 339: Line 361:
 
{action = 'edit'},
 
{action = 'edit'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Template:Foo', 'edit', 'templateeditor')
+
makeTitleObject{page = 'Template:Foo', edit = 'templateeditor'}
 
)
 
)
 
self:assertEquals('templateeditor', obj.level)
 
self:assertEquals('templateeditor', obj.level)
Line 348: Line 370:
 
{action = 'edit'},
 
{action = 'edit'},
 
makeConfig(),
 
makeConfig(),
makeTitleObject('Template:Editnotices/Page/Foo', 'edit', nil)
+
makeTitleObject{page = 'Template:Editnotices/Page/Foo', edit = nil}
 
)
 
)
 
self:assertEquals('templateeditor', obj.level)
 
self:assertEquals('templateeditor', obj.level)
 
end
 
end
  
-- Expiry
+
-- Reason
  
function suite:testProtectionExpiryIndef()
+
function suite:testProtectionReason()
 
local obj = Protection.new(
 
local obj = Protection.new(
{expiry = 'indefinite'},
+
{'foo'},
makeConfig{indefStrings = {indefinite = true}}
 
)
 
self:assertEquals('indef', obj.expiry)
 
end
 
 
 
function suite:testProtectionExpiryTemp()
 
local obj = Protection.new(
 
{expiry = d.expiry,  action = 'autoreview' },
 
 
makeConfig()
 
makeConfig()
 
)
 
)
self:assertEquals(d.expiryU, obj.expiry)
+
self:assertEquals('foo', obj.reason)
 
end
 
end
  
function suite:testProtectionNoExpiry()
+
function suite:testProtectionReasonLowerCase()
 
local obj = Protection.new(
 
local obj = Protection.new(
{ action = 'autoreview' },
+
{'fOO'},
 
makeConfig()
 
makeConfig()
 
)
 
)
self:assertEquals(nil, obj.expiry)
+
self:assertEquals('foo', obj.reason)
 
end
 
end
  
function suite:testProtectionBadExpiry()
+
function suite:testProtectionBadReason()
 
self:assertError(
 
self:assertError(
 
Protection.new,
 
Protection.new,
{{expiry = 'foobar', action = 'autoreview' }, makeConfig()},
+
{{'foo|bar'}, makeConfig()},
'invalid expiry date: foobar'
+
'reasons cannot contain the pipe character ("|")'
 
)
 
)
 
end
 
end
  
function suite:testProtectionPastExpiry()
+
function suite:testProtectionNoReason()
 
local obj = Protection.new(
 
local obj = Protection.new(
{expiry = d.protectionDate, action = 'autoreview' },
+
{},
 
makeConfig()
 
makeConfig()
 
)
 
)
self:assertEquals(d.protectionDateU, obj.expiry)
+
self:assertEquals(nil, obj.reason)
 
end
 
end
  
-- Reason
+
-- Protection date
  
function suite:testProtectionReason()
+
function suite:testProtectionProtectionDateIndef()
local obj = Protection.new(
+
self:assertError(
{'foo'},
+
Protection.new,
makeConfig()
+
{
 +
{date = 'indefinite'},
 +
makeConfig{indefStrings = {indefinite = true}}
 +
},
 +
'invalid protection date: indefinite'
 
)
 
)
self:assertEquals('foo', obj.reason)
 
 
end
 
end
  
function suite:testProtectionReasonLowerCase()
+
function suite:testProtectionProtectionDateTemp()
 
local obj = Protection.new(
 
local obj = Protection.new(
{'fOO'},
+
{date = d.protectionDate},
 
makeConfig()
 
makeConfig()
 
)
 
)
self:assertEquals('foo', obj.reason)
+
self:assertEquals(d.protectionDateU, obj.protectionDate)
 
end
 
end
  
function suite:testProtectionBadReason()
+
function suite:testProtectionNoProtectionDate()
self:assertError(
 
Protection.new,
 
{{'foo|bar'}, makeConfig()},
 
'reasons cannot contain the pipe character ("|")'
 
)
 
end
 
 
 
function suite:testProtectionNoReason()
 
 
local obj = Protection.new(
 
local obj = Protection.new(
 
{},
 
{},
 
makeConfig()
 
makeConfig()
 
)
 
)
self:assertEquals(nil, obj.reason)
+
self:assertEquals(nil, obj.protectionDate)
 
end
 
end
  
-- Protection date
+
function suite:testProtectionBadProtectionDate()
 
 
function suite:testProtectionProtectionDateIndef()
 
self:assertError(
 
Protection.new,
 
{
 
{date = 'indefinite'},
 
makeConfig{indefStrings = {indefinite = true}}
 
},
 
'invalid protection date: indefinite'
 
)
 
end
 
 
 
function suite:testProtectionProtectionDateTemp()
 
local obj = Protection.new(
 
{date = d.protectionDate},
 
makeConfig()
 
)
 
self:assertEquals(d.protectionDateU, obj.protectionDate)
 
end
 
 
 
function suite:testProtectionNoProtectionDate()
 
local obj = Protection.new(
 
{},
 
makeConfig()
 
)
 
self:assertEquals(nil, obj.protectionDate)
 
end
 
 
 
function suite:testProtectionBadProtectionDate()
 
 
self:assertError(
 
self:assertError(
 
Protection.new,
 
Protection.new,
Line 528: Line 508:
 
end
 
end
  
-- isProtected
+
-- isUserScript
  
function suite:testProtectionIsProtectedTrue()
+
function suite:testProtectionIsUserScriptWithUserCSS()
local obj = makeDefaultProtectionObject()
+
local protection = Protection.new(
obj.level = 'autoconfirmed'
+
{},
self:assertTrue(obj:isProtected())
+
makeConfig(),
 +
mw.title.new('User:Example/common.css')
 +
)
 +
self:assertTrue(protection:isUserScript())
 
end
 
end
  
function suite:testProtectionIsProtectedFalse()
+
function suite:testProtectionIsUserScriptWithUserJS()
local obj = makeDefaultProtectionObject()
+
local protection = Protection.new(
obj.level = '*'
+
{},
self:assertFalse(obj:isProtected())
+
makeConfig(),
 +
mw.title.new('User:Example/common.js')
 +
)
 +
self:assertTrue(protection:isUserScript())
 
end
 
end
  
-- isTemporary
+
function suite:testProtectionIsUserScriptWithUserPage()
 
+
local protection = Protection.new(
function suite:testProtectionIsProtectedTrue()
+
{},
local obj = makeDefaultProtectionObject()
+
makeConfig(),
obj.expiry = 123456789012
+
mw.title.new('User:Example')
self:assertTrue(obj:isTemporary())
+
)
 +
self:assertFalse(protection:isUserScript())
 
end
 
end
  
function suite:testProtectionIsProtectedFalse1()
+
function suite:testProtectionIsUserScriptWithNormalSubpage()
local obj = makeDefaultProtectionObject()
+
local protection = Protection.new(
obj.expiry = 'indef'
+
{},
self:assertFalse(obj:isTemporary())
+
makeConfig(),
 +
mw.title.new('User:Example/common')
 +
)
 +
self:assertFalse(protection:isUserScript())
 
end
 
end
  
function suite:testProtectionIsProtectedFalse2()
+
function suite:testProtectionIsUserScriptWithUsernameEndingInCSS()
local obj = makeDefaultProtectionObject()
+
local protection = Protection.new(
obj.expiry = nil
+
{},
self:assertFalse(obj:isTemporary())
+
makeConfig(),
 +
mw.title.new('User:My username ends in .css')
 +
)
 +
self:assertFalse(protection:isUserScript())
 
end
 
end
  
-- makeProtectionCategory
 
  
function suite:testProtectionCategoryPrecedence1()
+
function suite:testProtectionIsUserScriptWithUsernameEndingInJS()
-- Test that expiry has the lowest priority.
+
self:assertFalse(
local protectionCategories = {
+
Protection.new(
[makeDefaultProtectionCategoryKey{expiry = 'all'}] = 'all expiries allowed',
+
{},
[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
+
makeConfig(),
[makeDefaultProtectionCategoryKey{reason = 'all'}] = 'all reasons allowed',
+
mw.title.new('User:My username ends in .js')
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
+
):isUserScript()
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed'
+
)
}
+
end
local obj = makeDefaultProtectionObject{
+
 
protectionCategories = protectionCategories,
+
function suite:testProtectionIsUserScriptWithSubpageContainingCSS()
}
+
self:assertFalse(
self:assertStringContains(
+
Protection.new(
'all expiries allowed',
+
{},
obj:makeProtectionCategory(),
+
makeConfig(),
true
+
mw.title.new('User:Example/common.css.txt')
 +
):isUserScript()
 
)
 
)
 
end
 
end
  
function suite:testProtectionCategoryPrecedence2()
+
function suite:testProtectionIsUserScriptWithNoSubpagePrefix()
-- Test that reason has the highest priority.
+
self:assertTrue(
local protectionCategories = {
+
Protection.new(
[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
+
{},
[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
+
makeConfig(),
[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
+
mw.title.new('User:Example/.css')
[makeProtectionCategoryKey{level = d.level}] = 'level only',
+
):isUserScript()
[makeProtectionCategoryKey{action = d.action}] = 'action only'
+
)
}
+
end
local obj = makeDefaultProtectionObject{
+
 
protectionCategories = protectionCategories,
+
function suite:testProtectionIsUserScriptWithBlankUsernameAndNoSubpagePrefix()
}
+
self:assertTrue(
self:assertStringContains(
+
Protection.new(
'reason only',
+
{},
obj:makeProtectionCategory(),
+
makeConfig(),
true
+
mw.title.new('User:/.css')
 +
):isUserScript()
 +
)
 +
end
 +
 
 +
function suite:testProtectionIsUserScriptWhenUsernameIsOnlyCSS()
 +
self:assertFalse(
 +
Protection.new(
 +
{},
 +
makeConfig(),
 +
mw.title.new('User:.css')
 +
):isUserScript()
 
)
 
)
 
end
 
end
  
function suite:testProtectionCategoryPrecedence3()
+
function suite:testProtectionIsUserScriptWithNonstandardCSSContentModel()
-- Test that namespace has the highest priority if the reason is set in
+
self:assertTrue(
-- cfg.reasonsWithNamespacePriority.
+
Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
+
makeConfig(),
[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
+
makeTitleObject{page='User:Example/foo', contentModel='css'}
[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
+
):isUserScript()
[makeProtectionCategoryKey{level = d.level}] = 'level only',
 
[makeProtectionCategoryKey{action = d.action}] = 'action only'
 
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
reasonsWithNamespacePriority = {[d.reason] = true}
 
}
 
self:assertStringContains(
 
'namespace only',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 
end
 
end
  
function suite:testProtectionCategoryPrecedence4()
+
function suite:testProtectionIsUserScriptWithNonstandardJSContentModel()
-- Test that level has a higher priority than namespace.
+
self:assertTrue(
local protectionCategories = {
+
Protection.new(
[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
+
{},
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
+
makeConfig(),
}
+
makeTitleObject{page='User:Example/foo', contentModel='javascript'}
local obj = makeDefaultProtectionObject{
+
):isUserScript()
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'all namespaces allowed',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 
end
 
end
  
function suite:testProtectionCategoryPrecedence5()
+
function suite:testProtectionIsUserScriptWithNonstandardWikitextContentModel()
-- Test that action has a higher priority than level.
+
self:assertFalse(
local protectionCategories = {
+
Protection.new(
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
+
{},
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
+
makeConfig(),
}
+
makeTitleObject{page='User:Example/foo.css', contentModel='wikitext'}
local obj = makeDefaultProtectionObject{
+
):isUserScript()
protectionCategories = protectionCategories,
+
)
}
+
end
self:assertStringContains(
+
 
'all levels allowed',
+
function suite:testProtectionIsUserScriptWithNonUserspacePage()
obj:makeProtectionCategory(),
+
self:assertFalse(
true
+
Protection.new(
 +
{},
 +
makeConfig(),
 +
makeTitleObject{page='Wikipedia:Example.css', contentModel='css'}
 +
):isUserScript()
 
)
 
)
 
end
 
end
  
function suite:testProtectionCategoryPrecedence6()
+
-- isProtected
-- Test that an exact match will be first.
 
local protectionCategories = {
 
[makeDefaultProtectionCategoryKey()] = 'exact match',
 
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
 
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'exact match',
 
obj:makeProtectionCategory(),
 
true
 
)
 
end
 
  
function suite:testProtectionCategoryPrecedence7()
+
function suite:testProtectionIsProtectedTrue()
-- Test that matching 4 keys will come before matching 3 keys
+
local obj = makeDefaultProtectionObject()
local protectionCategories = {
+
obj.level = 'autoconfirmed'
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'four keys',
+
self:assertTrue(obj:isProtected())
[makeDefaultProtectionCategoryKey{action = 'all', level = 'all'}] = 'three keys',
+
end
}
+
 
local obj = makeDefaultProtectionObject{
+
function suite:testProtectionIsProtectedFalse()
protectionCategories = protectionCategories,
+
local obj = makeDefaultProtectionObject()
}
+
obj.level = '*'
self:assertStringContains(
+
self:assertFalse(obj:isProtected())
'four keys',
+
end
obj:makeProtectionCategory(),
+
 
true
+
-- shouldShowLock
 +
 
 +
function suite:testProtectionShouldShowLockWithProtectedPage()
 +
local obj = Protection.new(
 +
{},
 +
makeConfig(),
 +
makeTitleObject{page='Foo', edit='sysop'}
 
)
 
)
 +
self:assertTrue(obj:shouldShowLock())
 
end
 
end
  
function suite:testProtectionCategoryPrecedence8()
+
function suite:testProtectionShouldShowLockWithProtectedUserScript()
-- Test that matching 3 keys will come before matching 2 keys
+
local obj = Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey{reason = d.reason, action = d.action, level = d.level}] = 'three keys',
+
makeConfig(),
[makeProtectionCategoryKey{reason = d.reason, action = d.action}] = 'two keys',
+
makeTitleObject{page='User:Example/common.css', contentModel='css', edit='sysop'}
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'three keys',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 +
self:assertFalse(obj:shouldShowLock())
 
end
 
end
  
function suite:testProtectionCategoryPrecedence9()
+
function suite:testProtectionShouldShowLockWithUnprotectedPage()
-- Test that matching 2 keys will come before matching 1 key
+
local obj = Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey{action = d.action, level = d.level}] = 'two keys',
+
makeConfig(),
[makeProtectionCategoryKey{action = d.action}] = 'one key',
+
makeTitleObject{page='Foo'}
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'two keys',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 +
self:assertFalse(obj:shouldShowLock())
 
end
 
end
  
function suite:testProtectionCategoryPrecedence10()
+
-- shouldHaveProtectionCategory
-- Test that matching 1 keys will come before matching 0 keys
 
local protectionCategories = {
 
[makeProtectionCategoryKey{action = d.action}] = 'one key',
 
[makeProtectionCategoryKey()] = 'no keys',
 
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'one key',
 
obj:makeProtectionCategory(),
 
true
 
)
 
end
 
  
function suite:testProtectionCategoryAllAlls()
+
function suite:testProtectionShouldHaveProtectionCategoryWithProtectedPage()
-- Test that 'all|all|all|all|all' works
+
local obj = Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey()] = 'no keys',
+
makeConfig(),
}
+
makeTitleObject{page='Foo', edit='sysop'}
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'no keys',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 +
self:assertTrue(obj:shouldHaveProtectionCategory())
 
end
 
end
  
function suite:testProtectionCategoryNoFalseMatches()
+
function suite:testProtectionShouldHaveProtectionCategoryWithProtectedUserScript()
-- Test that we don't match things that we aren't supposed to.
+
local obj = Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey{expiry = 'foo'}]    = 'expiry foo',
+
makeConfig(),
[makeProtectionCategoryKey{namespace = 'foo'}] = 'namespace foo',
+
makeTitleObject{page='User:Example/common.css', contentModel='css', edit='sysop'}
[makeProtectionCategoryKey{reason = 'foo'}]    = 'reason foo',
 
[makeProtectionCategoryKey{level = 'foo'}]    = 'level foo',
 
[makeProtectionCategoryKey{action = 'foo'}]    = 'action foo',
 
[makeProtectionCategoryKey()]                  = 'no keys',
 
}
 
local obj = makeDefaultProtectionObject{
 
protectionCategories = protectionCategories,
 
}
 
self:assertStringContains(
 
'no keys',
 
obj:makeProtectionCategory(),
 
true
 
 
)
 
)
 +
self:assertFalse(obj:shouldHaveProtectionCategory())
 
end
 
end
  
function suite:testProtectionCategoryProtected()
+
function suite:testProtectionShouldHaveProtectionCategoryWithUnprotectedPage()
-- Test that protected pages produce some kind of category link.
+
local obj = Protection.new(
local protectionCategories = {
+
{},
[makeProtectionCategoryKey()] = 'no keys',
+
makeConfig(),
}
+
makeTitleObject{page='Foo'}
local obj = makeDefaultProtectionObject{
+
)
protectionCategories = protectionCategories,
+
self:assertFalse(obj:shouldHaveProtectionCategory())
}
+
end
self:assertStringContains(
 
'^%[%[Category:.*%]%]$',
 
obj:makeProtectionCategory()
 
)
 
end
 
  
function suite:testProtectionCategoryNotProtected()
+
-- isTemporary
-- Test that unprotected pages produce the blank string.
 
local protectionCategories = {
 
[makeProtectionCategoryKey()] = 'no keys',
 
}
 
local obj = Protection.new(
 
{},
 
makeConfig{protectionCategories = protectionCategories},
 
makeTitleObject(d.page, 'edit', nil)
 
)
 
self:assertEquals('', obj:makeProtectionCategory())
 
end
 
  
function suite:testProtectionCategoryNoMatch()
+
function suite:testProtectionIsProtectedTrue()
-- Test that protected pages that don't match any categories
 
-- produce the blank string.
 
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
self:assertEquals('', obj:makeProtectionCategory())
+
obj.expiry = 123456789012
 +
self:assertTrue(obj:isTemporary())
 
end
 
end
  
function suite:testProtectionCategoryExpiryIndef()
+
function suite:testProtectionIsProtectedFalse1()
-- Test that indefinite protection matches the "indef" expiry fragment.
 
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
obj._cfg.protectionCategories = {
 
[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview'}] = 'indef expiry',
 
}
 
obj.action = 'autoreview'
 
obj.level = 'autoconfirmed'
 
 
obj.expiry = 'indef'
 
obj.expiry = 'indef'
self:assertStringContains('indef expiry', obj:makeProtectionCategory(), true)
+
self:assertFalse(obj:isTemporary())
 
end
 
end
  
function suite:testProtectionCategoryExpiryTemp()
+
function suite:testProtectionIsProtectedFalse2()
-- Test that temporary protection matches the "temp" expiry fragment.
 
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
obj._cfg.protectionCategories = {
+
obj.expiry = nil
[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview'}] = 'temporary expiry',
+
self:assertFalse(obj:isTemporary())
}
 
obj.action = 'autoreview'
 
obj.level = 'autoconfirmed'
 
obj.expiry = d.expiryU
 
self:assertStringContains('temporary expiry', obj:makeProtectionCategory(), true)
 
 
end
 
end
  
function suite:testProtectionCategoryNoExpiry()
+
-- makeProtectionCategory
-- Test that pages with no expiry set don't match "indef" or "temp".
+
--
local protectionCategories = {
+
function suite:testProtectionCategoryWithUnprotectedPage()
[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview' }] = 'temporary expiry',
 
[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview' }] = 'indefinite expiry',
 
[makeProtectionCategoryKey()] = 'no matches'
 
}
 
 
local obj = Protection.new(
 
local obj = Protection.new(
 
{},
 
{},
makeConfig{protectionCategories = protectionCategories},
+
makeConfig(),
makeProtectedTitleObject()
+
makeTitleObject{page='Foo'}
 
)
 
)
self:assertStringContains('no matches', obj:makeProtectionCategory(), true)
+
self:assertEquals(obj:makeProtectionCategory(), '')
 
end
 
end
  
function suite:testProtectionCategoryNamespaceFragment()
+
function suite:testProtectionCategoryPrecedence1()
-- Test that values in cfg.categoryNamespaceKeys will work.
+
-- Test that expiry has the lowest priority.
 
local protectionCategories = {
 
local protectionCategories = {
[makeProtectionCategoryKey{namespace = 'foobar'}] = 'we found a match',
+
[makeDefaultProtectionCategoryKey{expiry = 'all'}] = 'all expiries allowed',
 +
[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
 +
[makeDefaultProtectionCategoryKey{reason = 'all'}] = 'all reasons allowed',
 +
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
 +
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed'
 +
}
 +
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 
}
 
}
local obj = Protection.new(
+
self:assertStringContains(
{},
+
'all expiries allowed',
makeConfig{
+
obj:makeProtectionCategory(),
protectionCategories = protectionCategories,
+
true
categoryNamespaceKeys = {[10] = 'foobar'} -- Template namespace
 
},
 
makeTitleObject('Template:Foo', 'edit', 'autoconfirmed')
 
 
)
 
)
self:assertStringContains('we found a match', obj:makeProtectionCategory(), true)
 
 
end
 
end
  
function suite:testProtectionCategoryTalk()
+
function suite:testProtectionCategoryPrecedence2()
-- Test that talk pages match the "talk" namespace fragment.
+
-- Test that reason has the highest priority.
 
local protectionCategories = {
 
local protectionCategories = {
[makeProtectionCategoryKey{namespace = 'talk'}] = 'talk namespace',
+
[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
 +
[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
 +
[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
 +
[makeProtectionCategoryKey{level = d.level}] = 'level only',
 +
[makeProtectionCategoryKey{action = d.action}] = 'action only'
 +
}
 +
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 
}
 
}
local obj = Protection.new(
+
self:assertStringContains(
{},
+
'reason only',
makeConfig{protectionCategories = protectionCategories},
+
obj:makeProtectionCategory(),
makeTitleObject('Template talk:Example', 'edit', 'autoconfirmed')
+
true
 
)
 
)
self:assertStringContains('talk namespace', obj:makeProtectionCategory(), true)
 
 
end
 
end
  
-- needsExpiry
+
function suite:testProtectionCategoryPrecedence3()
 
+
-- Test that namespace has the highest priority if the reason is set in
function suite:testProtectionNeedsExpiryFalse1()
+
-- cfg.reasonsWithNamespacePriority.
local obj = makeDefaultProtectionObject()
+
local protectionCategories = {
obj.expiry = d.expiryU
+
[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
self:assertFalse(
+
[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
obj:needsExpiry(),
+
[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
'expiry is already set'
+
[makeProtectionCategoryKey{level = d.level}] = 'level only',
)
+
[makeProtectionCategoryKey{action = d.action}] = 'action only'
end
+
}
 
+
local obj = makeDefaultProtectionObject{
function suite:testProtectionNeedsExpiryFalse2()
+
protectionCategories = protectionCategories,
local obj = makeDefaultProtectionObject()
+
reasonsWithNamespacePriority = {[d.reason] = true}
obj.expiry = nil
+
}
obj.action = 'move'
+
self:assertStringContains(
obj._cfg.expiryCheckActions.move = false
+
'namespace only',
self:assertFalse(
+
obj:makeProtectionCategory(),
obj:needsExpiry(),
+
true
'action is blacklisted in cfg.expiryCheckActions'
 
 
)
 
)
 
end
 
end
  
function suite:testProtectionNeedsExpiryFalse3()
+
function suite:testProtectionCategoryPrecedence4()
local obj = makeDefaultProtectionObject()
+
-- Test that level has a higher priority than namespace.
obj.expiry = nil
+
local protectionCategories = {
obj.action = 'move'
+
[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
obj._cfg.expiryCheckActions.move = nil
+
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
obj.reason = nil
+
}
self:assertFalse(
+
local obj = makeDefaultProtectionObject{
obj:needsExpiry(),
+
protectionCategories = protectionCategories,
'cfg.expiryCheckActions is nil, and no reason is set'
+
}
)
+
self:assertStringContains(
end
+
'all namespaces allowed',
 
+
obj:makeProtectionCategory(),
function suite:testProtectionNeedsExpiryFalse4()
+
true
local obj = makeDefaultProtectionObject()
 
obj.expiry = nil
 
obj.action = 'move'
 
obj._cfg.expiryCheckActions.move = nil
 
obj.reason = 'vandalism'
 
obj._cfg.reasonsWithoutExpiryCheck.vandalism = true
 
self:assertFalse(
 
obj:needsExpiry(),
 
'cfg.expiryCheckActions is nil, and the reason is blacklisted'
 
 
)
 
)
 
end
 
end
  
function suite:testProtectionNeedsExpiryTrue1()
+
function suite:testProtectionCategoryPrecedence5()
local obj = makeDefaultProtectionObject()
+
-- Test that action has a higher priority than level.
obj.expiry = nil
+
local protectionCategories = {
obj.action = 'move'
+
[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
obj._cfg.expiryCheckActions.move = true
+
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
self:assertTrue(
+
}
obj:needsExpiry(),
+
local obj = makeDefaultProtectionObject{
'expiry is nil and action is whitelisted in cfg.expiryCheckActions'
+
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'all levels allowed',
 +
obj:makeProtectionCategory(),
 +
true
 
)
 
)
 
end
 
end
  
function suite:testProtectionNeedsExpiryTrue2()
+
function suite:testProtectionCategoryPrecedence6()
local obj = makeDefaultProtectionObject()
+
-- Test that an exact match will be first.
obj.expiry = nil
+
local protectionCategories = {
obj.action = 'move'
+
[makeDefaultProtectionCategoryKey()] = 'exact match',
obj._cfg.expiryCheckActions.move = nil
+
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
obj.reason = 'vandalism'
+
}
obj._cfg.reasonsWithoutExpiryCheck.vandalism = nil
+
local obj = makeDefaultProtectionObject{
self:assertTrue(
+
protectionCategories = protectionCategories,
obj:needsExpiry(),
+
}
'expiry and expiryCheckActions are nil, and the reason is present and'
+
self:assertStringContains(
.. ' not blacklisted.'
+
'exact match',
 +
obj:makeProtectionCategory(),
 +
true
 
)
 
)
 
end
 
end
  
-- isIncorrect
+
function suite:testProtectionCategoryPrecedence7()
 +
-- Test that matching 4 keys will come before matching 3 keys
 +
local protectionCategories = {
 +
[makeDefaultProtectionCategoryKey{action = 'all'}] = 'four keys',
 +
[makeDefaultProtectionCategoryKey{action = 'all', level = 'all'}] = 'three keys',
 +
}
 +
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'four keys',
 +
obj:makeProtectionCategory(),
 +
true
 +
)
 +
end
  
function suite:testProtectionIsIncorrectTrue1()
+
function suite:testProtectionCategoryPrecedence8()
local obj = makeDefaultProtectionObject()
+
-- Test that matching 3 keys will come before matching 2 keys
function obj:isProtected()
+
local protectionCategories = {
return false
+
[makeProtectionCategoryKey{reason = d.reason, action = d.action, level = d.level}] = 'three keys',
end
+
[makeProtectionCategoryKey{reason = d.reason, action = d.action}] = 'two keys',
self:assertTrue(obj:isIncorrect(), 'Protection:isProtected() returned false')
+
}
 +
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'three keys',
 +
obj:makeProtectionCategory(),
 +
true
 +
)
 
end
 
end
  
function suite:testProtectionIsIncorrectTrue2()
+
function suite:testProtectionCategoryPrecedence9()
local obj = makeDefaultProtectionObject()
+
-- Test that matching 2 keys will come before matching 1 key
function obj:isProtected()
+
local protectionCategories = {
return true
+
[makeProtectionCategoryKey{action = d.action, level = d.level}] = 'two keys',
end
+
[makeProtectionCategoryKey{action = d.action}] = 'one key',
obj.expiry = 0
+
}
self:assertTrue(obj:isIncorrect(), 'the page is protected and expiry is in the past')
+
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'two keys',
 +
obj:makeProtectionCategory(),
 +
true
 +
)
 
end
 
end
  
function suite:testProtectionIsIncorrectFalse1()
+
function suite:testProtectionCategoryPrecedence10()
local obj = makeDefaultProtectionObject()
+
-- Test that matching 1 keys will come before matching 0 keys
function obj:isProtected()
+
local protectionCategories = {
return true
+
[makeProtectionCategoryKey{action = d.action}] = 'one key',
end
+
[makeProtectionCategoryKey()] = 'no keys',
obj.expiry = d.expiryU
+
}
self:assertFalse(obj:isIncorrect(), 'the page is protected and expiry is in the future')
+
local obj = makeDefaultProtectionObject{
end
+
protectionCategories = protectionCategories,
 
+
}
function suite:testProtectionIsIncorrectFalse2()
+
self:assertStringContains(
local obj = makeDefaultProtectionObject()
+
'one key',
obj.expiry = nil
+
obj:makeProtectionCategory(),
function obj:isProtected()
+
true
return true
+
)
end
 
self:assertFalse(obj:isIncorrect(), 'the page is protected and no expiry is set')
 
 
end
 
end
  
-- isTemplateProtectedNonTemplate
+
function suite:testProtectionCategoryAllAlls()
 
+
-- Test that 'all|all|all|all|all' works
function suite:testProtectionIsTemplateProtectedNonTemplateFalse1()
+
local protectionCategories = {
local obj = makeDefaultProtectionObject()
+
[makeProtectionCategoryKey()] = 'no keys',
obj.level = 'autoconfirmed'
+
}
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'the page is semi-protected')
+
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'no keys',
 +
obj:makeProtectionCategory(),
 +
true
 +
)
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateFalse2()
+
function suite:testProtectionCategoryNoFalseMatches()
local obj = makeDefaultProtectionObject()
+
-- Test that we don't match things that we aren't supposed to.
obj.level = 'templateeditor'
+
local protectionCategories = {
obj.action = 'edit'
+
[makeProtectionCategoryKey{expiry = 'foo'}]    = 'expiry foo',
rawset(obj.title, 'namespace', 10) -- template space
+
[makeProtectionCategoryKey{namespace = 'foo'}] = 'namespace foo',
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected template')
+
[makeProtectionCategoryKey{reason = 'foo'}]    = 'reason foo',
 +
[makeProtectionCategoryKey{level = 'foo'}]    = 'level foo',
 +
[makeProtectionCategoryKey{action = 'foo'}]    = 'action foo',
 +
[makeProtectionCategoryKey()]                  = 'no keys',
 +
}
 +
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'no keys',
 +
obj:makeProtectionCategory(),
 +
true
 +
)
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateFalse3()
+
function suite:testProtectionCategoryProtected()
local obj = makeDefaultProtectionObject()
+
-- Test that protected pages produce some kind of category link.
obj.level = 'templateeditor'
+
local protectionCategories = {
obj.action = 'edit'
+
[makeProtectionCategoryKey()] = 'no keys',
rawset(obj.title, 'namespace', 828) -- module space
+
}
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected module')
+
local obj = makeDefaultProtectionObject{
 +
protectionCategories = protectionCategories,
 +
}
 +
self:assertStringContains(
 +
'^%[%[Category:.*%]%]$',
 +
obj:makeProtectionCategory()
 +
)
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateFalse4()
+
function suite:testProtectionCategoryNotProtected()
local obj = makeDefaultProtectionObject()
+
-- Test that unprotected pages produce the blank string.
obj.level = 'templatemoveor'
+
local protectionCategories = {
obj.action = 'move'
+
[makeProtectionCategoryKey()] = 'no keys',
rawset(obj.title, 'namespace', 10) -- template space
+
}
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected template')
+
local obj = Protection.new(
 +
{},
 +
makeConfig{protectionCategories = protectionCategories},
 +
makeTitleObject{page = d.page, 'edit', nil}
 +
)
 +
self:assertEquals('', obj:makeProtectionCategory())
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateFalse5()
+
function suite:testProtectionCategoryNoMatch()
 +
-- Test that protected pages that don't match any categories
 +
-- produce the blank string.
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
obj.level = 'templatemoveor'
+
self:assertEquals('', obj:makeProtectionCategory())
obj.action = 'move'
 
rawset(obj.title, 'namespace', 828) -- module space
 
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected module')
 
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateTrue1()
+
function suite:testProtectionCategoryExpiryIndef()
 +
-- Test that indefinite protection matches the "indef" expiry fragment.
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
obj.level = 'templateeditor'
+
obj._cfg.protectionCategories = {
 +
[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview'}] = 'indef expiry',
 +
}
 
obj.action = 'autoreview'
 
obj.action = 'autoreview'
self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not edit or move')
+
obj.level = 'autoconfirmed'
 +
obj.expiry = 'indef'
 +
self:assertStringContains('indef expiry', obj:makeProtectionCategory(), true)
 
end
 
end
  
function suite:testProtectionIsTemplateProtectedNonTemplateTrue2()
+
function suite:testProtectionCategoryExpiryTemp()
 +
-- Test that temporary protection matches the "temp" expiry fragment.
 
local obj = makeDefaultProtectionObject()
 
local obj = makeDefaultProtectionObject()
obj.level = 'templateeditor'
+
obj._cfg.protectionCategories = {
rawset(obj.title, 'namespace', 2) -- user space
+
[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview'}] = 'temporary expiry',
self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not in template or module space')
+
}
 +
obj.action = 'autoreview'
 +
obj.level = 'autoconfirmed'
 +
obj.expiry = d.expiryU
 +
self:assertStringContains('temporary expiry', obj:makeProtectionCategory(), true)
 
end
 
end
  
-- makeCategoryLinks
+
function suite:testProtectionCategoryNoExpiry()
 
+
-- Test that pages with no expiry set don't match "indef" or "temp".
function suite:testProtectionMakeCategoryLinksAllPresent()
+
local protectionCategories = {
local obj = makeDefaultProtectionObject()
+
[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview' }] = 'temporary expiry',
obj._cfg.msg = {
+
[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview' }] = 'indefinite expiry',
['tracking-category-expiry'] = 'Expiry category',
+
[makeProtectionCategoryKey()] = 'no matches'
['tracking-category-incorrect'] = 'Incorrect category',
 
['tracking-category-template'] = 'Template category'
 
 
}
 
}
function obj:makeProtectionCategory()
+
local obj = Protection.new(
return '[[Category:Protection category|' .. d.baseText .. ']]'
+
{},
end
+
makeConfig{protectionCategories = protectionCategories},
for _, field in ipairs{'needsExpiry', 'isIncorrect', 'isTemplateProtectedNonTemplate'} do
+
makeProtectedTitleObject()
obj[field] = function () return true end
 
end
 
self:assertEquals(
 
'[[Category:Protection category|' .. d.baseText .. ']]' ..
 
'[[Category:Expiry category|' .. d.baseText .. ']]' ..
 
'[[Category:Incorrect category|' .. d.baseText .. ']]' ..
 
'[[Category:Template category|' .. d.baseText .. ']]',
 
obj:makeCategoryLinks()
 
 
)
 
)
 +
self:assertStringContains('no matches', obj:makeProtectionCategory(), true)
 
end
 
end
  
function suite:testProtectionMakeCategoryLinksAllAbsent()
+
function suite:testProtectionCategoryNamespaceFragment()
local obj = makeDefaultProtectionObject()
+
-- Test that values in cfg.categoryNamespaceKeys will work.
obj._cfg.msg = {
+
local protectionCategories = {
['tracking-category-expiry'] = 'Expiry category',
+
[makeProtectionCategoryKey{namespace = 'foobar'}] = 'we found a match',
['tracking-category-incorrect'] = 'Incorrect category',
 
['tracking-category-template'] = 'Template category'
 
 
}
 
}
function obj:makeProtectionCategory()
+
local obj = Protection.new(
return ''
+
{},
end
+
makeConfig{
for _, field in ipairs{'needsExpiry', 'isIncorrect', 'isTemplateProtectedNonTemplate'} do
+
protectionCategories = protectionCategories,
obj[field] = function () return false end
+
categoryNamespaceKeys = {[10] = 'foobar'} -- Template namespace
end
+
},
self:assertEquals('', obj:makeCategoryLinks())
+
makeTitleObject{page = 'Template:Foo', edit = 'autoconfirmed'}
 +
)
 +
self:assertStringContains('we found a match', obj:makeProtectionCategory(), true)
 
end
 
end
  
--------------------------------------------------------------------------------
+
function suite:testProtectionCategoryTalk()
-- Blurb class tests
+
-- Test that talk pages match the "talk" namespace fragment.
--------------------------------------------------------------------------------
+
local protectionCategories = {
 
+
[makeProtectionCategoryKey{namespace = 'talk'}] = 'talk namespace',
-- initialize
+
}
 
+
local obj = Protection.new(
function suite:testBlurbNew()
+
{},
local obj = Blurb.new({'foo'}, {'bar'}, {'baz'})
+
makeConfig{protectionCategories = protectionCategories},
self:assertEquals('foo', obj._protectionObj[1])
+
makeTitleObject{page = 'Template talk:Example', edit = 'autoconfirmed'}
self:assertEquals('bar', obj._args[1])
+
)
self:assertEquals('baz', obj._cfg[1])
+
self:assertStringContains('talk namespace', obj:makeProtectionCategory(), true)
 
end
 
end
  
-- _formatDate
+
-- isIncorrect
  
function suite:testBlurbFormatDateStandard()
+
function suite:testProtectionIsIncorrectTrue1()
local obj = makeDefaultBlurbObject()
+
local obj = makeDefaultProtectionObject()
self:assertEquals('1 January 1970', obj:_formatDate(0))
+
function obj:isProtected()
 +
return false
 +
end
 +
self:assertTrue(obj:isIncorrect(), 'Protection:isProtected() returned false')
 
end
 
end
  
function suite:testBlurbFormatDateCustom()
+
function suite:testProtectionIsIncorrectTrue2()
local obj = makeDefaultBlurbObject{msg = {['expiry-date-format'] = 'Y Y F F j'}}
+
local obj = makeDefaultProtectionObject()
self:assertEquals('1970 1970 January January 1', obj:_formatDate(0))
+
function obj:isProtected()
 +
return true
 +
end
 +
obj.expiry = 0
 +
self:assertTrue(obj:isIncorrect(), 'the page is protected and expiry is in the past')
 
end
 
end
  
function suite:testBlurbFormatDateError()
+
function suite:testProtectionIsIncorrectFalse1()
local obj = makeDefaultBlurbObject()
+
local obj = makeDefaultProtectionObject()
self:assertEquals(nil, obj:_formatDate('foo'))
+
function obj:isProtected()
 +
return true
 +
end
 +
obj.expiry = d.expiryU
 +
self:assertFalse(obj:isIncorrect(), 'the page is protected and expiry is in the future')
 
end
 
end
  
-- _getExpandedMessage
+
function suite:testProtectionIsIncorrectFalse2()
 
+
local obj = makeDefaultProtectionObject()
function suite:testBlurbGetExpandedMessage()
+
obj.expiry = nil
local obj = makeDefaultBlurbObject()
+
function obj:isProtected()
function obj:_substituteParameters(s)
+
return true
return 'testing ' .. s
 
 
end
 
end
obj._cfg.msg = {['test-key'] = 'test message'}
+
self:assertFalse(obj:isIncorrect(), 'the page is protected and no expiry is set')
self:assertEquals('testing test message', obj:_getExpandedMessage('test-key'))
 
 
end
 
end
  
-- _substituteParameters
+
-- isTemplateProtectedNonTemplate
 +
 
 +
function suite:testProtectionIsTemplateProtectedNonTemplateFalse1()
 +
local obj = makeDefaultProtectionObject()
 +
obj.level = 'autoconfirmed'
 +
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'the page is semi-protected')
 +
end
  
function suite:testBlurbSubstituteParameters()
+
function suite:testProtectionIsTemplateProtectedNonTemplateFalse2()
local obj = makeDefaultBlurbObject()
+
local obj = makeDefaultProtectionObject()
 +
obj.level = 'templateeditor'
 +
obj.action = 'edit'
 +
rawset(obj.title, 'namespace', 10) -- template space
 +
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected template')
 +
end
  
obj._makeCurrentVersionParameter = function () return '1' end
+
function suite:testProtectionIsTemplateProtectedNonTemplateFalse3()
obj._makeEditRequestParameter = function () return '2' end
+
local obj = makeDefaultProtectionObject()
obj._makeExpiryParameter = function () return '3' end
+
obj.level = 'templateeditor'
obj._makeExplanationBlurbParameter = function () return '4' end
+
obj.action = 'edit'
obj._makeImageLinkParameter = function () return '5' end
+
rawset(obj.title, 'namespace', 828) -- module space
obj._makeIntroBlurbParameter = function () return '6' end
+
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected module')
obj._makeIntroFragmentParameter = function () return '7' end
+
end
obj._makePagetypeParameter = function () return '8' end
+
 
obj._makeProtectionBlurbParameter = function () return '9' end
+
function suite:testProtectionIsTemplateProtectedNonTemplateFalse4()
obj._makeProtectionDateParameter = function () return '10' end
+
local obj = makeDefaultProtectionObject()
obj._makeProtectionLevelParameter = function () return '11' end
+
obj.level = 'templatemoveor'
obj._makeProtectionLogParameter = function () return '12' end
+
obj.action = 'move'
obj._makeTalkPageParameter = function () return '13' end
+
rawset(obj.title, 'namespace', 10) -- template space
obj._makeTooltipBlurbParameter = function () return '14' end
+
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected template')
obj._makeTooltipFragmentParameter = function () return '15' end
+
end
obj._makeVandalTemplateParameter = function () return '16' end
 
  
local msg = '${CURRENTVERSION}-' ..
+
function suite:testProtectionIsTemplateProtectedNonTemplateFalse5()
'${EDITREQUEST}-' ..
+
local obj = makeDefaultProtectionObject()
'${EXPIRY}-' ..
+
obj.level = 'templatemoveor'
'${EXPLANATIONBLURB}-' ..
+
obj.action = 'move'
'${IMAGELINK}-' ..
+
rawset(obj.title, 'namespace', 828) -- module space
'${INTROBLURB}-' ..
+
self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected module')
'${INTROFRAGMENT}-' ..
+
end
'${PAGETYPE}-' ..
 
'${PROTECTIONBLURB}-' ..
 
'${PROTECTIONDATE}-' ..
 
'${PROTECTIONLEVEL}-' ..
 
'${PROTECTIONLOG}-' ..
 
'${TALKPAGE}-' ..
 
'${TOOLTIPBLURB}-' ..
 
'${TOOLTIPFRAGMENT}-' ..
 
'${VANDAL}'
 
  
local expected = '1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16'
+
function suite:testProtectionIsTemplateProtectedNonTemplateTrue1()
 +
local obj = makeDefaultProtectionObject()
 +
obj.level = 'templateeditor'
 +
obj.action = 'autoreview'
 +
self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not edit or move')
 +
end
  
self:assertEquals(expected, obj:_substituteParameters(msg))
+
function suite:testProtectionIsTemplateProtectedNonTemplateTrue2()
 +
local obj = makeDefaultProtectionObject()
 +
obj.level = 'templateeditor'
 +
rawset(obj.title, 'namespace', 2) -- user space
 +
self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not in template or module space')
 
end
 
end
 
-- makeCurrentVersionParameter
 
  
function suite:testBlurbMakeCurrentVersionParameterEdit()
+
-- makeCategoryLinks
local obj = makeDefaultBlurbObject()
+
 
obj._protectionObj.action = 'edit'
+
function suite:testProtectionMakeCategoryLinksAllPresent()
obj._cfg.msg['current-version-edit-display'] = 'edit display'
+
local obj = makeDefaultProtectionObject()
 +
obj._cfg.msg = {
 +
['tracking-category-incorrect'] = 'Incorrect category',
 +
['tracking-category-template'] = 'Template category'
 +
}
 +
function obj:makeProtectionCategory()
 +
return '[[Category:Protection category|' .. d.baseText .. ']]'
 +
end
 +
for _, field in ipairs{'isIncorrect', 'isTemplateProtectedNonTemplate'} do
 +
obj[field] = function () return true end
 +
end
 
self:assertEquals(
 
self:assertEquals(
'[//en.wikipedia.org/w/index.php?title='
+
'[[Category:Protection category|' .. d.baseText .. ']]' ..
.. mw.uri.encode(d.page)
+
'[[Category:Incorrect category|' .. d.baseText .. ']]' ..
.. '&action=history edit display]',
+
'[[Category:Template category|' .. d.baseText .. ']]',
obj:_makeCurrentVersionParameter()
+
obj:makeCategoryLinks()
 
)
 
)
 
end
 
end
  
function suite:testBlurbMakeCurrentVersionParameterMove()
+
function suite:testProtectionMakeCategoryLinksAllAbsent()
local obj = makeDefaultBlurbObject()
+
local obj = makeDefaultProtectionObject()
obj._protectionObj.action = 'move'
+
obj._cfg.msg = {
obj._cfg.msg['current-version-move-display'] = 'move display'
+
['tracking-category-expiry'] = 'Expiry category',
self:assertEquals(
+
['tracking-category-incorrect'] = 'Incorrect category',
'[//en.wikipedia.org/w/index.php?title='
+
['tracking-category-template'] = 'Template category'
.. mw.uri.encode('Special:Log')
+
}
.. '&page='
+
function obj:makeProtectionCategory()
.. mw.uri.encode(d.page)
+
return ''
.. '&type=move move display]',
+
end
obj:_makeCurrentVersionParameter()
+
for _, field in ipairs{'needsExpiry', 'isIncorrect', 'isTemplateProtectedNonTemplate'} do
)
+
obj[field] = function () return false end
 +
end
 +
self:assertEquals('', obj:makeCategoryLinks())
 
end
 
end
  
-- _makeEditRequestParameter
+
--------------------------------------------------------------------------------
 +
-- Blurb class tests
 +
--------------------------------------------------------------------------------
 +
 
 +
-- initialize
  
function suite:testBlurbMakeEditRequestParameterLevels()
+
function suite:testBlurbNew()
-- We can't test the edit requests directly as that is the responsibility of
+
local obj = Blurb.new({'foo'}, {'bar'}, {'baz'})
-- [[Module:Submit an edit request]], but we can check that we get different
+
self:assertEquals('foo', obj._protectionObj[1])
-- outputs for different protection levels.
+
self:assertEquals('bar', obj._args[1])
local function makeBlurbObjectWithLevels(action, level)
+
self:assertEquals('baz', obj._cfg[1])
local obj = makeDefaultBlurbObject()
+
end
obj._protectionObj.action = action
+
 
obj._protectionObj.level = level
+
-- _formatDate
obj._cfg.msg['edit-request-display'] = 'display'
 
return obj
 
end
 
local obj1 = makeBlurbObjectWithLevels('edit', 'autoconfirmed')
 
local obj2 = makeBlurbObjectWithLevels('edit', 'templateeditor')
 
local obj3 = makeBlurbObjectWithLevels('edit', 'sysop')
 
local obj4 = makeBlurbObjectWithLevels('move', 'templateeditor')
 
  
self:assertFalse(obj1:_makeEditRequestParameter() == obj2:_makeEditRequestParameter())
+
function suite:testBlurbFormatDateStandard()
self:assertFalse(obj2:_makeEditRequestParameter() == obj3:_makeEditRequestParameter())
+
local obj = makeDefaultBlurbObject()
self:assertEquals(obj3:_makeEditRequestParameter(), obj4:_makeEditRequestParameter())
+
self:assertEquals('1 January 1970', obj:_formatDate(0))
 
end
 
end
  
function suite:testBlurbMakeEditRequestParameterLink()
+
function suite:testBlurbFormatDateCustom()
-- Check that the edit request links have features that we can always expect
+
local obj = makeDefaultBlurbObject{msg = {['expiry-date-format'] = 'Y Y F F j'}}
-- to be there. The rest is subject to be changed by [[Module:Submit an edit request]]
+
self:assertEquals('1970 1970 January January 1', obj:_formatDate(0))
-- at any time, so we won't test that here.
 
local obj = makeDefaultBlurbObject()
 
obj._protectionObj.action = 'edit'
 
obj._protectionObj.level = 'autoconfirmed'
 
obj._cfg.msg['edit-request-display'] = 'the edit request display'
 
self:assertStringContains(
 
'//en.wikipedia.org/w/index.php?',
 
obj:_makeEditRequestParameter(),
 
true
 
)
 
self:assertStringContains(
 
'action=edit',
 
obj:_makeEditRequestParameter(),
 
true
 
)
 
self:assertStringContains(
 
'title=[a-zA-Z0-9%%_]*[tT]alk',
 
obj:_makeEditRequestParameter(),
 
false
 
)
 
self:assertStringContains(
 
'the edit request display',
 
obj:_makeEditRequestParameter(),
 
true
 
)
 
 
end
 
end
  
-- _makeExpiryParameter
+
function suite:testBlurbFormatDateError()
 
 
function suite:testBlurbMakeExpiryParameterTemp()
 
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.expiry = 0
+
self:assertEquals(nil, obj:_formatDate('foo'))
function obj:_formatDate(num)
+
end
return 'unix date is ' .. tostring(num)
+
 
end
+
-- _getExpandedMessage
self:assertEquals('unix date is 0', obj:_makeExpiryParameter())
 
end
 
  
function suite:testBlurbMakeExpiryParameterOther()
+
function suite:testBlurbGetExpandedMessage()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.expiry = 'indef'
+
function obj:_substituteParameters(s)
function obj:_formatDate(num)
+
return 'testing ' .. s
return 'unix date is ' .. tostring(num)
 
 
end
 
end
self:assertEquals('indef', obj:_makeExpiryParameter())
+
obj._cfg.msg = {['test-key'] = 'test message'}
 +
self:assertEquals('testing test message', obj:_getExpandedMessage('test-key'))
 
end
 
end
  
-- _makeExplanationBlurbParameter
+
-- _substituteParameters
  
function suite:testBlurbMakeExplanationBlurbParameter()
+
function suite:testBlurbSubstituteParameters()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.action = 'edit'
 
obj._protectionObj.level = 'autoconfirmed'
 
rawset(obj._protectionObj.title, 'isTalkPage', true)
 
  
obj._cfg.explanationBlurbs = {
+
obj._makeCurrentVersionParameter = function () return '1' end
edit = {
+
obj._makeEditRequestParameter = function () return '2' end
autoconfirmed = {
+
obj._makeExpiryParameter = function () return '3' end
talk = 'edit-autoconfirmed-talk',
+
obj._makeExplanationBlurbParameter = function () return '4' end
default = 'edit-autoconfirmed-default'
+
obj._makeImageLinkParameter = function () return '5' end
},
+
obj._makeIntroBlurbParameter = function () return '6' end
default = {
+
obj._makeIntroFragmentParameter = function () return '7' end
talk = 'edit-default-talk',
+
obj._makePagetypeParameter = function () return '8' end
default = 'edit-default-default'
+
obj._makeProtectionBlurbParameter = function () return '9' end
}
+
obj._makeProtectionDateParameter = function () return '10' end
}
+
obj._makeProtectionLevelParameter = function () return '11' end
}
+
obj._makeProtectionLogParameter = function () return '12' end
 +
obj._makeTalkPageParameter = function () return '13' end
 +
obj._makeTooltipBlurbParameter = function () return '14' end
 +
obj._makeTooltipFragmentParameter = function () return '15' end
 +
obj._makeVandalTemplateParameter = function () return '16' end
  
function obj:_substituteParameters(msg)
+
local msg = '${CURRENTVERSION}-' ..
return msg
+
'${EDITREQUEST}-' ..
end
+
'${EXPIRY}-' ..
 +
'${EXPLANATIONBLURB}-' ..
 +
'${IMAGELINK}-' ..
 +
'${INTROBLURB}-' ..
 +
'${INTROFRAGMENT}-' ..
 +
'${PAGETYPE}-' ..
 +
'${PROTECTIONBLURB}-' ..
 +
'${PROTECTIONDATE}-' ..
 +
'${PROTECTIONLEVEL}-' ..
 +
'${PROTECTIONLOG}-' ..
 +
'${TALKPAGE}-' ..
 +
'${TOOLTIPBLURB}-' ..
 +
'${TOOLTIPFRAGMENT}-' ..
 +
'${VANDAL}'
 +
 
 +
local expected = '1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16'
 +
 
 +
self:assertEquals(expected, obj:_substituteParameters(msg))
 +
end
 +
 +
-- makeCurrentVersionParameter
  
 +
function suite:testBlurbMakeCurrentVersionParameterEdit()
 +
local obj = makeDefaultBlurbObject()
 +
obj._protectionObj.action = 'edit'
 +
obj._cfg.msg['current-version-edit-display'] = 'edit display'
 
self:assertEquals(
 
self:assertEquals(
'edit-autoconfirmed-talk',
+
'[//en.wikipedia.org/w/index.php?title='
obj:_makeExplanationBlurbParameter()
+
.. mw.uri.encode(d.page)
 +
.. '&action=history edit display]',
 +
obj:_makeCurrentVersionParameter()
 
)
 
)
 +
end
  
obj._cfg.explanationBlurbs.edit.autoconfirmed.talk = nil
+
function suite:testBlurbMakeCurrentVersionParameterMove()
 +
local obj = makeDefaultBlurbObject()
 +
obj._protectionObj.action = 'move'
 +
obj._cfg.msg['current-version-move-display'] = 'move display'
 
self:assertEquals(
 
self:assertEquals(
'edit-autoconfirmed-default',
+
'[//en.wikipedia.org/w/index.php?title='
obj:_makeExplanationBlurbParameter()
+
.. mw.uri.encode('Special:Log')
 +
.. '&page='
 +
.. mw.uri.encode(d.page)
 +
.. '&type=move move display]',
 +
obj:_makeCurrentVersionParameter()
 
)
 
)
 +
end
  
obj._cfg.explanationBlurbs.edit.autoconfirmed.default = nil
+
-- _makeEditRequestParameter
self:assertEquals(
 
'edit-default-talk',
 
obj:_makeExplanationBlurbParameter()
 
)
 
  
obj._cfg.explanationBlurbs.edit.default.talk = nil
+
function suite:testBlurbMakeEditRequestParameterLevels()
self:assertEquals(
+
-- We can't test the edit requests directly as that is the responsibility of
'edit-default-default',
+
-- [[Module:Submit an edit request]], but we can check that we get different
obj:_makeExplanationBlurbParameter()
+
-- outputs for different protection levels.
)
+
local function makeBlurbObjectWithLevels(action, level)
 +
local obj = makeDefaultBlurbObject()
 +
obj._protectionObj.action = action
 +
obj._protectionObj.level = level
 +
obj._cfg.msg['edit-request-display'] = 'display'
 +
return obj
 +
end
 +
local obj1 = makeBlurbObjectWithLevels('edit', 'autoconfirmed')
 +
local obj2 = makeBlurbObjectWithLevels('edit', 'templateeditor')
 +
local obj3 = makeBlurbObjectWithLevels('edit', 'sysop')
 +
local obj4 = makeBlurbObjectWithLevels('move', 'templateeditor')
  
obj._cfg.explanationBlurbs.edit.default.default = nil
+
self:assertFalse(obj1:_makeEditRequestParameter() == obj2:_makeEditRequestParameter())
self:assertError(
+
self:assertFalse(obj2:_makeEditRequestParameter() == obj3:_makeEditRequestParameter())
obj._makeExplanationBlurbParameter,
+
self:assertEquals(obj3:_makeEditRequestParameter(), obj4:_makeEditRequestParameter())
{obj},
 
'could not find explanation blurb for action "edit",'
 
.. ' level "autoconfirmed" and talk key "talk"'
 
)
 
 
end
 
end
  
function suite:testBlurbMakeExplanationBlurbParameterSpecialCases()
+
function suite:testBlurbMakeEditRequestParameterLink()
 +
-- Check that the edit request links have features that we can always expect
 +
-- to be there. The rest is subject to be changed by [[Module:Submit an edit request]]
 +
-- at any time, so we won't test that here.
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
rawset(obj._protectionObj.title, 'namespace', 8)
+
obj._protectionObj.action = 'edit'
function obj:_getExpandedMessage(key)
+
obj._protectionObj.level = 'autoconfirmed'
return 'the key is ' .. key
+
obj._cfg.msg['edit-request-display'] = 'the edit request display'
end
+
self:assertStringContains(
self:assertEquals(
+
'//en.wikipedia.org/w/index.php?',
'the key is explanation-blurb-nounprotect',
+
obj:_makeEditRequestParameter(),
obj:_makeExplanationBlurbParameter()
+
true
 +
)
 +
self:assertStringContains(
 +
'action=edit',
 +
obj:_makeEditRequestParameter(),
 +
true
 +
)
 +
self:assertStringContains(
 +
'title=[a-zA-Z0-9%%_]*[tT]alk',
 +
obj:_makeEditRequestParameter(),
 +
false
 +
)
 +
self:assertStringContains(
 +
'the edit request display',
 +
obj:_makeEditRequestParameter(),
 +
true
 
)
 
)
 
end
 
end
  
-- _makeImageLinkParameter
+
-- _makeExpiryParameter
  
function suite:testBlurbMakeImageLinkParameter()
+
function suite:testBlurbMakeExpiryParameterTemp()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.action = 'move'
+
obj._protectionObj.expiry = 0
obj._protectionObj.level = 'sysop'
+
function obj:_formatDate(num)
 +
return 'unix date is ' .. tostring(num)
 +
end
 +
self:assertEquals('unix date is 0', obj:_makeExpiryParameter())
 +
end
  
obj._cfg.imageLinks = {
+
function suite:testBlurbMakeExpiryParameterOther()
edit = {
+
local obj = makeDefaultBlurbObject()
sysop = 'edit-sysop',
+
obj._protectionObj.expiry = 'indef'
default = 'edit-default'
+
function obj:_formatDate(num)
},
+
return 'unix date is ' .. tostring(num)
move = {
+
end
sysop = 'move-sysop',
+
self:assertEquals('indef', obj:_makeExpiryParameter())
default = 'move-default'
+
end
}
 
}
 
  
function obj:_substituteParameters(msg)
+
-- _makeExplanationBlurbParameter
return msg
 
end
 
  
self:assertEquals(
+
function suite:testBlurbMakeExplanationBlurbParameter()
'move-sysop',
+
local obj = makeDefaultBlurbObject()
obj:_makeImageLinkParameter()
+
obj._protectionObj.action = 'edit'
)
+
obj._protectionObj.level = 'autoconfirmed'
 +
rawset(obj._protectionObj.title, 'isTalkPage', true)
 +
 
 +
obj._cfg.explanationBlurbs = {
 +
edit = {
 +
autoconfirmed = {
 +
talk = 'edit-autoconfirmed-talk',
 +
default = 'edit-autoconfirmed-default'
 +
},
 +
default = {
 +
talk = 'edit-default-talk',
 +
default = 'edit-default-default'
 +
}
 +
}
 +
}
 +
 
 +
function obj:_substituteParameters(msg)
 +
return msg
 +
end
  
obj._cfg.imageLinks.move.sysop = nil
 
 
self:assertEquals(
 
self:assertEquals(
'move-default',
+
'edit-autoconfirmed-talk',
obj:_makeImageLinkParameter()
+
obj:_makeExplanationBlurbParameter()
 
)
 
)
  
obj._cfg.imageLinks.move.default = nil
+
obj._cfg.explanationBlurbs.edit.autoconfirmed.talk = nil
 
self:assertEquals(
 
self:assertEquals(
'edit-default',
+
'edit-autoconfirmed-default',
obj:_makeImageLinkParameter()
+
obj:_makeExplanationBlurbParameter()
 
)
 
)
end
 
  
-- _makeIntroBlurbParameter
+
obj._cfg.explanationBlurbs.edit.autoconfirmed.default = nil
 
 
function suite:testBlurbMakeIntroBlurbParameter()
 
local obj = makeDefaultBlurbObject()
 
function obj._protectionObj:isTemporary()
 
return true
 
end
 
function obj:_getExpandedMessage(key)
 
return 'the key is ' .. key
 
end
 
 
self:assertEquals(
 
self:assertEquals(
'the key is intro-blurb-expiry',
+
'edit-default-talk',
obj:_makeIntroBlurbParameter()
+
obj:_makeExplanationBlurbParameter()
 
)
 
)
  
function obj._protectionObj:isTemporary()
+
obj._cfg.explanationBlurbs.edit.default.talk = nil
return false
 
end
 
 
self:assertEquals(
 
self:assertEquals(
'the key is intro-blurb-noexpiry',
+
'edit-default-default',
obj:_makeIntroBlurbParameter()
+
obj:_makeExplanationBlurbParameter()
 
)
 
)
end
 
  
-- _makeIntroFragmentParameter
+
obj._cfg.explanationBlurbs.edit.default.default = nil
 +
self:assertError(
 +
obj._makeExplanationBlurbParameter,
 +
{obj},
 +
'could not find explanation blurb for action "edit",'
 +
.. ' level "autoconfirmed" and talk key "talk"'
 +
)
 +
end
  
function suite:testBlurbMakeIntroFragmentParameter()
+
function suite:testBlurbMakeExplanationBlurbParameterSpecialCases()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
function obj._protectionObj:isTemporary()
+
rawset(obj._protectionObj.title, 'namespace', 8)
return true
 
end
 
 
function obj:_getExpandedMessage(key)
 
function obj:_getExpandedMessage(key)
 
return 'the key is ' .. key
 
return 'the key is ' .. key
 
end
 
end
 
self:assertEquals(
 
self:assertEquals(
'the key is intro-fragment-expiry',
+
'the key is explanation-blurb-nounprotect',
obj:_makeIntroFragmentParameter()
+
obj:_makeExplanationBlurbParameter()
)
 
 
 
function obj._protectionObj:isTemporary()
 
return false
 
end
 
self:assertEquals(
 
'the key is intro-fragment-noexpiry',
 
obj:_makeIntroFragmentParameter()
 
 
)
 
)
 
end
 
end
  
-- _makePagetypeParameter
+
-- _makeImageLinkParameter
  
function suite:testPagetypeParameter()
+
function suite:testBlurbMakeImageLinkParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
rawset(obj._protectionObj.title, 'namespace', 3)
+
obj._protectionObj.action = 'move'
 +
obj._protectionObj.level = 'sysop'
  
obj._cfg.pagetypes = {
+
obj._cfg.imageLinks = {
[3] = 'user talk page',
+
edit = {
default = 'default page'
+
sysop = 'edit-sysop',
 +
default = 'edit-default'
 +
},
 +
move = {
 +
sysop = 'move-sysop',
 +
default = 'move-default'
 +
}
 
}
 
}
 +
 +
function obj:_substituteParameters(msg)
 +
return msg
 +
end
  
 
self:assertEquals(
 
self:assertEquals(
'user talk page',
+
'move-sysop',
obj:_makePagetypeParameter()
+
obj:_makeImageLinkParameter()
 
)
 
)
  
obj._cfg.pagetypes[3] = nil
+
obj._cfg.imageLinks.move.sysop = nil
 
self:assertEquals(
 
self:assertEquals(
'default page',
+
'move-default',
obj:_makePagetypeParameter()
+
obj:_makeImageLinkParameter()
 
)
 
)
  
obj._cfg.pagetypes.default = nil
+
obj._cfg.imageLinks.move.default = nil
self:assertError(
+
self:assertEquals(
obj._makePagetypeParameter,
+
'edit-default',
{obj},
+
obj:_makeImageLinkParameter()
'no default pagetype defined'
 
 
)
 
)
 
end
 
end
  
-- _makeProtectionBlurbParameter
+
-- _makeIntroBlurbParameter
  
function suite:testBlurbMakeProtectionBlurbParameter()
+
function suite:testBlurbMakeIntroBlurbParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.action = 'move'
+
function obj._protectionObj:isTemporary()
obj._protectionObj.level = 'sysop'
+
return true
 +
end
 +
function obj:_getExpandedMessage(key)
 +
return 'the key is ' .. key
 +
end
 +
self:assertEquals(
 +
'the key is intro-blurb-expiry',
 +
obj:_makeIntroBlurbParameter()
 +
)
  
obj._cfg.protectionBlurbs = {
+
function obj._protectionObj:isTemporary()
edit = {
+
return false
sysop = 'edit-sysop',
+
end
default = 'edit-default'
+
self:assertEquals(
},
+
'the key is intro-blurb-noexpiry',
move = {
+
obj:_makeIntroBlurbParameter()
sysop = 'move-sysop',
+
)
default = 'move-default'
+
end
}
+
 
}
+
-- _makeIntroFragmentParameter
  
function obj:_substituteParameters(msg)
+
function suite:testBlurbMakeIntroFragmentParameter()
return msg
+
local obj = makeDefaultBlurbObject()
 +
function obj._protectionObj:isTemporary()
 +
return true
 +
end
 +
function obj:_getExpandedMessage(key)
 +
return 'the key is ' .. key
 
end
 
end
 
 
self:assertEquals(
 
self:assertEquals(
'move-sysop',
+
'the key is intro-fragment-expiry',
obj:_makeProtectionBlurbParameter()
+
obj:_makeIntroFragmentParameter()
 
)
 
)
  
obj._cfg.protectionBlurbs.move.sysop = nil
+
function obj._protectionObj:isTemporary()
 +
return false
 +
end
 
self:assertEquals(
 
self:assertEquals(
'move-default',
+
'the key is intro-fragment-noexpiry',
obj:_makeProtectionBlurbParameter()
+
obj:_makeIntroFragmentParameter()
)
 
 
 
obj._cfg.protectionBlurbs.move.default = nil
 
self:assertEquals(
 
'edit-default',
 
obj:_makeProtectionBlurbParameter()
 
)
 
 
 
obj._cfg.protectionBlurbs.edit.default = nil
 
self:assertError(
 
obj._makeProtectionBlurbParameter,
 
{obj},
 
'no protection blurb defined for protectionBlurbs.edit.default'
 
 
)
 
)
 
end
 
end
  
-- _makeProtectionDateParameter
+
-- _makePagetypeParameter
  
function suite:testBlurbMakeProtectionDateParameter()
+
function suite:testPagetypeParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.protectionDate = 0
+
rawset(obj._protectionObj.title, 'namespace', 3)
function obj:_formatDate(num)
 
return 'unix date is ' .. tostring(num)
 
end
 
self:assertEquals('unix date is 0', obj:_makeProtectionDateParameter())
 
  
obj._protectionObj.protectionDate = 'indef'
+
obj._cfg.pagetypes = {
self:assertEquals('indef', obj:_makeProtectionDateParameter())
+
[3] = 'user talk page',
 +
default = 'default page'
 +
}
 +
 
 +
self:assertEquals(
 +
'user talk page',
 +
obj:_makePagetypeParameter()
 +
)
 +
 
 +
obj._cfg.pagetypes[3] = nil
 +
self:assertEquals(
 +
'default page',
 +
obj:_makePagetypeParameter()
 +
)
 +
 
 +
obj._cfg.pagetypes.default = nil
 +
self:assertError(
 +
obj._makePagetypeParameter,
 +
{obj},
 +
'no default pagetype defined'
 +
)
 
end
 
end
  
-- _makeProtectionLevelParameter
+
-- _makeProtectionBlurbParameter
  
function suite:testBlurbMakeProtectionLevelParameter()
+
function suite:testBlurbMakeProtectionBlurbParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
 
obj._protectionObj.action = 'move'
 
obj._protectionObj.action = 'move'
 
obj._protectionObj.level = 'sysop'
 
obj._protectionObj.level = 'sysop'
  
obj._cfg.protectionLevels = {
+
obj._cfg.protectionBlurbs = {
 
edit = {
 
edit = {
 
sysop = 'edit-sysop',
 
sysop = 'edit-sysop',
Line 1,533: Line 1,583:
 
self:assertEquals(
 
self:assertEquals(
 
'move-sysop',
 
'move-sysop',
obj:_makeProtectionLevelParameter()
+
obj:_makeProtectionBlurbParameter()
 
)
 
)
  
obj._cfg.protectionLevels.move.sysop = nil
+
obj._cfg.protectionBlurbs.move.sysop = nil
 
self:assertEquals(
 
self:assertEquals(
 
'move-default',
 
'move-default',
obj:_makeProtectionLevelParameter()
+
obj:_makeProtectionBlurbParameter()
 
)
 
)
  
obj._cfg.protectionLevels.move.default = nil
+
obj._cfg.protectionBlurbs.move.default = nil
 
self:assertEquals(
 
self:assertEquals(
 
'edit-default',
 
'edit-default',
obj:_makeProtectionLevelParameter()
+
obj:_makeProtectionBlurbParameter()
 
)
 
)
  
obj._cfg.protectionLevels.edit.default = nil
+
obj._cfg.protectionBlurbs.edit.default = nil
 
self:assertError(
 
self:assertError(
obj._makeProtectionLevelParameter,
+
obj._makeProtectionBlurbParameter,
 
{obj},
 
{obj},
'no protection level defined for protectionLevels.edit.default'
+
'no protection blurb defined for protectionBlurbs.edit.default'
 
)
 
)
 
end
 
end
  
-- _makeProtectionLogParameter
+
-- _makeProtectionDateParameter
  
function suite:testBlurbMakeProtectionLogParameterPC()
+
function suite:testBlurbMakeProtectionDateParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.action = 'autoreview'
+
obj._protectionObj.protectionDate = 0
function obj:_getExpandedMessage(key)
+
function obj:_formatDate(num)
return 'the key is ' .. key
+
return 'unix date is ' .. tostring(num)
 
end
 
end
self:assertStringContains(
+
self:assertEquals('unix date is 0', obj:_makeProtectionDateParameter())
'^%[//en%.wikipedia%.org/w/index%.php?',
+
 
obj:_makeProtectionLogParameter(),
+
obj._protectionObj.protectionDate = 'indef'
false
+
self:assertEquals('indef', obj:_makeProtectionDateParameter())
)
 
self:assertStringContains(
 
'title=' .. mw.uri.encode('Special:Log'),
 
obj:_makeProtectionLogParameter(),
 
true
 
)
 
self:assertStringContains(
 
'type=stable',
 
obj:_makeProtectionLogParameter(),
 
true
 
)
 
self:assertStringContains(
 
'page=' .. mw.uri.encode(d.page),
 
obj:_makeProtectionLogParameter(),
 
true
 
)
 
self:assertStringContains(
 
'the key is pc%-log%-display%]$',
 
obj:_makeProtectionLogParameter(),
 
false
 
)
 
 
end
 
end
  
function suite:testBlurbMakeProtectionLogParameterProtection()
+
-- _makeProtectionLevelParameter
 +
 
 +
function suite:testBlurbMakeProtectionLevelParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.action = 'edit'
+
obj._protectionObj.action = 'move'
function obj:_getExpandedMessage(key)
+
obj._protectionObj.level = 'sysop'
return 'the key is ' .. key
+
 
 +
obj._cfg.protectionLevels = {
 +
edit = {
 +
sysop = 'edit-sysop',
 +
default = 'edit-default'
 +
},
 +
move = {
 +
sysop = 'move-sysop',
 +
default = 'move-default'
 +
}
 +
}
 +
 
 +
function obj:_substituteParameters(msg)
 +
return msg
 
end
 
end
self:assertStringContains(
+
 
'^%[//en%.wikipedia%.org/w/index%.php?',
+
self:assertEquals(
obj:_makeProtectionLogParameter(),
+
'move-sysop',
false
+
obj:_makeProtectionLevelParameter()
 
)
 
)
self:assertStringContains(
+
 
'title=' .. mw.uri.encode('Special:Log'),
+
obj._cfg.protectionLevels.move.sysop = nil
obj:_makeProtectionLogParameter(),
+
self:assertEquals(
true
+
'move-default',
 +
obj:_makeProtectionLevelParameter()
 
)
 
)
self:assertStringContains(
+
 
'type=protect',
+
obj._cfg.protectionLevels.move.default = nil
obj:_makeProtectionLogParameter(),
+
self:assertEquals(
true
+
'edit-default',
 +
obj:_makeProtectionLevelParameter()
 
)
 
)
self:assertStringContains(
+
 
'page=' .. mw.uri.encode(d.page),
+
obj._cfg.protectionLevels.edit.default = nil
obj:_makeProtectionLogParameter(),
+
self:assertError(
true
+
obj._makeProtectionLevelParameter,
)
+
{obj},
self:assertStringContains(
+
'no protection level defined for protectionLevels.edit.default'
'the key is protection%-log%-display%]$',
 
obj:_makeProtectionLogParameter(),
 
false
 
 
)
 
)
 
end
 
end
  
-- _makeTalkPageParameter
+
-- _makeProtectionLogParameter
  
function suite:testBlurbMakeTalkPageParameter()
+
function suite:testBlurbMakeProtectionLogParameterPC()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
 +
obj._protectionObj.action = 'autoreview'
 
function obj:_getExpandedMessage(key)
 
function obj:_getExpandedMessage(key)
 
return 'the key is ' .. key
 
return 'the key is ' .. key
 
end
 
end
self:assertEquals(
+
self:assertStringContains(
'[['
+
'^%[//en%.wikipedia%.org/w/index%.php?',
.. d.talkPage .. '#top|'
+
obj:_makeProtectionLogParameter(),
.. 'the key is talk-page-link-display'
+
false
.. ']]',
 
obj:_makeTalkPageParameter()
 
 
)
 
)
obj._args.section = 'talk section'
+
self:assertStringContains(
self:assertEquals(
+
'title=' .. mw.uri.encode('Special:Log'),
'[['
+
obj:_makeProtectionLogParameter(),
.. d.talkPage .. '#talk section|'
+
true
.. 'the key is talk-page-link-display'
 
.. ']]',
 
obj:_makeTalkPageParameter()
 
 
)
 
)
end
+
self:assertStringContains(
 
+
'type=stable',
-- _makeTooltipBlurbParameter
+
obj:_makeProtectionLogParameter(),
 +
true
 +
)
 +
self:assertStringContains(
 +
'page=' .. mw.uri.encode(d.page),
 +
obj:_makeProtectionLogParameter(),
 +
true
 +
)
 +
self:assertStringContains(
 +
'the key is pc%-log%-display%]$',
 +
obj:_makeProtectionLogParameter(),
 +
false
 +
)
 +
end
  
function suite:testBlurbMakeTooltipBlurbParameter()
+
function suite:testBlurbMakeProtectionLogParameterProtection()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
function obj._protectionObj:isTemporary()
+
obj._protectionObj.action = 'edit'
return true
 
end
 
 
function obj:_getExpandedMessage(key)
 
function obj:_getExpandedMessage(key)
 
return 'the key is ' .. key
 
return 'the key is ' .. key
 
end
 
end
self:assertEquals(
+
self:assertStringContains(
'the key is tooltip-blurb-expiry',
+
'^%[//en%.wikipedia%.org/w/index%.php?',
obj:_makeTooltipBlurbParameter()
+
obj:_makeProtectionLogParameter(),
 +
false
 
)
 
)
 
+
self:assertStringContains(
function obj._protectionObj:isTemporary()
+
'title=' .. mw.uri.encode('Special:Log'),
return false
+
obj:_makeProtectionLogParameter(),
end
+
true
self:assertEquals(
+
)
'the key is tooltip-blurb-noexpiry',
+
self:assertStringContains(
obj:_makeTooltipBlurbParameter()
+
'type=protect',
 +
obj:_makeProtectionLogParameter(),
 +
true
 +
)
 +
self:assertStringContains(
 +
'page=' .. mw.uri.encode(d.page),
 +
obj:_makeProtectionLogParameter(),
 +
true
 +
)
 +
self:assertStringContains(
 +
'the key is protection%-log%-display%]$',
 +
obj:_makeProtectionLogParameter(),
 +
false
 
)
 
)
 
end
 
end
  
-- _makeTooltipFragmentParameter
+
-- _makeTalkPageParameter
  
function suite:testBlurbMakeTooltipFragmentParameter()
+
function suite:testBlurbMakeTalkPageParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
function obj._protectionObj:isTemporary()
 
return true
 
end
 
 
function obj:_getExpandedMessage(key)
 
function obj:_getExpandedMessage(key)
 
return 'the key is ' .. key
 
return 'the key is ' .. key
 
end
 
end
 
self:assertEquals(
 
self:assertEquals(
'the key is tooltip-fragment-expiry',
+
'[['
obj:_makeTooltipFragmentParameter()
+
.. d.talkPage .. '#top|'
 +
.. 'the key is talk-page-link-display'
 +
.. ']]',
 +
obj:_makeTalkPageParameter()
 
)
 
)
 
+
obj._args.section = 'talk section'
function obj._protectionObj:isTemporary()
 
return false
 
end
 
 
self:assertEquals(
 
self:assertEquals(
'the key is tooltip-fragment-noexpiry',
+
'[['
obj:_makeTooltipFragmentParameter()
+
.. d.talkPage .. '#talk section|'
 +
.. 'the key is talk-page-link-display'
 +
.. ']]',
 +
obj:_makeTalkPageParameter()
 
)
 
)
 
end
 
end
  
-- _makeVandalTemplateParameter
+
-- _makeTooltipBlurbParameter
  
function suite:testBlurbMakeVandalTemplateParameter()
+
function suite:testBlurbMakeTooltipBlurbParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
self:assertStringContains(
+
function obj._protectionObj:isTemporary()
d.baseText,
+
return true
obj:_makeVandalTemplateParameter(),
+
end
true
+
function obj:_getExpandedMessage(key)
 +
return 'the key is ' .. key
 +
end
 +
self:assertEquals(
 +
'the key is tooltip-blurb-expiry',
 +
obj:_makeTooltipBlurbParameter()
 
)
 
)
obj._args.user = 'Some user'
+
 
self:assertStringContains(
+
function obj._protectionObj:isTemporary()
'Some user',
+
return false
obj:_makeVandalTemplateParameter(),
+
end
true
+
self:assertEquals(
 +
'the key is tooltip-blurb-noexpiry',
 +
obj:_makeTooltipBlurbParameter()
 
)
 
)
 
end
 
end
  
-- makeBannerText
+
-- _makeTooltipFragmentParameter
  
function suite:testBlurbMakeBannerTextBadInput()
+
function suite:testBlurbMakeTooltipFragmentParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
self:assertError(
+
function obj._protectionObj:isTemporary()
obj.makeBannerText,
+
return true
{obj, 'foo'},
+
end
'"foo" is not a valid banner config field'
+
function obj:_getExpandedMessage(key)
 +
return 'the key is ' .. key
 +
end
 +
self:assertEquals(
 +
'the key is tooltip-fragment-expiry',
 +
obj:_makeTooltipFragmentParameter()
 
)
 
)
self:assertError(
+
 
obj.makeBannerText,
+
function obj._protectionObj:isTemporary()
{obj, nil},
+
return false
'"nil" is not a valid banner config field'
+
end
 +
self:assertEquals(
 +
'the key is tooltip-fragment-noexpiry',
 +
obj:_makeTooltipFragmentParameter()
 
)
 
)
 
end
 
end
  
function suite:testBlurbMakeBannerTextGoodInput()
+
-- _makeVandalTemplateParameter
 +
 
 +
function suite:testBlurbMakeVandalTemplateParameter()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
obj._protectionObj.bannerConfig = {
+
self:assertStringContains(
text = 'banner text',
+
d.baseText,
explanation = 'banner explanation',
+
obj:_makeVandalTemplateParameter(),
tooltip = 'banner tooltip',
+
true
alt = 'banner alt',
+
)
link = 'banner link'
+
obj._args.user = 'Some user'
}
+
self:assertStringContains(
self:assertNotError(obj.makeBannerText, {obj, 'text'})
+
'Some user',
self:assertNotError(obj.makeBannerText, {obj, 'explanation'})
+
obj:_makeVandalTemplateParameter(),
self:assertNotError(obj.makeBannerText, {obj, 'tooltip'})
+
true
self:assertNotError(obj.makeBannerText, {obj, 'alt'})
+
)
self:assertNotError(obj.makeBannerText, {obj, 'link'})
 
 
end
 
end
  
function suite:testBlurbMakeBannerTextString()
+
-- makeBannerText
local obj = makeDefaultBlurbObject()
 
function obj:_substituteParameters(msg)
 
return msg
 
end
 
obj._protectionObj.bannerConfig = {
 
text = 'banner text',
 
}
 
self:assertEquals('banner text', obj:makeBannerText('text'))
 
end
 
  
function suite:testBlurbMakeBannerTextBadFunction()
+
function suite:testBlurbMakeBannerTextBadInput()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
function obj:_substituteParameters(msg)
 
return msg
 
end
 
obj._protectionObj.bannerConfig = {
 
text = function () return 9 end,
 
}
 
 
self:assertError(
 
self:assertError(
 
obj.makeBannerText,
 
obj.makeBannerText,
{obj, 'text'},
+
{obj, 'foo'},
'bad output from banner config function with key "text"'
+
'"foo" is not a valid banner config field'
.. ' (expected string, got number)'
+
)
 +
self:assertError(
 +
obj.makeBannerText,
 +
{obj, nil},
 +
'"nil" is not a valid banner config field'
 
)
 
)
 
end
 
end
  
function suite:testBlurbMakeBannerTextGoodFunction()
+
function suite:testBlurbMakeBannerTextGoodInput()
 
local obj = makeDefaultBlurbObject()
 
local obj = makeDefaultBlurbObject()
function obj:_substituteParameters(msg)
 
return msg
 
end
 
 
obj._protectionObj.bannerConfig = {
 
obj._protectionObj.bannerConfig = {
text = function () return 'some text' end,
+
text = 'banner text',
 +
explanation = 'banner explanation',
 +
tooltip = 'banner tooltip',
 +
alt = 'banner alt',
 +
link = 'banner link'
 
}
 
}
self:assertEquals('some text', obj:makeBannerText('text'))
+
self:assertNotError(obj.makeBannerText, {obj, 'text'})
 +
self:assertNotError(obj.makeBannerText, {obj, 'explanation'})
 +
self:assertNotError(obj.makeBannerText, {obj, 'tooltip'})
 +
self:assertNotError(obj.makeBannerText, {obj, 'alt'})
 +
self:assertNotError(obj.makeBannerText, {obj, 'link'})
 
end
 
end
  
--------------------------------------------------------------------------------
+
function suite:testBlurbMakeBannerTextString()
-- BannerTemplate class tests
+
local obj = makeDefaultBlurbObject()
--------------------------------------------------------------------------------
+
function obj:_substituteParameters(msg)
 
+
return msg
-- BannerTemplate.new
+
end
 
+
obj._protectionObj.bannerConfig = {
function suite:testBannerTemplateNewCfg()
+
text = 'banner text',
local protectionObj = makeDefaultProtectionObject()
+
}
local obj = BannerTemplate.new(protectionObj, makeConfig{foo = 'bar'})
+
self:assertEquals('banner text', obj:makeBannerText('text'))
self:assertEquals('bar', obj._cfg.foo)
 
 
end
 
end
  
function suite:testBannerTemplateNewImageIndefTemplateOrModule()
+
function suite:testBlurbMakeBannerTextBadFunction()
local cfg = {
+
local obj = makeDefaultBlurbObject()
msg = {['image-filename-indef'] = 'red padlock'}
+
function obj:_substituteParameters(msg)
 +
return msg
 +
end
 +
obj._protectionObj.bannerConfig = {
 +
text = function () return 9 end,
 
}
 
}
local protectionObj = makeDefaultProtectionObject()
+
self:assertError(
protectionObj.action = 'edit'
+
obj.makeBannerText,
protectionObj.level = 'sysop'
+
{obj, 'text'},
function protectionObj:isTemporary() return false end
+
'bad output from banner config function with key "text"'
 +
.. ' (expected string, got number)'
 +
)
 +
end
  
rawset(protectionObj.title, 'namespace', 10)
+
function suite:testBlurbMakeBannerTextGoodFunction()
local obj1 = BannerTemplate.new(protectionObj, makeConfig(cfg))
+
local obj = makeDefaultBlurbObject()
self:assertEquals('red padlock', obj1._imageFilename)
+
function obj:_substituteParameters(msg)
 
+
return msg
rawset(protectionObj.title, 'namespace', 828)
+
end
local obj2 = BannerTemplate.new(protectionObj, makeConfig(cfg))
+
obj._protectionObj.bannerConfig = {
self:assertEquals('red padlock', obj2._imageFilename)
+
text = function () return 'some text' end,
 +
}
 +
self:assertEquals('some text', obj:makeBannerText('text'))
 
end
 
end
  
function suite:testBannerTemplateNewImageUsesIndefReason()
+
--------------------------------------------------------------------------------
local cfg = {
+
-- BannerTemplate class tests
indefImageReasons = {[d.reason] = true},
+
--------------------------------------------------------------------------------
msg = {['image-filename-indef'] = 'red padlock'}
+
 
 +
-- BannerTemplate.new
 +
 
 +
function suite:testBannerTemplateNewCfg()
 +
local protectionObj = makeDefaultProtectionObject()
 +
local obj = BannerTemplate.new(protectionObj, makeConfig{foo = 'bar'})
 +
self:assertEquals('bar', obj._cfg.foo)
 +
end
 +
 
 +
function suite:testBannerTemplateNewImageIndefTemplateOrModule()
 +
local cfg = {
 +
msg = {['image-filename-indef'] = 'red padlock'}
 
}
 
}
 
local protectionObj = makeDefaultProtectionObject()
 
local protectionObj = makeDefaultProtectionObject()
Line 1,822: Line 1,914:
 
protectionObj.level = 'sysop'
 
protectionObj.level = 'sysop'
 
function protectionObj:isTemporary() return false end
 
function protectionObj:isTemporary() return false end
rawset(protectionObj.title, 'namespace', 2)
 
local obj = BannerTemplate.new(protectionObj, makeConfig(cfg))
 
self:assertEquals('red padlock', obj._imageFilename)
 
end
 
  
function suite:testBannerTemplateNewImageDefault()
+
rawset(protectionObj.title, 'namespace', 10)
local images = {
+
local obj1 = BannerTemplate.new(protectionObj, makeConfig(cfg))
move = {
+
self:assertEquals('red padlock', obj1._imageFilename)
sysop = 'foo',
+
 
default = 'bar'
+
rawset(protectionObj.title, 'namespace', 828)
}
+
local obj2 = BannerTemplate.new(protectionObj, makeConfig(cfg))
 +
self:assertEquals('red padlock', obj2._imageFilename)
 +
end
 +
 
 +
function suite:testBannerTemplateNewImageUsesIndefReason()
 +
local cfg = {
 +
indefImageReasons = {[d.reason] = true},
 +
msg = {['image-filename-indef'] = 'red padlock'}
 +
}
 +
local protectionObj = makeDefaultProtectionObject()
 +
protectionObj.action = 'edit'
 +
protectionObj.level = 'sysop'
 +
function protectionObj:isTemporary() return false end
 +
rawset(protectionObj.title, 'namespace', 2)
 +
local obj = BannerTemplate.new(protectionObj, makeConfig(cfg))
 +
self:assertEquals('red padlock', obj._imageFilename)
 +
end
 +
 
 +
function suite:testBannerTemplateNewImageDefault()
 +
local images = {
 +
move = {
 +
sysop = 'foo',
 +
default = 'bar'
 +
}
 
}
 
}
 
local protectionObj = makeDefaultProtectionObject()
 
local protectionObj = makeDefaultProtectionObject()
Line 2,023: Line 2,134:
  
 
function suite:test_mainError()
 
function suite:test_mainError()
local args = {expiry = 'foo', action = 'autoreview' }
+
local args = {action = 'foobar'}
 
local cfg = makeConfig()
 
local cfg = makeConfig()
local title
+
local title = makeDefaultTitleObject()
 
local success, result = pcall(mProtectionBanner._main, args, cfg, title)
 
local success, result = pcall(mProtectionBanner._main, args, cfg, title)
 
self:assertFalse(success)
 
self:assertFalse(success)
 
self:assertEquals(
 
self:assertEquals(
'invalid expiry date: foo',
+
'invalid action: foobar',
 
result,
 
result,
 
false
 
false
Line 2,056: Line 2,167:
 
end
 
end
  
function suite:test_mainLarge1()
+
function suite:test_mainLarge1()
local args = {}
+
local args = {}
local cfg = makeConfig()
+
local cfg = makeConfig()
local title = makeDefaultTitleObject()
+
local title = makeDefaultTitleObject()
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
+
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end
+
end
 
+
 
function suite:test_mainLarge2()
+
function suite:test_mainLarge2()
local args = {small = 'no'}
+
local args = {small = 'no'}
local cfg = makeConfig()
+
local cfg = makeConfig()
local title = makeDefaultTitleObject()
+
local title = makeDefaultTitleObject()
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
+
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end
+
end
 
+
 
function suite:test_mainLarge3()
+
function suite:test_mainLarge3()
local args = {small = 'No'}
+
local args = {small = 'No'}
local cfg = makeConfig()
+
local cfg = makeConfig()
local title = makeDefaultTitleObject()
+
local title = makeDefaultTitleObject()
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
+
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end
+
end
 
+
 
function suite:test_mainLarge4()
+
function suite:test_mainLarge4()
local args = {small = 'false'}
+
local args = {small = 'false'}
local cfg = makeConfig()
+
local cfg = makeConfig()
local title = makeDefaultTitleObject()
+
local title = makeDefaultTitleObject()
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
+
self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end
+
end
 
+
 
function suite:test_mainNoBanner()
+
function suite:test_mainNoBanner()
 +
local args = {}
 +
local cfg = makeConfig()
 +
local title = makeTitleObject{page = d.page, edit = nil}
 +
self:assertNoBanner(mProtectionBanner._main(args, cfg, title), 'page unprotected')
 +
end
 +
 
 +
function suite:test_mainNoBannerForUserScripts()
 
local args = {}
 
local args = {}
 
local cfg = makeConfig()
 
local cfg = makeConfig()
local title = makeTitleObject(d.page, 'edit', nil)
+
local title = makeTitleObject{page = 'User:Example/common.css', contentType = 'css'}
self:assertNoBanner(mProtectionBanner._main(args, cfg, title), 'page unprotected')
+
self:assertNoBanner(mProtectionBanner._main(args, cfg, title), 'page is a user script')
 
end
 
end
  
Line 2,094: Line 2,212:
 
local args = {}
 
local args = {}
 
local cfg -- Use main config module
 
local cfg -- Use main config module
local title = makeTitleObject(d.page, 'edit', nil)
+
local title = makeTitleObject{page = d.page, 'edit', nil}
 
self:assertStringContains(
 
self:assertStringContains(
 
'%[%[Category:.-%]%]',
 
'%[%[Category:.-%]%]',

Latest revision as of 08:29, 27 September 2020

Documentation for this module may be created at Module:Protection banner/testcases/doc

-- Load necessary modules
local mProtectionBanner = require('Module:Protection banner/sandbox')
local ScribuntoUnit = require('Module:ScribuntoUnit')

-- Get the classes
local Protection = mProtectionBanner._exportClasses().Protection
local Blurb = mProtectionBanner._exportClasses().Blurb
local BannerTemplate = mProtectionBanner._exportClasses().BannerTemplate
local Banner = mProtectionBanner._exportClasses().Banner
local Padlock = mProtectionBanner._exportClasses().Padlock

-- Initialise test suite
local suite = ScribuntoUnit:new()

--------------------------------------------------------------------------------
-- Default values
--------------------------------------------------------------------------------

local d = {}
d.reason = 'vandalism'
d.action = 'edit'
d.level = 'sysop'
d.page = 'User:Example'
d.talkPage = 'User talk:Example'
d.baseText = 'Example'
d.namespace = 2 -- Namespace of d.page
d.namespaceFragment = 'user' -- namespace fragment of d.page for makeProtectionCategory
d.categoryNamespaceKeys = {[d.namespace] = d.namespaceFragment} -- namespace key config table
d.expiry = '1 January 9999'
d.expiryU = 253370764800 -- d.expiry in Unix time
d.expiryFragment = 'temp' -- expiry fragment of d.expiry for makeProtectionCategory
d.protectionDate = '1 January 2000'
d.protectionDateU = 946684800 -- d.protectionDate in Unix time

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------

function suite:assertError(func, args, msg)
	args = args or {}
	local success, result = pcall(func, unpack(args))
	self:assertFalse(success)
	if msg then
		self:assertStringContains(msg, result, true) -- does a plain match
	end
end

function suite:assertNotError(func, args)
	args = args or {}
	local success, result = pcall(func, unpack(args))
	self:assertTrue(success)
end

local function makeTitleObject(options)
	local title = mw.title.new(options.page)

	-- Set protection levels
	local levels = {
		edit = {},
		move = {},
		autoreview = {}
	}
	for _, action in ipairs{'edit', 'move', 'autoreview'} do
		local level = options[action]
		if level then
			levels[action][1] = level
		end
	end
	rawset(title, 'protectionLevels', levels)

	-- Set content model
	rawset(title, 'contentModel', options.contentModel or 'wikitext')

	return title
end

local function makeDefaultTitleObject()
	return makeTitleObject{page = d.page, [d.action] = d.level}
end

-- Make an alias, to be clear the object is for a protected page.
local makeProtectedTitleObject = makeDefaultTitleObject

local function makeConfig(t)
	local cfg = {
		masterBanner = {},
		padlockIndicatorNames = {},
		indefImageReasons = {},
		reasonsWithNamespacePriority = {},
		categoryNamespaceKeys = {},
		protectionCategories = {},
		reasonsWithoutExpiryCheck = {},
		expiryCheckActions = {},
		pagetypes = {},
		indefStrings = {},
		hierarchy = {
			sysop = {},
			reviewer = {'sysop'},
			filemover = {'sysop'},
			templateeditor = {'sysop'},
			extendedconfirmed = {'sysop'},
			autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'},
			user = {'autoconfirmed'},
			['*'] = {'user'}
		},
		wrappers = {},
		msg = {}
	}
	local protLevelFields = {
		'defaultBanners',
		'banners',
		'protectionBlurbs',
		'explanationBlurbs',
		'protectionLevels',
		'images',
		'imageLinks'
	}
	for _, field in ipairs(protLevelFields) do
		cfg[field] = {
			edit = {},
			move = {},
			autoreview = {}
		}
	end

	-- Add fields that cause errors if not present
	cfg.masterBanner.text = 'reason text'

	-- Add custom fields
	for k, v in pairs(t or {}) do
		cfg[k] = v
	end
	return cfg
end

local function makeDefaultProtectionObject(cfg)
	cfg = makeConfig(cfg)
	if next(cfg.categoryNamespaceKeys) == nil then -- cfg.categoryNamespaceKeys is empty
		cfg.categoryNamespaceKeys = d.categoryNamespaceKeys
	end
	local obj = Protection.new(
		{d.reason, action = d.action, expiry = d.expiry},
		cfg,
		makeDefaultTitleObject()
	)
	obj.expiry = d.expiryU -- Hack to override [[Module:Effective protection expiry]]
	return obj
end

local function makeProtectionCategoryKey(testFragments, defaults)
	local fragments = {
		expiry = 'all',
		namespace = 'all',
		reason = 'all',
		level = 'all',
		action = 'all'
	}
	for i, t in ipairs{defaults or {}, testFragments} do
		for k, v in pairs(t) do
			fragments[k] = v
		end
	end
	local key = {
		fragments.expiry,
		fragments.namespace,
		fragments.reason,
		fragments.level,
		fragments.action
	}
	return table.concat(key, '|')
end

local function makeDefaultProtectionCategoryKey(testFragments)
	local defaults = {
		expiry = d.expiryFragment,
		namespace = d.namespaceFragment,
		reason = d.reason,
		level = d.level,
		action = d.action
	}
	return makeProtectionCategoryKey(testFragments, defaults)
end

local function makeDefaultBlurbObject(cfg)
	cfg = makeConfig(cfg)
	return Blurb.new(
		makeDefaultProtectionObject(),
		{},
		cfg
	)
end

local function makeDefaultBannerTemplateObject(cfg)
	cfg = makeConfig(cfg)
	return BannerTemplate.new(
		makeDefaultProtectionObject(),
		cfg
	)
end

local function makeDefaultBannerObject(cfg)
	cfg = makeConfig(cfg)
	return Banner.new(
		makeDefaultProtectionObject(),
		makeDefaultBlurbObject(),
		cfg
	)
end

local function makeDefaultPadlockObject(cfg)
	cfg = makeConfig(cfg)
	return Padlock.new(
		makeDefaultProtectionObject(),
		makeDefaultBlurbObject(),
		cfg
	)
end

function suite:assertIsPadlock(s, msg)
	self:assertStringContains(
		'\127[^\127]*UNIQ%-%-indicator%-%x+%-QINU[^\127]*\127',
		s,
		false,
		msg
	)
end

function suite:assertIsBanner(s, msg)
	self:assertStringContains(
		'class="[^"]*mbox[^"]*"',
		s,
		false,
		msg
	)
	self:assertStringContains(
		'role="presentation"',
		s,
		true,
		msg
	)
	self:assertNotStringContains(
		'id="protected-icon"',
		s,
		true,
		msg
	)
	self:assertNotStringContains(
		'topicon',
		s,
		true,
		msg
	)
end

function suite:assertNoBanner(s, msg)
	self:assertNotStringContains(
		'class="[^"]*mbox[^"]*"',
		s,
		false,
		msg
	)
	self:assertNotStringContains(
		'role="presentation"',
		s,
		true,
		msg
	)
	self:assertNotStringContains(
		'id="protected-icon"',
		s,
		true,
		msg
	)
	self:assertNotStringContains(
		'topicon',
		s,
		true,
		msg
	)
end

--------------------------------------------------------------------------------
-- Protection object tests
--------------------------------------------------------------------------------

-- Protection action

function suite:testProtectionActionError()
	suite:assertError(
		Protection.new,
		{{action = 'foo'}, makeConfig()},
		'invalid action: foo'
	)
end

function suite:testProtectionActionEdit()
	local obj = Protection.new({action = 'edit'}, makeConfig())
	suite:assertEquals('edit', obj.action)
end

function suite:testProtectionActionMove()
	local obj = Protection.new({action = 'move'}, makeConfig())
	suite:assertEquals('move', obj.action)
end

function suite:testProtectionActionAutoreview()
	local obj = Protection.new({action = 'autoreview'}, makeConfig())
	suite:assertEquals('autoreview', obj.action)
end

function suite:testProtectionActionUpload()
	local obj = Protection.new({action = 'upload'}, makeConfig())
	suite:assertEquals('upload', obj.action)
end

function suite:testProtectionNoAction()
	local obj = Protection.new({}, makeConfig())
	suite:assertEquals('edit', obj.action)
end

-- Protection level

function suite:testProtectionSemi()
	local obj = Protection.new(
		{action = 'edit'},
		makeConfig(),
		makeTitleObject{page = 'Foo', edit = 'autoconfirmed'}
	)
	self:assertEquals('autoconfirmed', obj.level)
end

function suite:testProtectionFull()
	local obj = Protection.new(
		{action = 'edit'},
		makeConfig(),
		makeTitleObject{page = 'Foo', edit = 'sysop'}
	)
	self:assertEquals('sysop', obj.level)
end

function suite:testProtectionUnprotected()
	local obj = Protection.new(
		{action = 'edit'},
		makeConfig(),
		makeTitleObject{page = 'Foo', edit = nil}
	)
	self:assertEquals('*', obj.level)
end

function suite:testProtectionSemiMove()
	local obj = Protection.new(
		{action = 'move'},
		makeConfig(),
		makeTitleObject{page = 'Foo', move = 'autoconfirmed'}
	)
	self:assertEquals('*', obj.level)
end

function suite:testProtectionTemplate()
	local obj = Protection.new(
		{action = 'edit'},
		makeConfig(),
		makeTitleObject{page = 'Template:Foo', edit = 'templateeditor'}
	)
	self:assertEquals('templateeditor', obj.level)
end

function suite:testProtectionTitleBlacklist()
	local obj = Protection.new(
		{action = 'edit'},
		makeConfig(),
		makeTitleObject{page = 'Template:Editnotices/Page/Foo', edit = nil}
	)
	self:assertEquals('templateeditor', obj.level)
end

-- Reason

function suite:testProtectionReason()
	local obj = Protection.new(
		{'foo'},
		makeConfig()
	)
	self:assertEquals('foo', obj.reason)
end

function suite:testProtectionReasonLowerCase()
	local obj = Protection.new(
		{'fOO'},
		makeConfig()
	)
	self:assertEquals('foo', obj.reason)
end

function suite:testProtectionBadReason()
	self:assertError(
		Protection.new,
		{{'foo|bar'}, makeConfig()},
		'reasons cannot contain the pipe character ("|")'
	)
end

function suite:testProtectionNoReason()
	local obj = Protection.new(
		{},
		makeConfig()
	)
	self:assertEquals(nil, obj.reason)
end

-- Protection date

function suite:testProtectionProtectionDateIndef()
	self:assertError(
		Protection.new,
		{
			{date = 'indefinite'},
			makeConfig{indefStrings = {indefinite = true}}
		},
		'invalid protection date: indefinite'
	)
end

function suite:testProtectionProtectionDateTemp()
	local obj = Protection.new(
		{date = d.protectionDate},
		makeConfig()
	)
	self:assertEquals(d.protectionDateU, obj.protectionDate)
end

function suite:testProtectionNoProtectionDate()
	local obj = Protection.new(
		{},
		makeConfig()
	)
	self:assertEquals(nil, obj.protectionDate)
end

function suite:testProtectionBadProtectionDate()
	self:assertError(
		Protection.new,
		{
			{date = 'foobar'},
			makeConfig{indefStrings = {indefinite = true}}
		},
		'invalid protection date: foobar'
	)
end

-- bannerConfig

function suite:testProtectionMasterBannerConfigPrecedence1()
	local masterBanner = {text = 'master banner text'}
	local obj = makeDefaultProtectionObject{masterBanner = masterBanner}
	self:assertEquals('master banner text', obj.bannerConfig.text)
end

function suite:testProtectionMasterBannerConfigPrecedence2()
	local masterBanner = {text = 'master banner text'}
	local defaultBanners = {[d.action] = {default = {text = 'defaultBanners default text'}}}
	local obj = makeDefaultProtectionObject(makeConfig{
		masterBanner = masterBanner,
		defaultBanners = defaultBanners
	})
	self:assertEquals('defaultBanners default text', obj.bannerConfig.text)
end

function suite:testProtectionMasterBannerConfigPrecedence3()
	local defaultBanners = {[d.action] = {
		[d.level] = {text = 'defaultBanners level text'},
		default = {text = 'defaultBanners default text'}
	}}
	local obj = makeDefaultProtectionObject(makeConfig{defaultBanners = defaultBanners})
	self:assertEquals('defaultBanners level text', obj.bannerConfig.text)
end

function suite:testProtectionMasterBannerConfigPrecedence4()
	local defaultBanners = {[d.action] = {
		[d.level] = {text = 'defaultBanners level text'}
	}}
	local banners = {[d.action] ={
		[d.reason] = {text = 'banners text'}
	}}
	local obj = makeDefaultProtectionObject(makeConfig{
		defaultBanners = defaultBanners,
		banners = banners
	})
	self:assertEquals('banners text', obj.bannerConfig.text)
end

function suite:testProtectionMasterBannerConfigFields()
	local masterBanner = {
		text = 'master banner text',
		explanation = 'master banner explanation',
		tooltip = 'master banner tooltip',
		alt = 'master banner alt',
		link = 'master banner link',
		image = 'master banner image'
	}
	local obj = makeDefaultProtectionObject{masterBanner = masterBanner}
	self:assertEquals('master banner text', obj.bannerConfig.text)
	self:assertEquals('master banner explanation', obj.bannerConfig.explanation)
	self:assertEquals('master banner tooltip', obj.bannerConfig.tooltip)
	self:assertEquals('master banner alt', obj.bannerConfig.alt)
	self:assertEquals('master banner link', obj.bannerConfig.link)
	self:assertEquals('master banner image', obj.bannerConfig.image)
end

-- isUserScript

function suite:testProtectionIsUserScriptWithUserCSS()
	local protection = Protection.new(
		{},
		makeConfig(),
		mw.title.new('User:Example/common.css')
	)
	self:assertTrue(protection:isUserScript())
end

function suite:testProtectionIsUserScriptWithUserJS()
	local protection = Protection.new(
		{},
		makeConfig(),
		mw.title.new('User:Example/common.js')
	)
	self:assertTrue(protection:isUserScript())
end

function suite:testProtectionIsUserScriptWithUserPage()
	local protection = Protection.new(
		{},
		makeConfig(),
		mw.title.new('User:Example')
	)
	self:assertFalse(protection:isUserScript())
end

function suite:testProtectionIsUserScriptWithNormalSubpage()
	local protection = Protection.new(
		{},
		makeConfig(),
		mw.title.new('User:Example/common')
	)
	self:assertFalse(protection:isUserScript())
end

function suite:testProtectionIsUserScriptWithUsernameEndingInCSS()
	local protection = Protection.new(
		{},
		makeConfig(),
		mw.title.new('User:My username ends in .css')
	)
	self:assertFalse(protection:isUserScript())
end


function suite:testProtectionIsUserScriptWithUsernameEndingInJS()
	self:assertFalse(
		Protection.new(
			{},
			makeConfig(),
			mw.title.new('User:My username ends in .js')
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithSubpageContainingCSS()
	self:assertFalse(
		Protection.new(
			{},
			makeConfig(),
			mw.title.new('User:Example/common.css.txt')
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithNoSubpagePrefix()
	self:assertTrue(
		Protection.new(
			{},
			makeConfig(),
			mw.title.new('User:Example/.css')
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithBlankUsernameAndNoSubpagePrefix()
	self:assertTrue(
		Protection.new(
			{},
			makeConfig(),
			mw.title.new('User:/.css')
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWhenUsernameIsOnlyCSS()
	self:assertFalse(
		Protection.new(
			{},
			makeConfig(),
			mw.title.new('User:.css')
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithNonstandardCSSContentModel()
	self:assertTrue(
		Protection.new(
			{},
			makeConfig(),
			makeTitleObject{page='User:Example/foo', contentModel='css'}
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithNonstandardJSContentModel()
	self:assertTrue(
		Protection.new(
			{},
			makeConfig(),
			makeTitleObject{page='User:Example/foo', contentModel='javascript'}
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithNonstandardWikitextContentModel()
	self:assertFalse(
		Protection.new(
			{},
			makeConfig(),
			makeTitleObject{page='User:Example/foo.css', contentModel='wikitext'}
		):isUserScript()
	)
end

function suite:testProtectionIsUserScriptWithNonUserspacePage()
	self:assertFalse(
		Protection.new(
			{},
			makeConfig(),
			makeTitleObject{page='Wikipedia:Example.css', contentModel='css'}
		):isUserScript()
	)
end

-- isProtected

function suite:testProtectionIsProtectedTrue()
	local obj = makeDefaultProtectionObject()
	obj.level = 'autoconfirmed'
	self:assertTrue(obj:isProtected())
end

function suite:testProtectionIsProtectedFalse()
	local obj = makeDefaultProtectionObject()
	obj.level = '*'
	self:assertFalse(obj:isProtected())
end

-- shouldShowLock

function suite:testProtectionShouldShowLockWithProtectedPage()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='Foo', edit='sysop'}
	)
	self:assertTrue(obj:shouldShowLock())
end

function suite:testProtectionShouldShowLockWithProtectedUserScript()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='User:Example/common.css', contentModel='css', edit='sysop'}
	)
	self:assertFalse(obj:shouldShowLock())
end

function suite:testProtectionShouldShowLockWithUnprotectedPage()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='Foo'}
	)
	self:assertFalse(obj:shouldShowLock())
end

-- shouldHaveProtectionCategory

function suite:testProtectionShouldHaveProtectionCategoryWithProtectedPage()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='Foo', edit='sysop'}
	)
	self:assertTrue(obj:shouldHaveProtectionCategory())
end

function suite:testProtectionShouldHaveProtectionCategoryWithProtectedUserScript()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='User:Example/common.css', contentModel='css', edit='sysop'}
	)
	self:assertFalse(obj:shouldHaveProtectionCategory())
end

function suite:testProtectionShouldHaveProtectionCategoryWithUnprotectedPage()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='Foo'}
	)
	self:assertFalse(obj:shouldHaveProtectionCategory())
end

-- isTemporary

function suite:testProtectionIsProtectedTrue()
	local obj = makeDefaultProtectionObject()
	obj.expiry = 123456789012
	self:assertTrue(obj:isTemporary())
end

function suite:testProtectionIsProtectedFalse1()
	local obj = makeDefaultProtectionObject()
	obj.expiry = 'indef'
	self:assertFalse(obj:isTemporary())
end

function suite:testProtectionIsProtectedFalse2()
	local obj = makeDefaultProtectionObject()
	obj.expiry = nil
	self:assertFalse(obj:isTemporary())
end

-- makeProtectionCategory
--
function suite:testProtectionCategoryWithUnprotectedPage()
	local obj = Protection.new(
		{},
		makeConfig(),
		makeTitleObject{page='Foo'}
	)
	self:assertEquals(obj:makeProtectionCategory(), '')
end

function suite:testProtectionCategoryPrecedence1()
	-- Test that expiry has the lowest priority.
	local protectionCategories = {
		[makeDefaultProtectionCategoryKey{expiry = 'all'}] = 'all expiries allowed',
		[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
		[makeDefaultProtectionCategoryKey{reason = 'all'}] = 'all reasons allowed',
		[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
		[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed'
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'all expiries allowed',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence2()
	-- Test that reason has the highest priority.
	local protectionCategories = {
		[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
		[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
		[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
		[makeProtectionCategoryKey{level = d.level}] = 'level only',
		[makeProtectionCategoryKey{action = d.action}] = 'action only'
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'reason only',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence3()
	-- Test that namespace has the highest priority if the reason is set in
	-- cfg.reasonsWithNamespacePriority.
	local protectionCategories = {
		[makeProtectionCategoryKey{expiry = d.expiryFragment}] = 'expiry only',
		[makeProtectionCategoryKey{namespace = d.namespaceFragment}] = 'namespace only',
		[makeProtectionCategoryKey{reason = d.reason}] = 'reason only',
		[makeProtectionCategoryKey{level = d.level}] = 'level only',
		[makeProtectionCategoryKey{action = d.action}] = 'action only'
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
		reasonsWithNamespacePriority = {[d.reason] = true}
	}
	self:assertStringContains(
		'namespace only',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence4()
	-- Test that level has a higher priority than namespace.
	local protectionCategories = {
		[makeDefaultProtectionCategoryKey{namespace = 'all'}] = 'all namespaces allowed',
		[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'all namespaces allowed',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence5()
	-- Test that action has a higher priority than level.
	local protectionCategories = {
		[makeDefaultProtectionCategoryKey{level = 'all'}] = 'all levels allowed',
		[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'all levels allowed',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence6()
	-- Test that an exact match will be first.
	local protectionCategories = {
		[makeDefaultProtectionCategoryKey()] = 'exact match',
		[makeDefaultProtectionCategoryKey{action = 'all'}] = 'all actions allowed',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'exact match',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence7()
	-- Test that matching 4 keys will come before matching 3 keys
	local protectionCategories = {
		[makeDefaultProtectionCategoryKey{action = 'all'}] = 'four keys',
		[makeDefaultProtectionCategoryKey{action = 'all', level = 'all'}] = 'three keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'four keys',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence8()
	-- Test that matching 3 keys will come before matching 2 keys
	local protectionCategories = {
		[makeProtectionCategoryKey{reason = d.reason, action = d.action, level = d.level}] = 'three keys',
		[makeProtectionCategoryKey{reason = d.reason, action = d.action}] = 'two keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'three keys',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence9()
	-- Test that matching 2 keys will come before matching 1 key
	local protectionCategories = {
		[makeProtectionCategoryKey{action = d.action, level = d.level}] = 'two keys',
		[makeProtectionCategoryKey{action = d.action}] = 'one key',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'two keys',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryPrecedence10()
	-- Test that matching 1 keys will come before matching 0 keys
	local protectionCategories = {
		[makeProtectionCategoryKey{action = d.action}] = 'one key',
		[makeProtectionCategoryKey()] = 'no keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'one key',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryAllAlls()
	-- Test that 'all|all|all|all|all' works
	local protectionCategories = {
		[makeProtectionCategoryKey()] = 'no keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'no keys',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryNoFalseMatches()
	-- Test that we don't match things that we aren't supposed to.
	local protectionCategories = {
		[makeProtectionCategoryKey{expiry = 'foo'}]    = 'expiry foo',
		[makeProtectionCategoryKey{namespace = 'foo'}] = 'namespace foo',
		[makeProtectionCategoryKey{reason = 'foo'}]    = 'reason foo',
		[makeProtectionCategoryKey{level = 'foo'}]     = 'level foo',
		[makeProtectionCategoryKey{action = 'foo'}]    = 'action foo',
		[makeProtectionCategoryKey()]                  = 'no keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'no keys',
		obj:makeProtectionCategory(),
		true
	)
end

function suite:testProtectionCategoryProtected()
	-- Test that protected pages produce some kind of category link.
	local protectionCategories = {
		[makeProtectionCategoryKey()] = 'no keys',
	}
	local obj = makeDefaultProtectionObject{
		protectionCategories = protectionCategories,
	}
	self:assertStringContains(
		'^%[%[Category:.*%]%]$',
		obj:makeProtectionCategory()
	)
end

function suite:testProtectionCategoryNotProtected()
	-- Test that unprotected pages produce the blank string.
	local protectionCategories = {
		[makeProtectionCategoryKey()] = 'no keys',
	}
	local obj = Protection.new(
		{},
		makeConfig{protectionCategories = protectionCategories},
		makeTitleObject{page = d.page, 'edit', nil}
	)
	self:assertEquals('', obj:makeProtectionCategory())
end

function suite:testProtectionCategoryNoMatch()
	-- Test that protected pages that don't match any categories
	-- produce the blank string.
	local obj = makeDefaultProtectionObject()
	self:assertEquals('', obj:makeProtectionCategory())
end

function suite:testProtectionCategoryExpiryIndef()
	-- Test that indefinite protection matches the "indef" expiry fragment.
	local obj = makeDefaultProtectionObject()
	obj._cfg.protectionCategories = {
		[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview'}] = 'indef expiry',
	}
	obj.action = 'autoreview'
	obj.level = 'autoconfirmed'
	obj.expiry = 'indef'
	self:assertStringContains('indef expiry', obj:makeProtectionCategory(), true)
end

function suite:testProtectionCategoryExpiryTemp()
	-- Test that temporary protection matches the "temp" expiry fragment.
	local obj = makeDefaultProtectionObject()
	obj._cfg.protectionCategories = {
		[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview'}] = 'temporary expiry',
	}
	obj.action = 'autoreview'
	obj.level = 'autoconfirmed'
	obj.expiry = d.expiryU
	self:assertStringContains('temporary expiry', obj:makeProtectionCategory(), true)
end

function suite:testProtectionCategoryNoExpiry()
	-- Test that pages with no expiry set don't match "indef" or "temp".
	local protectionCategories = {
		[makeProtectionCategoryKey{expiry = 'temp', action = 'autoreview' }] = 'temporary expiry',
		[makeProtectionCategoryKey{expiry = 'indef', action = 'autoreview' }] = 'indefinite expiry',
		[makeProtectionCategoryKey()] = 'no matches'
	}
	local obj = Protection.new(
		{},
		makeConfig{protectionCategories = protectionCategories},
		makeProtectedTitleObject()
	)
	self:assertStringContains('no matches', obj:makeProtectionCategory(), true)
end

function suite:testProtectionCategoryNamespaceFragment()
	-- Test that values in cfg.categoryNamespaceKeys will work.
	local protectionCategories = {
		[makeProtectionCategoryKey{namespace = 'foobar'}] = 'we found a match',
	}
	local obj = Protection.new(
		{},
		makeConfig{
			protectionCategories = protectionCategories,
			categoryNamespaceKeys = {[10] = 'foobar'} -- Template namespace
		},
		makeTitleObject{page = 'Template:Foo', edit = 'autoconfirmed'}
	)
	self:assertStringContains('we found a match', obj:makeProtectionCategory(), true)
end

function suite:testProtectionCategoryTalk()
	-- Test that talk pages match the "talk" namespace fragment.
	local protectionCategories = {
		[makeProtectionCategoryKey{namespace = 'talk'}] = 'talk namespace',
	}
	local obj = Protection.new(
		{},
		makeConfig{protectionCategories = protectionCategories},
		makeTitleObject{page = 'Template talk:Example', edit = 'autoconfirmed'}
	)
	self:assertStringContains('talk namespace', obj:makeProtectionCategory(), true)
end

-- isIncorrect

function suite:testProtectionIsIncorrectTrue1()
	local obj = makeDefaultProtectionObject()
	function obj:isProtected()
		return false
	end
	self:assertTrue(obj:isIncorrect(), 'Protection:isProtected() returned false')
end

function suite:testProtectionIsIncorrectTrue2()
	local obj = makeDefaultProtectionObject()
	function obj:isProtected()
		return true
	end
	obj.expiry = 0
	self:assertTrue(obj:isIncorrect(), 'the page is protected and expiry is in the past')
end

function suite:testProtectionIsIncorrectFalse1()
	local obj = makeDefaultProtectionObject()
	function obj:isProtected()
		return true
	end
	obj.expiry = d.expiryU
	self:assertFalse(obj:isIncorrect(), 'the page is protected and expiry is in the future')
end

function suite:testProtectionIsIncorrectFalse2()
	local obj = makeDefaultProtectionObject()
	obj.expiry = nil
	function obj:isProtected()
		return true
	end
	self:assertFalse(obj:isIncorrect(), 'the page is protected and no expiry is set')
end

-- isTemplateProtectedNonTemplate

function suite:testProtectionIsTemplateProtectedNonTemplateFalse1()
	local obj = makeDefaultProtectionObject()
	obj.level = 'autoconfirmed'
	self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'the page is semi-protected')
end

function suite:testProtectionIsTemplateProtectedNonTemplateFalse2()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templateeditor'
	obj.action = 'edit'
	rawset(obj.title, 'namespace', 10) -- template space
	self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected template')
end

function suite:testProtectionIsTemplateProtectedNonTemplateFalse3()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templateeditor'
	obj.action = 'edit'
	rawset(obj.title, 'namespace', 828) -- module space
	self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-protected module')
end

function suite:testProtectionIsTemplateProtectedNonTemplateFalse4()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templatemoveor'
	obj.action = 'move'
	rawset(obj.title, 'namespace', 10) -- template space
	self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected template')
end

function suite:testProtectionIsTemplateProtectedNonTemplateFalse5()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templatemoveor'
	obj.action = 'move'
	rawset(obj.title, 'namespace', 828) -- module space
	self:assertFalse(obj:isTemplateProtectedNonTemplate(), 'template-move-protected module')
end

function suite:testProtectionIsTemplateProtectedNonTemplateTrue1()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templateeditor'
	obj.action = 'autoreview'
	self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not edit or move')
end

function suite:testProtectionIsTemplateProtectedNonTemplateTrue2()
	local obj = makeDefaultProtectionObject()
	obj.level = 'templateeditor'
	rawset(obj.title, 'namespace', 2) -- user space
	self:assertTrue(obj:isTemplateProtectedNonTemplate(), 'the action is not in template or module space')
end

-- makeCategoryLinks

function suite:testProtectionMakeCategoryLinksAllPresent()
	local obj = makeDefaultProtectionObject()
	obj._cfg.msg = {
		['tracking-category-incorrect'] = 'Incorrect category',
		['tracking-category-template'] = 'Template category'
	}
	function obj:makeProtectionCategory()
		return '[[Category:Protection category|' .. d.baseText .. ']]'
	end
	for _, field in ipairs{'isIncorrect', 'isTemplateProtectedNonTemplate'} do
		obj[field] = function () return true end
	end
	self:assertEquals(
		'[[Category:Protection category|' .. d.baseText .. ']]' ..
		'[[Category:Incorrect category|' .. d.baseText .. ']]' ..
		'[[Category:Template category|' .. d.baseText .. ']]',
		obj:makeCategoryLinks()
	)
end

function suite:testProtectionMakeCategoryLinksAllAbsent()
	local obj = makeDefaultProtectionObject()
	obj._cfg.msg = {
		['tracking-category-expiry'] = 'Expiry category',
		['tracking-category-incorrect'] = 'Incorrect category',
		['tracking-category-template'] = 'Template category'
	}
	function obj:makeProtectionCategory()
		return ''
	end
	for _, field in ipairs{'needsExpiry', 'isIncorrect', 'isTemplateProtectedNonTemplate'} do
		obj[field] = function () return false end
	end
	self:assertEquals('', obj:makeCategoryLinks())
end

--------------------------------------------------------------------------------
-- Blurb class tests
--------------------------------------------------------------------------------

-- initialize

function suite:testBlurbNew()
	local obj = Blurb.new({'foo'}, {'bar'}, {'baz'})
	self:assertEquals('foo', obj._protectionObj[1])
	self:assertEquals('bar', obj._args[1])
	self:assertEquals('baz', obj._cfg[1])
end

-- _formatDate

function suite:testBlurbFormatDateStandard()
	local obj = makeDefaultBlurbObject()
	self:assertEquals('1 January 1970', obj:_formatDate(0))
end

function suite:testBlurbFormatDateCustom()
	local obj = makeDefaultBlurbObject{msg = {['expiry-date-format'] = 'Y Y F F j'}}
	self:assertEquals('1970 1970 January January 1', obj:_formatDate(0))
end

function suite:testBlurbFormatDateError()
	local obj = makeDefaultBlurbObject()
	self:assertEquals(nil, obj:_formatDate('foo'))
end

-- _getExpandedMessage

function suite:testBlurbGetExpandedMessage()
	local obj = makeDefaultBlurbObject()
	function obj:_substituteParameters(s)
		return 'testing ' .. s
	end
	obj._cfg.msg = {['test-key'] = 'test message'}
	self:assertEquals('testing test message', obj:_getExpandedMessage('test-key'))
end

-- _substituteParameters

function suite:testBlurbSubstituteParameters()
	local obj = makeDefaultBlurbObject()

	obj._makeCurrentVersionParameter = function () return '1' end
	obj._makeEditRequestParameter = function () return '2' end
	obj._makeExpiryParameter = function () return '3' end
	obj._makeExplanationBlurbParameter = function () return '4' end
	obj._makeImageLinkParameter = function () return '5' end
	obj._makeIntroBlurbParameter = function () return '6' end
	obj._makeIntroFragmentParameter = function () return '7' end
	obj._makePagetypeParameter = function () return '8' end
	obj._makeProtectionBlurbParameter = function () return '9' end
	obj._makeProtectionDateParameter = function () return '10' end
	obj._makeProtectionLevelParameter = function () return '11' end
	obj._makeProtectionLogParameter = function () return '12' end
	obj._makeTalkPageParameter = function () return '13' end
	obj._makeTooltipBlurbParameter = function () return '14' end
	obj._makeTooltipFragmentParameter = function () return '15' end
	obj._makeVandalTemplateParameter = function () return '16' end

	local msg = '${CURRENTVERSION}-' ..
	'${EDITREQUEST}-' ..
	'${EXPIRY}-' ..
	'${EXPLANATIONBLURB}-' ..
	'${IMAGELINK}-' ..
	'${INTROBLURB}-' ..
	'${INTROFRAGMENT}-' ..
	'${PAGETYPE}-' ..
	'${PROTECTIONBLURB}-' ..
	'${PROTECTIONDATE}-' ..
	'${PROTECTIONLEVEL}-' ..
	'${PROTECTIONLOG}-' ..
	'${TALKPAGE}-' ..
	'${TOOLTIPBLURB}-' ..
	'${TOOLTIPFRAGMENT}-' ..
	'${VANDAL}'

	local expected = '1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16'

	self:assertEquals(expected, obj:_substituteParameters(msg))
end
 
-- makeCurrentVersionParameter

function suite:testBlurbMakeCurrentVersionParameterEdit()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'edit'
	obj._cfg.msg['current-version-edit-display'] = 'edit display'
	self:assertEquals(
		'[//en.wikipedia.org/w/index.php?title='
			.. mw.uri.encode(d.page)
			.. '&action=history edit display]',
		obj:_makeCurrentVersionParameter()
	)
end

function suite:testBlurbMakeCurrentVersionParameterMove()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'move'
	obj._cfg.msg['current-version-move-display'] = 'move display'
	self:assertEquals(
		'[//en.wikipedia.org/w/index.php?title='
			.. mw.uri.encode('Special:Log')
			.. '&page='
			.. mw.uri.encode(d.page)
			.. '&type=move move display]',
		obj:_makeCurrentVersionParameter()
	)
end

-- _makeEditRequestParameter

function suite:testBlurbMakeEditRequestParameterLevels()
	-- We can't test the edit requests directly as that is the responsibility of
	-- [[Module:Submit an edit request]], but we can check that we get different
	-- outputs for different protection levels.
	local function makeBlurbObjectWithLevels(action, level)
		local obj = makeDefaultBlurbObject()
		obj._protectionObj.action = action
		obj._protectionObj.level = level
		obj._cfg.msg['edit-request-display'] = 'display'
		return obj
	end
	local obj1 = makeBlurbObjectWithLevels('edit', 'autoconfirmed')
	local obj2 = makeBlurbObjectWithLevels('edit', 'templateeditor')
	local obj3 = makeBlurbObjectWithLevels('edit', 'sysop')
	local obj4 = makeBlurbObjectWithLevels('move', 'templateeditor')

	self:assertFalse(obj1:_makeEditRequestParameter() == obj2:_makeEditRequestParameter())
	self:assertFalse(obj2:_makeEditRequestParameter() == obj3:_makeEditRequestParameter())
	self:assertEquals(obj3:_makeEditRequestParameter(), obj4:_makeEditRequestParameter())
end

function suite:testBlurbMakeEditRequestParameterLink()
	-- Check that the edit request links have features that we can always expect
	-- to be there. The rest is subject to be changed by [[Module:Submit an edit request]]
	-- at any time, so we won't test that here.
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'edit'
	obj._protectionObj.level = 'autoconfirmed'
	obj._cfg.msg['edit-request-display'] = 'the edit request display'
	self:assertStringContains(
		'//en.wikipedia.org/w/index.php?',
		obj:_makeEditRequestParameter(),
		true
	)
	self:assertStringContains(
		'action=edit',
		obj:_makeEditRequestParameter(),
		true
	)
	self:assertStringContains(
		'title=[a-zA-Z0-9%%_]*[tT]alk',
		obj:_makeEditRequestParameter(),
		false
	)
	self:assertStringContains(
		'the edit request display',
		obj:_makeEditRequestParameter(),
		true
	)
end

-- _makeExpiryParameter

function suite:testBlurbMakeExpiryParameterTemp()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.expiry = 0
	function obj:_formatDate(num)
		return 'unix date is ' .. tostring(num)
	end
	self:assertEquals('unix date is 0', obj:_makeExpiryParameter())
end

function suite:testBlurbMakeExpiryParameterOther()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.expiry = 'indef'
	function obj:_formatDate(num)
		return 'unix date is ' .. tostring(num)
	end
	self:assertEquals('indef', obj:_makeExpiryParameter())
end

-- _makeExplanationBlurbParameter

function suite:testBlurbMakeExplanationBlurbParameter()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'edit'
	obj._protectionObj.level = 'autoconfirmed'
	rawset(obj._protectionObj.title, 'isTalkPage', true)

	obj._cfg.explanationBlurbs = {
		edit = {
			autoconfirmed = {
				talk = 'edit-autoconfirmed-talk',
				default = 'edit-autoconfirmed-default'
			},
			default = {
				talk = 'edit-default-talk',
				default = 'edit-default-default'
			}
		}
	}

	function obj:_substituteParameters(msg)
		return msg
	end

	self:assertEquals(
		'edit-autoconfirmed-talk',
		obj:_makeExplanationBlurbParameter()
	)

	obj._cfg.explanationBlurbs.edit.autoconfirmed.talk = nil
	self:assertEquals(
		'edit-autoconfirmed-default',
		obj:_makeExplanationBlurbParameter()
	)

	obj._cfg.explanationBlurbs.edit.autoconfirmed.default = nil
	self:assertEquals(
		'edit-default-talk',
		obj:_makeExplanationBlurbParameter()
	)

	obj._cfg.explanationBlurbs.edit.default.talk = nil
	self:assertEquals(
		'edit-default-default',
		obj:_makeExplanationBlurbParameter()
	)

	obj._cfg.explanationBlurbs.edit.default.default = nil
	self:assertError(
		obj._makeExplanationBlurbParameter,
		{obj},
		'could not find explanation blurb for action "edit",'
			.. ' level "autoconfirmed" and talk key "talk"'
	)
end

function suite:testBlurbMakeExplanationBlurbParameterSpecialCases()
	local obj = makeDefaultBlurbObject()
	rawset(obj._protectionObj.title, 'namespace', 8)
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'the key is explanation-blurb-nounprotect',
		obj:_makeExplanationBlurbParameter()
	)
end

-- _makeImageLinkParameter

function suite:testBlurbMakeImageLinkParameter()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'move'
	obj._protectionObj.level = 'sysop'

	obj._cfg.imageLinks = {
		edit = {
			sysop = 'edit-sysop',
			default = 'edit-default'
		},
		move = {
			sysop = 'move-sysop',
			default = 'move-default'
		}
	}

	function obj:_substituteParameters(msg)
		return msg
	end

	self:assertEquals(
		'move-sysop',
		obj:_makeImageLinkParameter()
	)

	obj._cfg.imageLinks.move.sysop = nil
	self:assertEquals(
		'move-default',
		obj:_makeImageLinkParameter()
	)

	obj._cfg.imageLinks.move.default = nil
	self:assertEquals(
		'edit-default',
		obj:_makeImageLinkParameter()
	)
end

-- _makeIntroBlurbParameter

function suite:testBlurbMakeIntroBlurbParameter()
	local obj = makeDefaultBlurbObject()
	function obj._protectionObj:isTemporary()
		return true
	end
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'the key is intro-blurb-expiry',
		obj:_makeIntroBlurbParameter()
	)

	function obj._protectionObj:isTemporary()
		return false
	end
	self:assertEquals(
		'the key is intro-blurb-noexpiry',
		obj:_makeIntroBlurbParameter()
	)
end

-- _makeIntroFragmentParameter

function suite:testBlurbMakeIntroFragmentParameter()
	local obj = makeDefaultBlurbObject()
	function obj._protectionObj:isTemporary()
		return true
	end
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'the key is intro-fragment-expiry',
		obj:_makeIntroFragmentParameter()
	)

	function obj._protectionObj:isTemporary()
		return false
	end
	self:assertEquals(
		'the key is intro-fragment-noexpiry',
		obj:_makeIntroFragmentParameter()
	)
end

-- _makePagetypeParameter

function suite:testPagetypeParameter()
	local obj = makeDefaultBlurbObject()
	rawset(obj._protectionObj.title, 'namespace', 3)

	obj._cfg.pagetypes = {
		[3] = 'user talk page',
		default = 'default page'
	}

	self:assertEquals(
		'user talk page',
		obj:_makePagetypeParameter()
	)

	obj._cfg.pagetypes[3] = nil
	self:assertEquals(
		'default page',
		obj:_makePagetypeParameter()
	)

	obj._cfg.pagetypes.default = nil
	self:assertError(
		obj._makePagetypeParameter,
		{obj},
		'no default pagetype defined'
	)
end

-- _makeProtectionBlurbParameter

function suite:testBlurbMakeProtectionBlurbParameter()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'move'
	obj._protectionObj.level = 'sysop'

	obj._cfg.protectionBlurbs = {
		edit = {
			sysop = 'edit-sysop',
			default = 'edit-default'
		},
		move = {
			sysop = 'move-sysop',
			default = 'move-default'
		}
	}

	function obj:_substituteParameters(msg)
		return msg
	end

	self:assertEquals(
		'move-sysop',
		obj:_makeProtectionBlurbParameter()
	)

	obj._cfg.protectionBlurbs.move.sysop = nil
	self:assertEquals(
		'move-default',
		obj:_makeProtectionBlurbParameter()
	)

	obj._cfg.protectionBlurbs.move.default = nil
	self:assertEquals(
		'edit-default',
		obj:_makeProtectionBlurbParameter()
	)

	obj._cfg.protectionBlurbs.edit.default = nil
	self:assertError(
		obj._makeProtectionBlurbParameter,
		{obj},
		'no protection blurb defined for protectionBlurbs.edit.default'
	)
end

-- _makeProtectionDateParameter

function suite:testBlurbMakeProtectionDateParameter()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.protectionDate = 0
	function obj:_formatDate(num)
		return 'unix date is ' .. tostring(num)
	end
	self:assertEquals('unix date is 0', obj:_makeProtectionDateParameter())

	obj._protectionObj.protectionDate = 'indef'
	self:assertEquals('indef', obj:_makeProtectionDateParameter())
end

-- _makeProtectionLevelParameter

function suite:testBlurbMakeProtectionLevelParameter()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'move'
	obj._protectionObj.level = 'sysop'

	obj._cfg.protectionLevels = {
		edit = {
			sysop = 'edit-sysop',
			default = 'edit-default'
		},
		move = {
			sysop = 'move-sysop',
			default = 'move-default'
		}
	}

	function obj:_substituteParameters(msg)
		return msg
	end

	self:assertEquals(
		'move-sysop',
		obj:_makeProtectionLevelParameter()
	)

	obj._cfg.protectionLevels.move.sysop = nil
	self:assertEquals(
		'move-default',
		obj:_makeProtectionLevelParameter()
	)

	obj._cfg.protectionLevels.move.default = nil
	self:assertEquals(
		'edit-default',
		obj:_makeProtectionLevelParameter()
	)

	obj._cfg.protectionLevels.edit.default = nil
	self:assertError(
		obj._makeProtectionLevelParameter,
		{obj},
		'no protection level defined for protectionLevels.edit.default'
	)
end

-- _makeProtectionLogParameter

function suite:testBlurbMakeProtectionLogParameterPC()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'autoreview'
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertStringContains(
		'^%[//en%.wikipedia%.org/w/index%.php?',
		obj:_makeProtectionLogParameter(),
		false
	)
	self:assertStringContains(
		'title=' .. mw.uri.encode('Special:Log'),
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'type=stable',
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'page=' .. mw.uri.encode(d.page),
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'the key is pc%-log%-display%]$',
		obj:_makeProtectionLogParameter(),
		false
	)
end

function suite:testBlurbMakeProtectionLogParameterProtection()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.action = 'edit'
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertStringContains(
		'^%[//en%.wikipedia%.org/w/index%.php?',
		obj:_makeProtectionLogParameter(),
		false
	)
	self:assertStringContains(
		'title=' .. mw.uri.encode('Special:Log'),
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'type=protect',
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'page=' .. mw.uri.encode(d.page),
		obj:_makeProtectionLogParameter(),
		true
	)
	self:assertStringContains(
		'the key is protection%-log%-display%]$',
		obj:_makeProtectionLogParameter(),
		false
	)
end

-- _makeTalkPageParameter

function suite:testBlurbMakeTalkPageParameter()
	local obj = makeDefaultBlurbObject()
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'[['
			.. d.talkPage .. '#top|'
			.. 'the key is talk-page-link-display'
			.. ']]',
		obj:_makeTalkPageParameter()
	)
	obj._args.section = 'talk section'
	self:assertEquals(
		'[['
			.. d.talkPage .. '#talk section|'
			.. 'the key is talk-page-link-display'
			.. ']]',
		obj:_makeTalkPageParameter()
	)
end

-- _makeTooltipBlurbParameter

function suite:testBlurbMakeTooltipBlurbParameter()
	local obj = makeDefaultBlurbObject()
	function obj._protectionObj:isTemporary()
		return true
	end
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'the key is tooltip-blurb-expiry',
		obj:_makeTooltipBlurbParameter()
	)

	function obj._protectionObj:isTemporary()
		return false
	end
	self:assertEquals(
		'the key is tooltip-blurb-noexpiry',
		obj:_makeTooltipBlurbParameter()
	)
end

-- _makeTooltipFragmentParameter

function suite:testBlurbMakeTooltipFragmentParameter()
	local obj = makeDefaultBlurbObject()
	function obj._protectionObj:isTemporary()
		return true
	end
	function obj:_getExpandedMessage(key)
		return 'the key is ' .. key
	end
	self:assertEquals(
		'the key is tooltip-fragment-expiry',
		obj:_makeTooltipFragmentParameter()
	)

	function obj._protectionObj:isTemporary()
		return false
	end
	self:assertEquals(
		'the key is tooltip-fragment-noexpiry',
		obj:_makeTooltipFragmentParameter()
	)
end

-- _makeVandalTemplateParameter

function suite:testBlurbMakeVandalTemplateParameter()
	local obj = makeDefaultBlurbObject()
	self:assertStringContains(
		d.baseText,
		obj:_makeVandalTemplateParameter(),
		true
	)
	obj._args.user = 'Some user'
	self:assertStringContains(
		'Some user',
		obj:_makeVandalTemplateParameter(),
		true
	)
end

-- makeBannerText

function suite:testBlurbMakeBannerTextBadInput()
	local obj = makeDefaultBlurbObject()
	self:assertError(
		obj.makeBannerText,
		{obj, 'foo'},
		'"foo" is not a valid banner config field'
	)
	self:assertError(
		obj.makeBannerText,
		{obj, nil},
		'"nil" is not a valid banner config field'
	)
end

function suite:testBlurbMakeBannerTextGoodInput()
	local obj = makeDefaultBlurbObject()
	obj._protectionObj.bannerConfig = {
		text = 'banner text',
		explanation = 'banner explanation',
		tooltip = 'banner tooltip',
		alt = 'banner alt',
		link = 'banner link'
	}
	self:assertNotError(obj.makeBannerText, {obj, 'text'})
	self:assertNotError(obj.makeBannerText, {obj, 'explanation'})
	self:assertNotError(obj.makeBannerText, {obj, 'tooltip'})
	self:assertNotError(obj.makeBannerText, {obj, 'alt'})
	self:assertNotError(obj.makeBannerText, {obj, 'link'})
end

function suite:testBlurbMakeBannerTextString()
	local obj = makeDefaultBlurbObject()
	function obj:_substituteParameters(msg)
		return msg
	end
	obj._protectionObj.bannerConfig = {
		text = 'banner text',
	}
	self:assertEquals('banner text', obj:makeBannerText('text'))
end

function suite:testBlurbMakeBannerTextBadFunction()
	local obj = makeDefaultBlurbObject()
	function obj:_substituteParameters(msg)
		return msg
	end
	obj._protectionObj.bannerConfig = {
		text = function () return 9 end,
	}
	self:assertError(
		obj.makeBannerText,
		{obj, 'text'},
		'bad output from banner config function with key "text"'
			.. ' (expected string, got number)'
	)
end

function suite:testBlurbMakeBannerTextGoodFunction()
	local obj = makeDefaultBlurbObject()
	function obj:_substituteParameters(msg)
		return msg
	end
	obj._protectionObj.bannerConfig = {
		text = function () return 'some text' end,
	}
	self:assertEquals('some text', obj:makeBannerText('text'))
end

--------------------------------------------------------------------------------
-- BannerTemplate class tests
--------------------------------------------------------------------------------

-- BannerTemplate.new

function suite:testBannerTemplateNewCfg()
	local protectionObj = makeDefaultProtectionObject()
	local obj = BannerTemplate.new(protectionObj, makeConfig{foo = 'bar'})
	self:assertEquals('bar', obj._cfg.foo)
end

function suite:testBannerTemplateNewImageIndefTemplateOrModule()
	local cfg = {
		msg = {['image-filename-indef'] = 'red padlock'}
	}
	local protectionObj = makeDefaultProtectionObject()
	protectionObj.action = 'edit'
	protectionObj.level = 'sysop'
	function protectionObj:isTemporary() return false end

	rawset(protectionObj.title, 'namespace', 10)
	local obj1 = BannerTemplate.new(protectionObj, makeConfig(cfg))
	self:assertEquals('red padlock', obj1._imageFilename)

	rawset(protectionObj.title, 'namespace', 828)
	local obj2 = BannerTemplate.new(protectionObj, makeConfig(cfg))
	self:assertEquals('red padlock', obj2._imageFilename)
end

function suite:testBannerTemplateNewImageUsesIndefReason()
	local cfg = {
		indefImageReasons = {[d.reason] = true},
		msg = {['image-filename-indef'] = 'red padlock'}
	}
	local protectionObj = makeDefaultProtectionObject()
	protectionObj.action = 'edit'
	protectionObj.level = 'sysop'
	function protectionObj:isTemporary() return false end
	rawset(protectionObj.title, 'namespace', 2)
	local obj = BannerTemplate.new(protectionObj, makeConfig(cfg))
	self:assertEquals('red padlock', obj._imageFilename)
end		

function suite:testBannerTemplateNewImageDefault()
	local images = {
		move = {
			sysop = 'foo',
			default = 'bar'
		}
	}
	local protectionObj = makeDefaultProtectionObject()
	protectionObj.action = 'move'
	protectionObj.level = 'sysop'
	local obj = BannerTemplate.new(protectionObj, makeConfig{
		images = images
	})

	self:assertEquals('foo', obj._imageFilename)

	images.move.sysop = nil
	obj = BannerTemplate.new(protectionObj, makeConfig{
		images = images
	})
	self:assertEquals('bar', obj._imageFilename)

	images.move.default = nil
	obj = BannerTemplate.new(protectionObj, makeConfig{
		images = images
	})
	self:assertEquals(nil, obj._imageFilename)
end

-- renderImage

function suite:testBannerTemplateRenderImageFilename()
	local obj = makeDefaultBannerTemplateObject()
	obj._imageFilename = 'ImageFilename.png'
	self:assertStringContains('ImageFilename.png', obj:renderImage(), true)
end

function suite:testBannerTemplateRenderImageDefault()
	local obj = makeDefaultBannerTemplateObject()
	obj._cfg.msg['image-filename-default'] = 'Defaultfilename.png'
	self:assertStringContains('Defaultfilename.png', obj:renderImage(), true)
end

function suite:testBannerTemplateRenderImageDefaultNoConfig()
	local obj = makeDefaultBannerTemplateObject()
	self:assertStringContains('Transparent.gif', obj:renderImage(), true)
end

function suite:testBannerTemplateRenderImageDefaultWidth()
	local obj = makeDefaultBannerTemplateObject()
	self:assertStringContains('20px', obj:renderImage(), true)
end

function suite:testBannerTemplateRenderImageCustomWidth()
	local obj = makeDefaultBannerTemplateObject()
	obj.imageWidth = 50
	self:assertStringContains('50px', obj:renderImage(), true)
end

function suite:testBannerTemplateRenderImageAlt()
	local obj = makeDefaultBannerTemplateObject()
	obj._imageAlt = 'the alt text'
	self:assertStringContains('alt%s*=%s*the alt text', obj:renderImage(), false)
end

function suite:testBannerTemplateRenderImageLink()
	local obj = makeDefaultBannerTemplateObject()
	obj._imageLink = 'the link text'
	self:assertStringContains('link%s*=%s*the link text', obj:renderImage(), false)
end

function suite:testBannerTemplateRenderImageCaption()
	local obj = makeDefaultBannerTemplateObject()
	obj.imageCaption = 'the caption text'
	self:assertStringContains('the caption text', obj:renderImage(), true)
end

--------------------------------------------------------------------------------
-- Banner class tests
--------------------------------------------------------------------------------

function suite:testBannerNew()
	local protectionObj = makeDefaultProtectionObject()
	local blurbObj = makeDefaultBlurbObject()
	local cfg = makeConfig()

	function blurbObj:makeBannerText(key)
		if key == 'alt' then
			return 'the alt text'
		elseif key == 'text' then
			return 'the main text'
		elseif key == 'explanation' then
			return 'the explanation text'
		end
	end
	
	local obj = Banner.new(protectionObj, blurbObj, cfg)

	self:assertEquals(40, obj.imageWidth)
	self:assertEquals('the alt text', obj.imageCaption)
	self:assertEquals('the main text', obj._reasonText)
	self:assertEquals('the explanation text', obj._explanationText)
	self:assertEquals(d.page, obj._page)
end

-- __tostring

function suite:testBannerToStringError()
	local obj = makeDefaultBannerObject()
	obj._reasonText = nil
	self:assertError(obj.__tostring, {obj}, 'no reason text set')
end

function suite:testBannerToString()
	local obj = makeDefaultBannerObject()
	obj._reasonText = 'the reason text'
	obj._explanationText = 'the explanation text'
	
	function obj:renderImage()
		return '[[File:Example.png|30px]]'
	end

	self:assertStringContains('[[File:Example.png|30px]]', tostring(obj), true)
	self:assertStringContains(
		"'''the reason text'''<br />the explanation text",
		tostring(obj),
		true
	)

	obj._explanationText = nil
	self:assertStringContains("'''the reason text'''", tostring(obj), true)
end

--------------------------------------------------------------------------------
-- Padlock class tests
--------------------------------------------------------------------------------

function suite:testPadlockNew()
	local protectionObj = makeDefaultProtectionObject()
	local blurbObj = makeDefaultBlurbObject()
	local cfg = makeConfig()

	function blurbObj:makeBannerText(key)
		if key == 'alt' then
			return 'the alt text'
		elseif key == 'tooltip' then
			return 'the tooltip text'
		elseif key == 'link' then
			return 'the link text'
		end
	end
	
	local obj = Padlock.new(protectionObj, blurbObj, cfg)

	self:assertEquals(20, obj.imageWidth)
	self:assertEquals('the tooltip text', obj.imageCaption)
	self:assertEquals('the alt text', obj._imageAlt)
	self:assertEquals('the link text', obj._imageLink)
end

function suite:testPadlockNewIndicators()
	local protectionObj = makeDefaultProtectionObject()
	protectionObj.action = 'move'
	protectionObj.level = 'sysop'
	local blurbObj = makeDefaultBlurbObject()

	local cfg = makeConfig{padlockIndicatorNames = {
		move = 'move-indicator',
		default = 'default-indicator'
	}}
	local obj = Padlock.new(protectionObj, blurbObj, cfg)
	self:assertEquals(obj._indicatorName, 'move-indicator')

	cfg.padlockIndicatorNames.move = nil
	obj = Padlock.new(protectionObj, blurbObj, cfg)
	self:assertEquals(obj._indicatorName, 'default-indicator')

	cfg.padlockIndicatorNames.default = nil
	obj = Padlock.new(protectionObj, blurbObj, cfg)
	self:assertEquals(obj._indicatorName, 'pp-default')
end

-- __tostring

function suite:testPadlockToString()
	local obj = makeDefaultPadlockObject()
	self:assertIsPadlock(tostring(obj))
end

--------------------------------------------------------------------------------
-- Export tests
--------------------------------------------------------------------------------

-- _main

function suite:test_mainError()
	local args = {action = 'foobar'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	local success, result = pcall(mProtectionBanner._main, args, cfg, title)
	self:assertFalse(success)
	self:assertEquals(
		'invalid action: foobar',
		result,
		false
	)
end

function suite:test_mainSmall1()
	local args = {small = 'yes'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsPadlock(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainSmall2()
	local args = {small = 'Yes'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsPadlock(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainSmall3()
	local args = {small = 'true'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsPadlock(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainLarge1()
	local args = {}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainLarge2()
	local args = {small = 'no'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainLarge3()
	local args = {small = 'No'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainLarge4()
	local args = {small = 'false'}
	local cfg = makeConfig()
	local title = makeDefaultTitleObject()
	self:assertIsBanner(mProtectionBanner._main(args, cfg, title))
end

function suite:test_mainNoBanner()
	local args = {}
	local cfg = makeConfig()
	local title = makeTitleObject{page = d.page, edit = nil}
	self:assertNoBanner(mProtectionBanner._main(args, cfg, title), 'page unprotected')
end

function suite:test_mainNoBannerForUserScripts()
	local args = {}
	local cfg = makeConfig()
	local title = makeTitleObject{page = 'User:Example/common.css', contentType = 'css'}
	self:assertNoBanner(mProtectionBanner._main(args, cfg, title), 'page is a user script')
end

function suite:test_mainCategories()
	local args = {}
	local cfg -- Use main config module
	local title = makeTitleObject{page = d.page, 'edit', nil}
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner._main(args, cfg, title),
		false,
		'page unprotected'
	)
end

-- p.main

function suite:testMainHasOutput()
	local frame = mw.getCurrentFrame()
	local parent = frame:newChild{args = {}}
	local child = parent:newChild{args = {}}
	local cfg -- Use main config module
	self:assertStringContains('%S', mProtectionBanner.main(child, cfg), false)
end

function suite:testMainWrapper()
	local frame = mw.getCurrentFrame()
	local parent = frame:newChild{title = 'Template:Pp-example', args = {}}
	local child = parent:newChild{args = {}}
	local cfg = makeConfig{
		msg = {['tracking-category-incorrect'] = 'Incorrect'},
		wrappers = {['Template:Pp-example'] = {category = false}}
	}
	self:assertNotStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(child, cfg),
		false
	)
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(parent, cfg),
		false
	)
end

function suite:testMainWrapperOverride()
	local frame = mw.getCurrentFrame()
	local parent = frame:newChild{title = 'Template:Pp-example', args = {category = 'yes'}}
	local child = parent:newChild{args = {}}
	local cfg = makeConfig{
		msg = {['tracking-category-incorrect'] = 'Incorrect'},
		wrappers = {['Template:Pp-example'] = {category = false}}
	}
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(child, cfg),
		false
	)
end

function suite:testMainWrapperSandbox()
	local frame = mw.getCurrentFrame()
	local parent = frame:newChild{title = 'Template:Pp-example/sandbox', args = {}}
	local child = parent:newChild{args = {}}
	local cfg = makeConfig{
		msg = {['tracking-category-incorrect'] = 'Incorrect'},
		wrappers = {['Template:Pp-example'] = {category = false}}
	}
	self:assertNotStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(child, cfg),
		false
	)
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(parent, cfg),
		false
	)
end

function suite:testMainNoWrapper()
	local frame = mw.getCurrentFrame()
	local parent = frame:newChild{title = 'Template:Some template', args = {}}
	local child = parent:newChild{args = {}}
	local cfg = makeConfig{
		msg = {['tracking-category-incorrect'] = 'Incorrect'},
		wrappers = {['Template:Pp-example'] = {category = false}}
	}
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(child, cfg),
		false
	)
	self:assertStringContains(
		'%[%[Category:.-%]%]',
		mProtectionBanner.main(parent, cfg),
		false
	)
end

return suite