Module:Protection banner/testcases

-- 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

local padlockPattern = '\127[^\127]*UNIQ%-%-indicator%-%x+%-QINU[^\127]*\127'

function suite:assertIsPadlock(s, msg)

self:assertStringContains(

padlockPattern,

s,

false,

msg

)

end

function suite:assertNoPadlock(s, msg)

self:assertNotStringContains(

padlockPattern,

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 = 'templateeditor'

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 = 'templateeditor'

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 '' .. d.baseText .. ''

end

for _, field in ipairs{'isIncorrect', 'isTemplateProtectedNonTemplate'} do

obj[field] = function () return true end

end

self:assertEquals(

'' .. d.baseText .. '' ..

'' .. d.baseText .. '' ..

'' .. 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 '30px'

end

self:assertStringContains('30px', tostring(obj), true)

self:assertStringContains(

"the reason text
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('move-indicator', obj._indicatorName)

cfg.padlockIndicatorNames.move = nil

obj = Padlock.new(protectionObj, blurbObj, cfg)

self:assertEquals('default-indicator', obj._indicatorName)

cfg.padlockIndicatorNames.default = nil

obj = Padlock.new(protectionObj, blurbObj, cfg)

self:assertEquals('pp-default', obj._indicatorName)

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

)

end

function suite:testCatOnlyHidesOutput()

local args1 = {catonly = 'yes'}

local args2 = {catonly = 'yes', small = 'yes'}

local cfg = makeConfig()

local title = makeDefaultTitleObject()

self:assertNoBanner(mProtectionBanner._main(args1, cfg, title))

self:assertNoPadlock(mProtectionBanner._main(args2, cfg, title))

end

function suite:testCatOnlyShowsCategory()

local args = {catonly = 'yes'}

local cfg = makeConfig()

cfg.protectionCategories = {

[makeProtectionCategoryKey{expiry = 'indef', action = 'edit'}] = 'indef expiry',

}

local title = makeDefaultTitleObject()

self:assertStringContains("Category:indef expiry", mProtectionBanner._main(args, cfg, title), true)

end

function suite:testCatOnlyNoGivesOutput()

local args = {catonly = 'no'}

local cfg = makeConfig()

local title = makeDefaultTitleObject()

self:assertIsBanner(mProtectionBanner._main(args, cfg, title))

end

function suite:testCatOnlyNoShowsCategory()

local args = {catonly = 'no'}

local cfg = makeConfig()

cfg.protectionCategories = {

[makeProtectionCategoryKey{expiry = 'indef', action = 'edit'}] = 'indef expiry',

}

local title = makeDefaultTitleObject()

self:assertStringContains("Category:indef expiry", mProtectionBanner._main(args, cfg, title), true)

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