Jump to content

Module:Mock title/testcases: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
add tests to check that titles are not patched before or after patching functions are run
m use single quotes for strings
Line 1: Line 1:
local mMockTitle = require("Module:Mock title")
local mMockTitle = require('Module:Mock title')
local ScribuntoUnit = require("Module:ScribuntoUnit")
local ScribuntoUnit = require('Module:ScribuntoUnit')


local suite = ScribuntoUnit:new()
local suite = ScribuntoUnit:new()
Line 10: Line 10:
function suite:assertTitlesAreEqual(expected, actual)
function suite:assertTitlesAreEqual(expected, actual)
local properties = {
local properties = {
"prefixedText",
'prefixedText',
"id",
'id',
"exists",
'exists',
"isRedirect",
'isRedirect',
"redirectTarget",
'redirectTarget',
"contentModel",
'contentModel',
}
}
for _, prop in ipairs(properties) do
for _, prop in ipairs(properties) do
Line 25: Line 25:
end
end
local deepProperties = {"protectionLevels", "cascadingProtection"}
local deepProperties = {'protectionLevels', 'cascadingProtection'}
for _, prop in ipairs(deepProperties) do
for _, prop in ipairs(deepProperties) do
self:assertDeepEquals(
self:assertDeepEquals(
Line 46: Line 46:
if expected.file and actual.file then
if expected.file and actual.file then
local fileProperties = {
local fileProperties = {
"exists",
'exists',
"width",
'width',
"height",
'height',
"size",
'size',
"mimeType",
'mimeType',
"length",
'length',
}
}
for _, prop in ipairs(fileProperties) do
for _, prop in ipairs(fileProperties) do
Line 71: Line 71:
expected:getContent(),
expected:getContent(),
actual:getContent(),
actual:getContent(),
"mismatch in getContent()"
'mismatch in getContent()'
)
)
end
end
Line 89: Line 89:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


suite["test MockTitle: when no argument table is supplied, an error is raised"] = function (self)
suite['test MockTitle: when no argument table is supplied, an error is raised'] = function (self)
self:assertThrows(
self:assertThrows(
function () mMockTitle.MockTitle() end,
function () mMockTitle.MockTitle() end,
Line 96: Line 96:
end
end


suite["test MockTitle: when no title argument is supplied, an error is raised"] = function (self)
suite['test MockTitle: when no title argument is supplied, an error is raised'] = function (self)
self:assertThrows(
self:assertThrows(
function () mMockTitle.MockTitle{} end,
function () mMockTitle.MockTitle{} end,
Line 104: Line 104:


local pageNames = {
local pageNames = {
"Example",
'Example',
"fr:Example",
'fr:Example',
"Module:Sandbox",
'Module:Sandbox',
"Module:Sandbox/subpage",
'Module:Sandbox/subpage',
"mw:Test",
'mw:Test',
"fr:b:Example",
'fr:b:Example',
}
}


Line 125: Line 125:
local simplePropertyTestData = {
local simplePropertyTestData = {
{
{
property = "id",
property = 'id',
title = "Example",
title = 'Example',
value = 123456,
value = 123456,
},
},
{
{
property = "isRedirect",
property = 'isRedirect',
title = "Main Page",
title = 'Main Page',
value = true,
value = true,
},
},
{
{
property = "exists",
property = 'exists',
title = "Main Page",
title = 'Main Page',
value = false,
value = false,
},
},
{
{
property = "contentModel",
property = 'contentModel',
title = "Main Page",
title = 'Main Page',
value = "foo",
value = 'foo',
},
},
}
}


for _, testData in ipairs(simplePropertyTestData) do
for _, testData in ipairs(simplePropertyTestData) do
suite[string.format("test MockTitle: property %s is mocked using the %s option", testData.property, testData.property)] = function (self)
suite[string.format('test MockTitle: property %s is mocked using the %s option', testData.property, testData.property)] = function (self)
local title = mMockTitle.MockTitle({title = testData.title, [testData.property] = testData.value})
local title = mMockTitle.MockTitle({title = testData.title, [testData.property] = testData.value})
self:assertEquals(testData.value, title[testData.property])
self:assertEquals(testData.value, title[testData.property])
Line 153: Line 153:
end
end


suite["test MockTitle: passing a page name to the redirectTarget option causes redirectTarget to be a MockTitle object with that page name"] = function (self)
suite['test MockTitle: passing a page name to the redirectTarget option causes redirectTarget to be a MockTitle object with that page name'] = function (self)
local title = mMockTitle.MockTitle({title = "Example", redirectTarget = "User:Example"})
local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = 'User:Example'})
self:assertEquals("User:Example", title.redirectTarget.prefixedText)
self:assertEquals('User:Example', title.redirectTarget.prefixedText)
end
end


suite["test MockTitle: passing an ID to the redirectTarget option causes redirectTarget to be a MockTitle object with that ID"] = function (self)
suite['test MockTitle: passing an ID to the redirectTarget option causes redirectTarget to be a MockTitle object with that ID'] = function (self)
local mainPageId = 15580374
local mainPageId = 15580374
local title = mMockTitle.MockTitle({title = "Example", redirectTarget = mainPageId})
local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = mainPageId})
self:assertEquals(mainPageId, title.redirectTarget.id)
self:assertEquals(mainPageId, title.redirectTarget.id)
end
end


suite["test MockTitle: passing an options table to the redirectTarget option causes redirectTarget to be a MockTitle object with those options"] = function (self)
suite['test MockTitle: passing an options table to the redirectTarget option causes redirectTarget to be a MockTitle object with those options'] = function (self)
local title = mMockTitle.MockTitle({title = "Example", redirectTarget = {title = "User:Example/common.js", contentModel = "wikitext"}})
local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = {title = 'User:Example/common.js', contentModel = 'wikitext'}})
self:assertEquals("User:Example/common.js", title.redirectTarget.prefixedText)
self:assertEquals('User:Example/common.js', title.redirectTarget.prefixedText)
self:assertEquals("wikitext", title.redirectTarget.contentModel)
self:assertEquals('wikitext', title.redirectTarget.contentModel)
end
end


suite["test MockTitle: passing a MockTitle object to the redirectTarget option causes redirectTarget to be that MockTitle object"] = function (self)
suite['test MockTitle: passing a MockTitle object to the redirectTarget option causes redirectTarget to be that MockTitle object'] = function (self)
local mockRedirectTarget = mMockTitle.MockTitle({title = "User:Example/common.js", contentModel = "wikitext"})
local mockRedirectTarget = mMockTitle.MockTitle({title = 'User:Example/common.js', contentModel = 'wikitext'})
local title = mMockTitle.MockTitle({title = "Example", redirectTarget = mockRedirectTarget})
local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = mockRedirectTarget})
self:assertEquals("User:Example/common.js", title.redirectTarget.prefixedText)
self:assertEquals('User:Example/common.js', title.redirectTarget.prefixedText)
self:assertEquals("wikitext", title.redirectTarget.contentModel)
self:assertEquals('wikitext', title.redirectTarget.contentModel)
end
end


local protectionLevelTestData = {
local protectionLevelTestData = {
{
{
optionName = "editProtection",
optionName = 'editProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "edit",
expectedProtectionAction = 'edit',
},
},
{
{
optionName = "moveProtection",
optionName = 'moveProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "move",
expectedProtectionAction = 'move',
},
},
{
{
optionName = "createProtection",
optionName = 'createProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "create",
expectedProtectionAction = 'create',
},
},
{
{
optionName = "uploadProtection",
optionName = 'uploadProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "upload",
expectedProtectionAction = 'upload',
},
},
}
}
Line 208: Line 208:
)
)
] = function (self)
] = function (self)
local title = mMockTitle.MockTitle{title = "Example", [testData.optionName] = testData.optionValue}
local title = mMockTitle.MockTitle{title = 'Example', [testData.optionName] = testData.optionValue}
self:assertEquals(testData.optionValue, title.protectionLevels[testData.expectedProtectionAction][1])
self:assertEquals(testData.optionValue, title.protectionLevels[testData.expectedProtectionAction][1])
end
end
Line 215: Line 215:
local cascadingProtectionLevelTestData = {
local cascadingProtectionLevelTestData = {
{
{
optionName = "cascadingEditProtection",
optionName = 'cascadingEditProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "edit",
expectedProtectionAction = 'edit',
},
},
{
{
optionName = "cascadingMoveProtection",
optionName = 'cascadingMoveProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "move",
expectedProtectionAction = 'move',
},
},
{
{
optionName = "cascadingCreateProtection",
optionName = 'cascadingCreateProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "create",
expectedProtectionAction = 'create',
},
},
{
{
optionName = "cascadingUploadProtection",
optionName = 'cascadingUploadProtection',
optionValue = "sysop",
optionValue = 'sysop',
expectedProtectionAction = "upload",
expectedProtectionAction = 'upload',
},
},
}
}
Line 247: Line 247:
] = function (self)
] = function (self)
local title = mMockTitle.MockTitle{
local title = mMockTitle.MockTitle{
title = "Example",
title = 'Example',
[testData.optionName] = testData.optionValue,
[testData.optionName] = testData.optionValue,
cascadingProtectionSources = {"Example", "Example 2"},
cascadingProtectionSources = {'Example', 'Example 2'},
}
}
self:assertEquals(
self:assertEquals(
Line 256: Line 256:
)
)
self:assertDeepEquals(
self:assertDeepEquals(
{"Example", "Example 2"},
{'Example', 'Example 2'},
title.cascadingProtection.sources
title.cascadingProtection.sources
)
)
Line 262: Line 262:
end
end


suite["test MockTitle: when a cascading protection argument is given, but no cascading protection sources are given, an error is raised"] = function (self)
suite['test MockTitle: when a cascading protection argument is given, but no cascading protection sources are given, an error is raised'] = function (self)
self:assertThrows(
self:assertThrows(
function () mMockTitle.MockTitle{title = "Example", cascadingEditProtection = "sysop"} end,
function () mMockTitle.MockTitle{title = 'Example', cascadingEditProtection = 'sysop'} end,
"a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty"
'a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty'
)
)
end
end


suite["test MockTitle: when a cascading protection argument is given, but the cascading protection sources table is empty, an error is raised"] = function (self)
suite['test MockTitle: when a cascading protection argument is given, but the cascading protection sources table is empty, an error is raised'] = function (self)
self:assertThrows(
self:assertThrows(
function () mMockTitle.MockTitle{title = "Example", cascadingEditProtection = "sysop", cascadingProtectionSources = {}} end,
function () mMockTitle.MockTitle{title = 'Example', cascadingEditProtection = 'sysop', cascadingProtectionSources = {}} end,
"a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty"
'a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty'
)
)
end
end


suite["test MockTitle: when cascading protection sources are given, but no cascading protection argument is given, an error is raised"] = function (self)
suite['test MockTitle: when cascading protection sources are given, but no cascading protection argument is given, an error is raised'] = function (self)
self:assertThrows(
self:assertThrows(
function () mMockTitle.MockTitle{title = "Example", cascadingProtectionSources = {"Example 2", "Example 3"}} end,
function () mMockTitle.MockTitle{title = 'Example', cascadingProtectionSources = {'Example 2', 'Example 3'}} end,
"the cascadingProtectionSources argument was given, but no cascading protection argument was given"
'the cascadingProtectionSources argument was given, but no cascading protection argument was given'
)
)
end
end


suite['test MockTitle: if the content option is provided, getContent() returns the specified string'] = function (self)
suite['test MockTitle: if the content option is provided, getContent() returns the specified string'] = function (self)
local title = mMockTitle.MockTitle{title = "Non-existent page 29dh12yxm", content = "some [[wikitext]] content"}
local title = mMockTitle.MockTitle{title = 'Non-existent page 29dh12yxm', content = 'some [[wikitext]] content'}
self:assertEquals("some [[wikitext]] content", title:getContent())
self:assertEquals('some [[wikitext]] content', title:getContent())
end
end


local filePropertyTestData = {
local filePropertyTestData = {
{
{
property = "exists",
property = 'exists',
optionName = "fileExists",
optionName = 'fileExists',
optionValue = true,
optionValue = true,
},
},
{
{
property = "width",
property = 'width',
optionName = "fileWidth",
optionName = 'fileWidth',
optionValue = 123,
optionValue = 123,
},
},
{
{
property = "height",
property = 'height',
optionName = "fileHeight",
optionName = 'fileHeight',
optionValue = 456,
optionValue = 456,
},
},
{
{
property = "pages",
property = 'pages',
optionName = "filePages",
optionName = 'filePages',
optionValue = {{height = 800, width = 600}, {height = 800, width = 600}},
optionValue = {{height = 800, width = 600}, {height = 800, width = 600}},
},
},
{
{
property = "size",
property = 'size',
optionName = "fileSize",
optionName = 'fileSize',
optionValue = 1024,
optionValue = 1024,
},
},
{
{
property = "mimeType",
property = 'mimeType',
optionName = "fileMimeType",
optionName = 'fileMimeType',
optionValue = "image/png",
optionValue = 'image/png',
},
},
{
{
property = "length",
property = 'length',
optionName = "fileLength",
optionName = 'fileLength',
optionValue = 60,
optionValue = 60,
},
},
Line 332: Line 332:
tostring(testData.optionValue)
tostring(testData.optionValue)
)] = function(self)
)] = function(self)
local title = mMockTitle.MockTitle{title = "File:Non-existent hf893bc0.png", [testData.optionName] = testData.optionValue}
local title = mMockTitle.MockTitle{title = 'File:Non-existent hf893bc0.png', [testData.optionName] = testData.optionValue}
self:assertEquals(testData.optionValue, title.file[testData.property])
self:assertEquals(testData.optionValue, title.file[testData.property])
end
end
end
end


suite["test MockTitle: when setting fileExists in a non-file namespace, no file table is set"] = function(self)
suite['test MockTitle: when setting fileExists in a non-file namespace, no file table is set'] = function(self)
local title = mMockTitle.MockTitle{title = "Non-existent page 34u8wg90bfr", fileExists = true}
local title = mMockTitle.MockTitle{title = 'Non-existent page 34u8wg90bfr', fileExists = true}
self:assertEquals(nil, title.file)
self:assertEquals(nil, title.file)
end
end
Line 344: Line 344:
local fileExistsTestData = {
local fileExistsTestData = {
{
{
title = "Non-existent article tr9w78ebna0",
title = 'Non-existent article tr9w78ebna0',
expected = nil,
expected = nil,
},
},
{
{
title = "Talk:Non-existent talk page 34hdbe0pafj",
title = 'Talk:Non-existent talk page 34hdbe0pafj',
expected = nil,
expected = nil,
},
},
{
{
title = "File:Non-existent file 341gh87fgg8",
title = 'File:Non-existent file 341gh87fgg8',
expected = true,
expected = true,
},
},
{
{
title = "Media:Non-existent file pbfhrw3v8d",
title = 'Media:Non-existent file pbfhrw3v8d',
expected = true,
expected = true,
},
},
Line 366: Line 366:
'test MockTitle: when using page "%s", the fileExists property is %s',
'test MockTitle: when using page "%s", the fileExists property is %s',
testData.title,
testData.title,
testData.expected and "set" or "not set"
testData.expected and 'set' or 'not set'
)
)
] = function (self)
] = function (self)
Line 376: Line 376:
local fallbackPropertyTestData = {
local fallbackPropertyTestData = {
{
{
property = "id",
property = 'id',
option = "id",
option = 'id',
title = "Example",
title = 'Example',
},
},
{
{
property = "isRedirect",
property = 'isRedirect',
option = "isRedirect",
option = 'isRedirect',
title = "WP:ANI",
title = 'WP:ANI',
},
},
{
{
property = "exists",
property = 'exists',
option = "exists",
option = 'exists',
title = "Non-existent title f292umz0tyi",
title = 'Non-existent title f292umz0tyi',
},
},
{
{
property = "contentModel",
property = 'contentModel',
option = "contentModel",
option = 'contentModel',
title = "User:Example/common.js",
title = 'User:Example/common.js',
},
},
{
{
property = "redirectTarget",
property = 'redirectTarget',
option = "redirectTarget",
option = 'redirectTarget',
title = "WP:ANI",
title = 'WP:ANI',
},
},
{
{
property = "protectionLevels",
property = 'protectionLevels',
option = "editProtection",
option = 'editProtection',
title = "Main Page",
title = 'Main Page',
},
},
{
{
property = "redirectTarget",
property = 'redirectTarget',
option = "redirectTarget",
option = 'redirectTarget',
title = "WP:ANI",
title = 'WP:ANI',
},
},
{
{
property = "cascadingProtection",
property = 'cascadingProtection',
option = "cascadingEditProtection",
option = 'cascadingEditProtection',
title = "Main Page",
title = 'Main Page',
},
},
{
{
property = "cascadingProtection",
property = 'cascadingProtection',
option = "cascadingProtectionSources",
option = 'cascadingProtectionSources',
title = "Main Page",
title = 'Main Page',
},
},
}
}


for _, testData in ipairs(fallbackPropertyTestData) do
for _, testData in ipairs(fallbackPropertyTestData) do
suite[string.format("test MockTitle: if no %s option is specified, the real %s value is used", testData.option, testData.property)] = function (self)
suite[string.format('test MockTitle: if no %s option is specified, the real %s value is used', testData.option, testData.property)] = function (self)
suite:assertDeepEquals(
suite:assertDeepEquals(
mw.title.new(testData.title)[testData.property],
mw.title.new(testData.title)[testData.property],
Line 431: Line 431:
end
end


suite["test MockTitle: if no content option is specified, getContent() returns the the real content"] = function (self)
suite['test MockTitle: if no content option is specified, getContent() returns the the real content'] = function (self)
suite:assertEquals(
suite:assertEquals(
mw.title.new("Main Page"):getContent(),
mw.title.new('Main Page'):getContent(),
mMockTitle.MockTitle{title = "Main Page"}:getContent()
mMockTitle.MockTitle{title = 'Main Page'}:getContent()
)
)
end
end


suite["test MockTitle: the content property is not shared between mock objects"] = function (self)
suite['test MockTitle: the content property is not shared between mock objects'] = function (self)
suite:assertNotEquals(
suite:assertNotEquals(
mMockTitle.MockTitle{title = "Foo", content = "Bar"}:getContent(),
mMockTitle.MockTitle{title = 'Foo', content = 'Bar'}:getContent(),
mMockTitle.MockTitle{title = "Foo", content = "Baz"}:getContent()
mMockTitle.MockTitle{title = 'Foo', content = 'Baz'}:getContent()
)
)
end
end
Line 449: Line 449:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


suite["test patchTitleObject: patchTitleObject returns an equivalent object to MockTitle when used on the same title"] = function (self)
suite['test patchTitleObject: patchTitleObject returns an equivalent object to MockTitle when used on the same title'] = function (self)
suite:assertTitlesAreEqual(
suite:assertTitlesAreEqual(
mMockTitle.MockTitle{
mMockTitle.MockTitle{
title = "Main Page",
title = 'Main Page',
isRedirect = true,
isRedirect = true,
redirectTarget = "Wikipedia:Village stocks",
redirectTarget = 'Wikipedia:Village stocks',
content = '#REDIRECT: [[Wikipedia:Village stocks]]',
content = '#REDIRECT: [[Wikipedia:Village stocks]]',
},
},
Line 461: Line 461:
{
{
isRedirect = true,
isRedirect = true,
redirectTarget = "Wikipedia:Village stocks",
redirectTarget = 'Wikipedia:Village stocks',
content = "#REDIRECT: [[Wikipedia:Village stocks]]",
content = '#REDIRECT: [[Wikipedia:Village stocks]]',
}
}
)
)
Line 468: Line 468:
end
end


suite["test patchTitleObject: title arguments are ignored"] = function (self)
suite['test patchTitleObject: title arguments are ignored'] = function (self)
self:assertEquals(
self:assertEquals(
'User:Example',
'User:Example',
mMockTitle.patchTitleObject(
mMockTitle.patchTitleObject(
mw.title.new('User:Example'),
mw.title.new('User:Example'),
{title = "Wikipedia:Sandbox"}
{title = 'Wikipedia:Sandbox'}
).prefixedText
).prefixedText
)
)
Line 499: Line 499:
end
end


suite["test registerMockTitle: real titles are used during patching when no mocks are registered"] = function (self)
suite['test registerMockTitle: real titles are used during patching when no mocks are registered'] = function (self)
-- Setup
-- Setup
local realTitle = mw.title.new('Wikipedia:Sandbox')
local realTitle = mw.title.new('Wikipedia:Sandbox')
Line 522: Line 522:
for _, testData in ipairs(registerMockTitleTestData) do
for _, testData in ipairs(registerMockTitleTestData) do
suite[string.format(
suite[string.format(
"test registerMockTitle: mocks are registered when specified using %s",
'test registerMockTitle: mocks are registered when specified using %s',
testData.argType
testData.argType
)] = function (self)
)] = function (self)
Line 543: Line 543:
for _, testData in ipairs(registerMockTitleTestData) do
for _, testData in ipairs(registerMockTitleTestData) do
suite[string.format(
suite[string.format(
"test registerMockCurrentTitle: mocks are registered when specified using %s",
'test registerMockCurrentTitle: mocks are registered when specified using %s',
testData.argType
testData.argType
)] = function (self)
)] = function (self)
Line 567: Line 567:
end
end


suite["test deregisterMockTitle: previously registered titles are not deregistered if a deregistration function is not called"] = function (self)
suite['test deregisterMockTitle: previously registered titles are not deregistered if a deregistration function is not called'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 584: Line 584:


local deregisterMockTitleTestData = {
local deregisterMockTitleTestData = {
{argType = "title", arg = MOCK_WP_SANDBOX_ARGS.title},
{argType = 'title', arg = MOCK_WP_SANDBOX_ARGS.title},
{argType = "args table", arg = MOCK_WP_SANDBOX_ARGS},
{argType = 'args table', arg = MOCK_WP_SANDBOX_ARGS},
{argType = "ID", arg = MOCK_WP_SANDBOX.id},
{argType = 'ID', arg = MOCK_WP_SANDBOX.id},
{argType = "title object", arg = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)},
{argType = 'title object', arg = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)},
{argType = "mock title object", arg = MOCK_WP_SANDBOX},
{argType = 'mock title object', arg = MOCK_WP_SANDBOX},
}
}


Line 594: Line 594:
suite[
suite[
string.format(
string.format(
"test deregisterMockTitle: previously registered titles are deregistered when specified by %s",
'test deregisterMockTitle: previously registered titles are deregistered when specified by %s',
testData.argType
testData.argType
)
)
Line 616: Line 616:
end
end


suite["test deregisterMockCurrentTitle: the previously registered current title is not deregistered if a deregistration function is not called"] = function (self)
suite['test deregisterMockCurrentTitle: the previously registered current title is not deregistered if a deregistration function is not called'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 629: Line 629:
end
end


suite["test deregisterMockCurrentTitle: the previously registered current title is deregistered if deregisterMockCurrentTitle is called"] = function (self)
suite['test deregisterMockCurrentTitle: the previously registered current title is deregistered if deregisterMockCurrentTitle is called'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 651: Line 651:
for _, testData in ipairs(registerMockTitlesTestData) do
for _, testData in ipairs(registerMockTitlesTestData) do
suite[string.format(
suite[string.format(
"test registerMockTitles: mocks are registered when specified using %s",
'test registerMockTitles: mocks are registered when specified using %s',
testData.argType
testData.argType
)] = function (self)
)] = function (self)
Line 676: Line 676:
local deregisterMockTitlesTestData = {
local deregisterMockTitlesTestData = {
{
{
argType = "title",
argType = 'title',
args = {MOCK_WP_SANDBOX_ARGS.title, MOCK_MAIN_PAGE_ARGS.title},
args = {MOCK_WP_SANDBOX_ARGS.title, MOCK_MAIN_PAGE_ARGS.title},
},
},
{argType = "args table", args = {MOCK_WP_SANDBOX_ARGS, MOCK_MAIN_PAGE_ARGS}},
{argType = 'args table', args = {MOCK_WP_SANDBOX_ARGS, MOCK_MAIN_PAGE_ARGS}},
{argType = "ID", args = {MOCK_WP_SANDBOX.id, MOCK_MAIN_PAGE.id}},
{argType = 'ID', args = {MOCK_WP_SANDBOX.id, MOCK_MAIN_PAGE.id}},
{
{
argType = "title object",
argType = 'title object',
args = {
args = {
mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
Line 688: Line 688:
},
},
},
},
{argType = "mock title object", args = {MOCK_WP_SANDBOX, MOCK_MAIN_PAGE}},
{argType = 'mock title object', args = {MOCK_WP_SANDBOX, MOCK_MAIN_PAGE}},
}
}


Line 694: Line 694:
suite[
suite[
string.format(
string.format(
"test deregisterMockTitle: previously registered titles are deregistered when specified by %s",
'test deregisterMockTitle: previously registered titles are deregistered when specified by %s',
testData.argType
testData.argType
)
)
Line 721: Line 721:
end
end


suite["test clearMockTitleRegistry: all mock titles are cleared"] = function (self)
suite['test clearMockTitleRegistry: all mock titles are cleared'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 744: Line 744:
end
end


suite["test clearAllMockTitles: all registered titles are deregistered when clearAllMockTitles is called"] = function (self)
suite['test clearAllMockTitles: all registered titles are deregistered when clearAllMockTitles is called'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 769: Line 769:
end
end


suite["test registered mocks do not share content with standalone mocks when title constructors are patched"] = function (self)
suite['test registered mocks do not share content with standalone mocks when title constructors are patched'] = function (self)
mMockTitle.registerMockTitle{title = 'Wikipedia:Sandbox', content = 'Foo'}
mMockTitle.registerMockTitle{title = 'Wikipedia:Sandbox', content = 'Foo'}
mMockTitle.patchTitleConstructors(function ()
mMockTitle.patchTitleConstructors(function ()
Line 786: Line 786:
local patchingFunctionTestArgs = {
local patchingFunctionTestArgs = {
{
{
patchFuncName = "patchTitleNew",
patchFuncName = 'patchTitleNew',
constructorName = "new",
constructorName = 'new',
constructorArgs = {'Wikipedia:Sandbox'},
constructorArgs = {'Wikipedia:Sandbox'},
expectedMockTitle = MOCK_WP_SANDBOX,
expectedMockTitle = MOCK_WP_SANDBOX,
Line 793: Line 793:
},
},
{
{
patchFuncName = "patchMakeTitle",
patchFuncName = 'patchMakeTitle',
constructorName = "makeTitle",
constructorName = 'makeTitle',
constructorArgs = {4, 'Sandbox'},
constructorArgs = {4, 'Sandbox'},
expectedMockTitle = MOCK_WP_SANDBOX,
expectedMockTitle = MOCK_WP_SANDBOX,
Line 800: Line 800:
},
},
{
{
patchFuncName = "patchGetCurrentTitle",
patchFuncName = 'patchGetCurrentTitle',
constructorName = "getCurrentTitle",
constructorName = 'getCurrentTitle',
constructorArgs = {},
constructorArgs = {},
expectedMockTitle = MOCK_WP_SANDBOX,
expectedMockTitle = MOCK_WP_SANDBOX,
Line 811: Line 811:
suite[
suite[
string.format(
string.format(
"test %s: %s is patched when the func parameter is run",
'test %s: %s is patched when the func parameter is run',
testData.patchFuncName,
testData.patchFuncName,
testData.constructorName
testData.constructorName
Line 828: Line 828:
suite[
suite[
string.format(
string.format(
"test %s: %s is not patched before the function is run",
'test %s: %s is not patched before the function is run',
testData.patchFuncName,
testData.patchFuncName,
testData.constructorName
testData.constructorName
Line 845: Line 845:
suite[
suite[
string.format(
string.format(
"test %s: %s is not patched after the function is run",
'test %s: %s is not patched after the function is run',
testData.patchFuncName,
testData.patchFuncName,
testData.constructorName
testData.constructorName
Line 866: Line 866:
end
end


suite["test patchTitleConstructors: all title constructors are patched when the func parameter is run"] = function (self)
suite['test patchTitleConstructors: all title constructors are patched when the func parameter is run'] = function (self)
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
mMockTitle.patchTitleConstructors(function ()
mMockTitle.patchTitleConstructors(function ()
Line 882: Line 882:
end
end


suite["test patchTitleConstructors: title constructors are not patched after the function is run"] = function (self)
suite['test patchTitleConstructors: title constructors are not patched after the function is run'] = function (self)
-- Setup
-- Setup
self:setUpDeregistrationTests()
self:setUpDeregistrationTests()
Line 905: Line 905:


local patchingFuncNames = {
local patchingFuncNames = {
"patchTitleNew",
'patchTitleNew',
"patchMakeTitle",
'patchMakeTitle',
"patchGetCurrentTitle",
'patchGetCurrentTitle',
"patchTitleConstructors",
'patchTitleConstructors',
}
}


for _, patchFuncName in ipairs(patchingFuncNames) do
for _, patchFuncName in ipairs(patchingFuncNames) do
suite[string.format("test %s: varargs are passed as arguments to the func parameter", patchFuncName)] = function (self)
suite[string.format('test %s: varargs are passed as arguments to the func parameter', patchFuncName)] = function (self)
mMockTitle[patchFuncName](
mMockTitle[patchFuncName](
function (a, b, c)
function (a, b, c)
self:assertEquals("foo", a)
self:assertEquals('foo', a)
self:assertEquals("bar", b)
self:assertEquals('bar', b)
self:assertEquals("baz", c)
self:assertEquals('baz', c)
end,
end,
"foo",
'foo',
"bar",
'bar',
"baz"
'baz'
)
)
end
end
suite[string.format("test %s: the result of func is returned", patchFuncName)] = function (self)
suite[string.format('test %s: the result of func is returned', patchFuncName)] = function (self)
local result = mMockTitle[patchFuncName](function ()
local result = mMockTitle[patchFuncName](function ()
return "some value"
return 'some value'
end)
end)
self:assertEquals("some value", result)
self:assertEquals('some value', result)
end
end
end
end

Revision as of 13:21, 23 August 2023

local mMockTitle = require('Module:Mock title')
local ScribuntoUnit = require('Module:ScribuntoUnit')

local suite = ScribuntoUnit:new()

--------------------------------------------------------------------------------
-- Custom assertion methods
--------------------------------------------------------------------------------

function suite:assertTitlesAreEqual(expected, actual)
	local properties = {
		'prefixedText',
		'id',
		'exists',
		'isRedirect',
		'redirectTarget',
		'contentModel',
	}
	for _, prop in ipairs(properties) do
		self:assertEquals(
			expected[prop],
			actual[prop],
			string.format('mismatch in property "%s"', prop)
		)
	end
	
	local deepProperties = {'protectionLevels', 'cascadingProtection'}
	for _, prop in ipairs(deepProperties) do
		self:assertDeepEquals(
			expected[prop],
			actual[prop],
			string.format('mismatch in property "%s"', prop)
		)
	end
	
	self:assertEquals(
		not not expected.file,
		not not actual.file,
		string.format(
			'expected %s file property; actual %s file property',
			expected.file and 'has' or 'has no',
			actual.file and 'has' or 'has no'
		)
	)
	
	if expected.file and actual.file then
		local fileProperties = {
			'exists',
			'width',
			'height',
			'size',
			'mimeType',
			'length',
		}
		for _, prop in ipairs(fileProperties) do
			self:assertEquals(
				expected.file[prop],
				actual.file[prop],
				string.format('mismatch in property "file.%s"', prop)
			)
		end
		
		self:assertDeepEquals(
			expected.file.pages,
			actual.file.pages,
			string.format('mismatch in property "%s"', prop)
		)
	end
	
	self:assertEquals(
		expected:getContent(),
		actual:getContent(),
		'mismatch in getContent()'
	)
end

function suite:assertTitlesAreNotEqual(expected, actual)
	local success, result = pcall(self.assertTitlesAreEqual, self, expected, actual)
	if success then
		self:fail(string.format(
			'Failed to assert that the specified titles are not equal (title: %s)',
			tostring(expected)
		))
	end
end

--------------------------------------------------------------------------------
-- MockTitle tests
--------------------------------------------------------------------------------

suite['test MockTitle: when no argument table is supplied, an error is raised'] = function (self)
	self:assertThrows(
		function () mMockTitle.MockTitle() end,
		"bad argument #1 to 'MockTitle' (table expected, got nil)"
	)
end

suite['test MockTitle: when no title argument is supplied, an error is raised'] = function (self)
	self:assertThrows(
		function () mMockTitle.MockTitle{} end,
		"bad named argument title to 'MockTitle' (string or number expected, got nil)"
	)
end

local pageNames = {
	'Example',
	'fr:Example',
	'Module:Sandbox',
	'Module:Sandbox/subpage',
	'mw:Test',
	'fr:b:Example',
}

for _, pageName in ipairs(pageNames) do
	suite[
		string.format(
			'test MockTitle: when using page name "%s", the prefixedText property equals the page name',
			pageName
		)
	] = function (self)
		self:assertEquals(pageName, mMockTitle.MockTitle({title = pageName}).prefixedText)
	end
end

local simplePropertyTestData = {
	{
		property = 'id',
		title = 'Example',
		value = 123456,
	},
	{
		property = 'isRedirect',
		title = 'Main Page',
		value = true,
	},
	{
		property = 'exists',
		title = 'Main Page',
		value = false,
	},
	{
		property = 'contentModel',
		title = 'Main Page',
		value = 'foo',
	},
}

for _, testData in ipairs(simplePropertyTestData) do
	suite[string.format('test MockTitle: property %s is mocked using the %s option', testData.property, testData.property)] = function (self)
		local title = mMockTitle.MockTitle({title = testData.title, [testData.property] = testData.value})
		self:assertEquals(testData.value, title[testData.property])
	end
end

suite['test MockTitle: passing a page name to the redirectTarget option causes redirectTarget to be a MockTitle object with that page name'] = function (self)
	local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = 'User:Example'})
	self:assertEquals('User:Example', title.redirectTarget.prefixedText)
end

suite['test MockTitle: passing an ID to the redirectTarget option causes redirectTarget to be a MockTitle object with that ID'] = function (self)
	local mainPageId = 15580374
	local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = mainPageId})
	self:assertEquals(mainPageId, title.redirectTarget.id)
end

suite['test MockTitle: passing an options table to the redirectTarget option causes redirectTarget to be a MockTitle object with those options'] = function (self)
	local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = {title = 'User:Example/common.js', contentModel = 'wikitext'}})
	self:assertEquals('User:Example/common.js', title.redirectTarget.prefixedText)
	self:assertEquals('wikitext', title.redirectTarget.contentModel)
end

suite['test MockTitle: passing a MockTitle object to the redirectTarget option causes redirectTarget to be that MockTitle object'] = function (self)
	local mockRedirectTarget = mMockTitle.MockTitle({title = 'User:Example/common.js', contentModel = 'wikitext'})
	local title = mMockTitle.MockTitle({title = 'Example', redirectTarget = mockRedirectTarget})
	self:assertEquals('User:Example/common.js', title.redirectTarget.prefixedText)
	self:assertEquals('wikitext', title.redirectTarget.contentModel)
end

local protectionLevelTestData = {
	{
		optionName = 'editProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'edit',
	},
	{
		optionName = 'moveProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'move',
	},
	{
		optionName = 'createProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'create',
	},
	{
		optionName = 'uploadProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'upload',
	},
}

for _, testData in ipairs(protectionLevelTestData) do
	suite[
		string.format(
			'test MockTitle: when setting option %s to "%s", the protectionLevels table is updated accordingly',
			testData.optionName,
			testData.optionValue
		)
	] = function (self)
		local title = mMockTitle.MockTitle{title = 'Example', [testData.optionName] = testData.optionValue}
		self:assertEquals(testData.optionValue, title.protectionLevels[testData.expectedProtectionAction][1])
	end
end

local cascadingProtectionLevelTestData = {
	{
		optionName = 'cascadingEditProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'edit',
	},
	{
		optionName = 'cascadingMoveProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'move',
	},
	{
		optionName = 'cascadingCreateProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'create',
	},
	{
		optionName = 'cascadingUploadProtection',
		optionValue = 'sysop',
		expectedProtectionAction = 'upload',
	},
}

for _, testData in ipairs(cascadingProtectionLevelTestData) do
	suite[
		string.format(
			'test MockTitle: when setting option %s to "%s", '
			.. 'and when setting the cascadingProtectionSources option, '
			.. 'the cascadingProtection table is updated accordingly',
			testData.optionName,
			testData.optionValue
		)
	] = function (self)
		local title = mMockTitle.MockTitle{
			title = 'Example',
			[testData.optionName] = testData.optionValue,
			cascadingProtectionSources = {'Example', 'Example 2'},
		}
		self:assertEquals(
			testData.optionValue,
			title.cascadingProtection.restrictions[testData.expectedProtectionAction][1]
		)
		self:assertDeepEquals(
			{'Example', 'Example 2'},
			title.cascadingProtection.sources
		)
	end
end

suite['test MockTitle: when a cascading protection argument is given, but no cascading protection sources are given, an error is raised'] = function (self)
	self:assertThrows(
		function () mMockTitle.MockTitle{title = 'Example', cascadingEditProtection = 'sysop'} end,
		'a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty'
	)
end

suite['test MockTitle: when a cascading protection argument is given, but the cascading protection sources table is empty, an error is raised'] = function (self)
	self:assertThrows(
		function () mMockTitle.MockTitle{title = 'Example', cascadingEditProtection = 'sysop', cascadingProtectionSources = {}} end,
		'a cascading protection argument was given but the cascadingProtectionSources argument was missing or empty'
	)
end

suite['test MockTitle: when cascading protection sources are given, but no cascading protection argument is given, an error is raised'] = function (self)
	self:assertThrows(
		function () mMockTitle.MockTitle{title = 'Example', cascadingProtectionSources = {'Example 2', 'Example 3'}} end,
		'the cascadingProtectionSources argument was given, but no cascading protection argument was given'
	)
end

suite['test MockTitle: if the content option is provided, getContent() returns the specified string'] = function (self)
	local title = mMockTitle.MockTitle{title = 'Non-existent page 29dh12yxm', content = 'some [[wikitext]] content'}
	self:assertEquals('some [[wikitext]] content', title:getContent())
end

local filePropertyTestData = {
	{
		property = 'exists',
		optionName = 'fileExists',
		optionValue = true,
	},
	{
		property = 'width',
		optionName = 'fileWidth',
		optionValue = 123,
	},
	{
		property = 'height',
		optionName = 'fileHeight',
		optionValue = 456,
	},
	{
		property = 'pages',
		optionName = 'filePages',
		optionValue =  {{height = 800, width = 600}, {height = 800, width = 600}},
	},
	{
		property = 'size',
		optionName = 'fileSize',
		optionValue = 1024,
	},
	{
		property = 'mimeType',
		optionName = 'fileMimeType',
		optionValue = 'image/png',
	},
	{
		property = 'length',
		optionName = 'fileLength',
		optionValue = 60,
	},
}

for _, testData in ipairs(filePropertyTestData) do
	suite[string.format(
			'test MockTitle: when setting option %s to "%s", the file table is updated accordingly',
			testData.optionName,
			tostring(testData.optionValue)
	)] = function(self)
		local title = mMockTitle.MockTitle{title = 'File:Non-existent hf893bc0.png', [testData.optionName] = testData.optionValue}
		self:assertEquals(testData.optionValue, title.file[testData.property])
	end
end

suite['test MockTitle: when setting fileExists in a non-file namespace, no file table is set'] = function(self)
	local title = mMockTitle.MockTitle{title = 'Non-existent page 34u8wg90bfr', fileExists = true}
	self:assertEquals(nil, title.file)
end

local fileExistsTestData = {
	{
		title = 'Non-existent article tr9w78ebna0',
		expected = nil,
	},
	{
		title = 'Talk:Non-existent talk page 34hdbe0pafj',
		expected = nil,
	},
	{
		title = 	'File:Non-existent file 341gh87fgg8',
		expected = true,
	},
	{
		title = 'Media:Non-existent file pbfhrw3v8d',
		expected = true,
	},
}

for _, testData in ipairs(fileExistsTestData) do
	suite[
		string.format(
			'test MockTitle: when using page "%s", the fileExists property is %s',
			testData.title,
			testData.expected and 'set' or 'not set'
		)
	] = function (self)
		local title = mMockTitle.MockTitle({title = testData.title, fileExists = true})
		self:assertEquals(testData.expected, title.fileExists)
	end
end

local fallbackPropertyTestData = {
	{
		property = 'id',
		option = 'id',
		title = 'Example',
	},
	{
		property = 'isRedirect',
		option = 'isRedirect',
		title = 'WP:ANI',
	},
	{
		property = 'exists',
		option = 'exists',
		title = 'Non-existent title f292umz0tyi',
	},
	{
		property = 'contentModel',
		option = 'contentModel',
		title = 'User:Example/common.js',
	},
	{
		property = 'redirectTarget',
		option = 'redirectTarget',
		title = 'WP:ANI',
	},
	{
		property = 'protectionLevels',
		option = 'editProtection',
		title = 'Main Page',
	},
	{
		property = 'redirectTarget',
		option = 'redirectTarget',
		title = 'WP:ANI',
	},
	{
		property = 'cascadingProtection',
		option = 'cascadingEditProtection',
		title = 'Main Page',
	},
	{
		property = 'cascadingProtection',
		option = 'cascadingProtectionSources',
		title = 'Main Page',
	},
}

for _, testData in ipairs(fallbackPropertyTestData) do
	suite[string.format('test MockTitle: if no %s option is specified, the real %s value is used', testData.option, testData.property)] = function (self)
		suite:assertDeepEquals(
			mw.title.new(testData.title)[testData.property],
			mMockTitle.MockTitle{title = testData.title}[testData.property]
		)
	end
end

suite['test MockTitle: if no content option is specified, getContent() returns the the real content'] = function (self)
	suite:assertEquals(
		mw.title.new('Main Page'):getContent(),
		mMockTitle.MockTitle{title = 'Main Page'}:getContent()
	)
end

suite['test MockTitle: the content property is not shared between mock objects'] = function (self)
	suite:assertNotEquals(
		mMockTitle.MockTitle{title = 'Foo', content = 'Bar'}:getContent(),
		mMockTitle.MockTitle{title = 'Foo', content = 'Baz'}:getContent()
	)
end

--------------------------------------------------------------------------------
-- patchTitleObject tests
--------------------------------------------------------------------------------

suite['test patchTitleObject: patchTitleObject returns an equivalent object to MockTitle when used on the same title'] = function (self)
	suite:assertTitlesAreEqual(
		mMockTitle.MockTitle{
			title = 'Main Page',
			isRedirect = true,
			redirectTarget = 'Wikipedia:Village stocks',
			content = '#REDIRECT: [[Wikipedia:Village stocks]]',
		},
		mMockTitle.patchTitleObject(
			mw.title.new('Main Page'),
			{
				isRedirect = true,
				redirectTarget = 'Wikipedia:Village stocks',
				content = '#REDIRECT: [[Wikipedia:Village stocks]]',
			}
		)
	)
end

suite['test patchTitleObject: title arguments are ignored'] = function (self)
	self:assertEquals(
		'User:Example',
		mMockTitle.patchTitleObject(
			mw.title.new('User:Example'),
			{title = 'Wikipedia:Sandbox'}
		).prefixedText
	)
end

--------------------------------------------------------------------------------
-- Tests for registering/deregistering mock titles
--------------------------------------------------------------------------------

local MOCK_WP_SANDBOX_ARGS = {
	title = 'Wikipedia:Sandbox',
	content = 'Some random content: zbS8mJ31l8eY',
	isRedirect = true -- Cannot be true for a real page with the above content, so we can prove it's a mock
}
local MOCK_WP_SANDBOX = mMockTitle.MockTitle(MOCK_WP_SANDBOX_ARGS)

local MOCK_MAIN_PAGE_ARGS = {
	title = 'Main Page',
	content = 'Mock main page content: 38fgbhqwu8adb',
}
local MOCK_MAIN_PAGE = mMockTitle.MockTitle(MOCK_MAIN_PAGE_ARGS)

function suite:tearDownRegistrationTests()
	mMockTitle.clearAllMockTitles()
end

suite['test registerMockTitle: real titles are used during patching when no mocks are registered'] = function (self)
	-- Setup
	local realTitle = mw.title.new('Wikipedia:Sandbox')

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			realTitle
		)
	end)
	
	-- Teardown
	self:tearDownRegistrationTests()
end

local registerMockTitleTestData = {
	{argType = 'option table', arg = MOCK_WP_SANDBOX_ARGS},
	{argType = 'mock title', arg = MOCK_WP_SANDBOX},
}

for _, testData in ipairs(registerMockTitleTestData) do
	suite[string.format(
		'test registerMockTitle: mocks are registered when specified using %s',
		testData.argType
	)] = function (self)
		-- Setup
		mMockTitle.registerMockTitle(testData.arg)

		-- Test
		mMockTitle.patchTitleConstructors(function ()
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
				MOCK_WP_SANDBOX
			)
		end)
		
		-- Teardown
		self:tearDownRegistrationTests()
	end
end

for _, testData in ipairs(registerMockTitleTestData) do
	suite[string.format(
		'test registerMockCurrentTitle: mocks are registered when specified using %s',
		testData.argType
	)] = function (self)
		-- Setup
		mMockTitle.registerMockCurrentTitle(testData.arg)

		-- Test
		mMockTitle.patchTitleConstructors(function ()
			self:assertTitlesAreEqual(
				mw.title.getCurrentTitle(),
				MOCK_WP_SANDBOX
			)
		end)
		
		-- Teardown
		self:tearDownRegistrationTests()
	end
end

function suite:setUpDeregistrationTests()
	mMockTitle.registerMockTitles(MOCK_WP_SANDBOX_ARGS,	MOCK_MAIN_PAGE_ARGS)
	mMockTitle.registerMockCurrentTitle(MOCK_WP_SANDBOX_ARGS)
end

suite['test deregisterMockTitle: previously registered titles are not deregistered if a deregistration function is not called'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			MOCK_WP_SANDBOX
		)
	end)

	-- Teardown
	self:tearDownRegistrationTests()
end

local deregisterMockTitleTestData = {
	{argType = 'title', arg = MOCK_WP_SANDBOX_ARGS.title},
	{argType = 'args table', arg = MOCK_WP_SANDBOX_ARGS},
	{argType = 'ID', arg = MOCK_WP_SANDBOX.id},
	{argType = 'title object', arg = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)},
	{argType = 'mock title object', arg = MOCK_WP_SANDBOX},
}

for _, testData in ipairs(deregisterMockTitleTestData) do
	suite[
		string.format(
			'test deregisterMockTitle: previously registered titles are deregistered when specified by %s',
			testData.argType
		)
	] = function (self)
		-- Setup
		self:setUpDeregistrationTests()
		local realTitle = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)
		mMockTitle.deregisterMockTitle(testData.arg)

		-- Test
		mMockTitle.patchTitleConstructors(function ()
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
				realTitle
			)
		end)

		-- Teardown
		self:tearDownRegistrationTests()
	end
end

suite['test deregisterMockCurrentTitle: the previously registered current title is not deregistered if a deregistration function is not called'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(mw.title.getCurrentTitle(), MOCK_WP_SANDBOX)
	end)

	-- Teardown
	self:tearDownRegistrationTests()
end

suite['test deregisterMockCurrentTitle: the previously registered current title is deregistered if deregisterMockCurrentTitle is called'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()
	local realTitle = mw.title.getCurrentTitle()
	mMockTitle.deregisterMockCurrentTitle()

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(mw.title.getCurrentTitle(), realTitle)
	end)

	-- Teardown
	self:tearDownRegistrationTests()
end

local registerMockTitlesTestData = {
	{argType = 'option table', args = {MOCK_WP_SANDBOX_ARGS, MOCK_MAIN_PAGE_ARGS}},
	{argType = 'mock title', args = {MOCK_WP_SANDBOX, MOCK_MAIN_PAGE}},
}

for _, testData in ipairs(registerMockTitlesTestData) do
	suite[string.format(
		'test registerMockTitles: mocks are registered when specified using %s',
		testData.argType
	)] = function (self)
		-- Setup
		mMockTitle.registerMockTitles(unpack(testData.args))

		-- Test
		mMockTitle.patchTitleConstructors(function ()
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
				MOCK_WP_SANDBOX
			)
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
				MOCK_MAIN_PAGE
			)
		end)
		
		-- Teardown
		self:tearDownRegistrationTests()
	end
end

local deregisterMockTitlesTestData = {
	{
		argType = 'title',
		args = {MOCK_WP_SANDBOX_ARGS.title, MOCK_MAIN_PAGE_ARGS.title},
	},
	{argType = 'args table', args = {MOCK_WP_SANDBOX_ARGS, MOCK_MAIN_PAGE_ARGS}},
	{argType = 'ID', args = {MOCK_WP_SANDBOX.id, MOCK_MAIN_PAGE.id}},
	{
		argType = 'title object',
		args = {
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
		},
	},
	{argType = 'mock title object', args = {MOCK_WP_SANDBOX, MOCK_MAIN_PAGE}},
}

for _, testData in ipairs(deregisterMockTitlesTestData) do
	suite[
		string.format(
			'test deregisterMockTitle: previously registered titles are deregistered when specified by %s',
			testData.argType
		)
	] = function (self)
		-- Setup
		self:setUpDeregistrationTests()
		local realSandboxTitle = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)
		local realMainPageTitle = mw.title.new(MOCK_MAIN_PAGE_ARGS.title)
		mMockTitle.deregisterMockTitles(unpack(testData.args))

		-- Test
		mMockTitle.patchTitleConstructors(function ()
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
				realSandboxTitle
			)
			self:assertTitlesAreEqual(
				mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
				realMainPageTitle
			)
		end)

		-- Teardown
		self:tearDownRegistrationTests()
	end
end

suite['test clearMockTitleRegistry: all mock titles are cleared'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()
	local realSandboxTitle = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)
	local realMainPageTitle = mw.title.new(MOCK_MAIN_PAGE_ARGS.title)
	mMockTitle.clearMockTitleRegistry()

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			realSandboxTitle
		)
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
			realMainPageTitle
		)
	end)

	-- Teardown
	self:tearDownRegistrationTests()
end

suite['test clearAllMockTitles: all registered titles are deregistered when clearAllMockTitles is called'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()
	local realSandboxTitle = mw.title.new(MOCK_WP_SANDBOX_ARGS.title)
	local realMainPageTitle = mw.title.new(MOCK_MAIN_PAGE_ARGS.title)
	local realCurrentTitle = mw.title.getCurrentTitle()
	mMockTitle.clearAllMockTitles()

	-- Test
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			realSandboxTitle
		)
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
			realMainPageTitle
		)
		self:assertTitlesAreEqual(mw.title.getCurrentTitle(), realCurrentTitle)
	end)

	-- Teardown
	self:tearDownRegistrationTests()
end

suite['test registered mocks do not share content with standalone mocks when title constructors are patched'] = function (self)
	mMockTitle.registerMockTitle{title = 'Wikipedia:Sandbox', content = 'Foo'}
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreNotEqual(
			mw.title.new('Wikipedia:Sandbox'),
			mMockTitle.MockTitle{title = 'Wikipedia:Sandbox', content = 'Bar'}
		)
	end)
	self:tearDownRegistrationTests()
end

--------------------------------------------------------------------------------
-- Tests for patching functions
--------------------------------------------------------------------------------

local patchingFunctionTestArgs = {
	{
		patchFuncName = 'patchTitleNew',
		constructorName = 'new',
		constructorArgs = {'Wikipedia:Sandbox'},
		expectedMockTitle = MOCK_WP_SANDBOX,
		expectedRealTitle = mw.title.new('Wikipedia:Sandbox'),
	},
	{
		patchFuncName = 'patchMakeTitle',
		constructorName = 'makeTitle',
		constructorArgs = {4, 'Sandbox'},
		expectedMockTitle = MOCK_WP_SANDBOX,
		expectedRealTitle = mw.title.new('Wikipedia:Sandbox'),
	},
	{
		patchFuncName = 'patchGetCurrentTitle',
		constructorName = 'getCurrentTitle',
		constructorArgs = {},
		expectedMockTitle = MOCK_WP_SANDBOX,
		expectedRealTitle = mw.title.getCurrentTitle(),
	},
}

for _, testData in ipairs(patchingFunctionTestArgs) do
	suite[
		string.format(
			'test %s: %s is patched when the func parameter is run',
			testData.patchFuncName,
			testData.constructorName
		)
	] = function (self)
		self:setUpDeregistrationTests()
		mMockTitle[testData.patchFuncName](function ()
			self:assertTitlesAreEqual(
				mw.title[testData.constructorName](unpack(testData.constructorArgs)),
				testData.expectedMockTitle
			)
		end)
		self:tearDownRegistrationTests()
	end

	suite[
		string.format(
			'test %s: %s is not patched before the function is run',
			testData.patchFuncName,
			testData.constructorName
		)
	] = function (self)
		-- Setup
		self:setUpDeregistrationTests()
		local realSandboxTitle = mw.title.new('Wikipedia:Sandbox')
		self:assertTitlesAreEqual(
			mw.title[testData.constructorName](unpack(testData.constructorArgs)),
			testData.expectedRealTitle
		)
		self:tearDownRegistrationTests()
	end

	suite[
		string.format(
			'test %s: %s is not patched after the function is run',
			testData.patchFuncName,
			testData.constructorName
		)
	] = function (self)
		-- Setup
		self:setUpDeregistrationTests()
		local realSandboxTitle = mw.title.new('Wikipedia:Sandbox')
		mMockTitle[testData.patchFuncName](function () end)
		
		-- Test
		self:assertTitlesAreEqual(
			mw.title[testData.constructorName](unpack(testData.constructorArgs)),
			testData.expectedRealTitle
		)
		
		-- Teardown
		self:tearDownRegistrationTests()
	end
end

suite['test patchTitleConstructors: all title constructors are patched when the func parameter is run'] = function (self)
	self:setUpDeregistrationTests()
	mMockTitle.patchTitleConstructors(function ()
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_WP_SANDBOX_ARGS.title),
			MOCK_WP_SANDBOX
		)
		self:assertTitlesAreEqual(
			mw.title.new(MOCK_MAIN_PAGE_ARGS.title),
			MOCK_MAIN_PAGE
		)
		self:assertTitlesAreEqual(mw.title.getCurrentTitle(), MOCK_WP_SANDBOX)
	end)
	self:tearDownRegistrationTests()
end

suite['test patchTitleConstructors: title constructors are not patched after the function is run'] = function (self)
	-- Setup
	self:setUpDeregistrationTests()
	local realSandboxTitle = mw.title.new('Wikipedia:Sandbox')
	local realCurrentTitle = mw.title.getCurrentTitle()
	mMockTitle.patchTitleConstructors(function () end)

	-- Test
	self:assertTitlesAreEqual(
		mw.title.new('Wikipedia:Sandbox'),
		realSandboxTitle
	)
	self:assertTitlesAreEqual(
		mw.title.makeTitle(4, 'Sandbox'),
		realSandboxTitle
	)
	self:assertTitlesAreEqual(mw.title.getCurrentTitle(), realCurrentTitle)

	-- Teardown
	self:tearDownRegistrationTests()
end

local patchingFuncNames = {
	'patchTitleNew',
	'patchMakeTitle',
	'patchGetCurrentTitle',
	'patchTitleConstructors',
}

for _, patchFuncName in ipairs(patchingFuncNames) do
	suite[string.format('test %s: varargs are passed as arguments to the func parameter', patchFuncName)] = function (self)
		mMockTitle[patchFuncName](
			function (a, b, c)
				self:assertEquals('foo', a)
				self:assertEquals('bar', b)
				self:assertEquals('baz', c)
			end,
			'foo',
			'bar',
			'baz'
		)
	end
	
	suite[string.format('test %s: the result of func is returned', patchFuncName)] = function (self)
		local result = mMockTitle[patchFuncName](function ()
			return 'some value'
		end)
		self:assertEquals('some value', result)
	end
end

return suite