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 '' end for _, field in ipairs{'isIncorrect', 'isTemplateProtectedNonTemplate'} do		obj[field] = function return true end end self:assertEquals(		 ..		 ..		'',		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(					.. 'the key is talk-page-link-display'			.. ,		obj:_makeTalkPageParameter	) obj._args.section = 'talk section' self:assertEquals(					.. '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 '' end

self:assertStringContains('', 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