Module:Template test case: Difference between revisions
(move the logic for finding the options into the exports - it seemed a bit unintuitive to have invocation objects handling argument code as well) |
(make a template class and add code to set template objects in the test case class) |
||
Line 9: | Line 9: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
-- | -- Template class | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
local Template = {} | |||
Template.__index = Template | |||
-- | function Template.new(invocationObj, options, titleCallback) | ||
do | local obj = setmetatable({}, Template) | ||
-- Set input | |||
for k, v in pairs(options or {}) do | |||
obj[k] = v | |||
end | |||
obj.invocation = invocationObj | |||
-- Validate template | |||
if not obj.template then | |||
if titleCallback then | |||
obj.title = titleCallback() | |||
else | else | ||
template | error('no template or title callback specified', 2) | ||
end | end | ||
end | end | ||
return obj | |||
if | end | ||
function Template:getFullPage() | |||
if self.template then | |||
local strippedTemplate, hasColon = self.template:gsub('^:', '', 1) | |||
local ns = strippedTemplate:match('^(.-):') | |||
ns = ns and mw.site.namespaces[ns] | |||
if ns then | |||
return strippedTemplate | |||
elseif hasColon then | |||
return strippedTemplate -- Main namespace | |||
else | |||
return mw.site.namespaces[10].name .. ':' .. strippedTemplate | |||
end | end | ||
else | |||
return self.title.prefixedText | |||
end | end | ||
end | end | ||
function Template:getName() | |||
if self.template then | |||
return self.template | |||
local | else | ||
local | return require('Module:Template invocation').name(self.title) | ||
end | |||
return | end | ||
function Template:makeLink(display) | |||
if display then | |||
return string.format('[[:%s|%s]]', self:getFullPage(), display) | |||
else | |||
return string.format('[[:%s]]', self:getFullPage()) | |||
end | |||
end | |||
function Template:makeBraceLink(display) | |||
display = display or self:getName() | |||
local link = self:makeLink(display) | |||
return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}') | |||
end | |||
function Template:getInvocation(format) | |||
local invocation = self.invocation:getInvocation(self:getName()) | |||
invocation = mw.text.nowiki(invocation) | |||
if format == 'code' then | |||
invocation = '<code>' .. invocation .. '</code>' | |||
elseif format == 'pre' then | |||
invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>' | |||
invocation = mw.getCurrentFrame():preprocess(invocation) | |||
end | |||
return invocation | |||
end | |||
function Template:getOutput() | |||
return self.invocation:getOutput(self:getName()) | |||
end | end | ||
Line 65: | Line 98: | ||
TestCase.__index = TestCase | TestCase.__index = TestCase | ||
function TestCase.new(invocationObj | function TestCase.new(invocationObj, options) | ||
local obj = setmetatable({}, TestCase) | local obj = setmetatable({}, TestCase) | ||
-- Validate options | |||
do | |||
local highestNum = 0 | |||
for k in pairs(options) do | |||
if type(k) == 'string' then | |||
local num = k:match('([1-9][0-9]*)$') | |||
num = tonumber(num) | |||
if num > highestNum then | |||
highestNum = num | |||
end | |||
end | |||
end | |||
for i = 3, highestNum do | |||
if not options['template' .. i] then | |||
error(string.format( | |||
"one or more options ending in '%d' were " .. | |||
"detected, but no 'template%d' option was found", | |||
i, i | |||
), 2) | |||
end | |||
end | |||
end | |||
-- Separate general options from options for specific templates | |||
local templateOptions = mTableTools.numData(options, true) | |||
options = templateOptions.other | |||
templateOptions.other = nil | |||
-- Make the template objects | |||
obj.templates = {} | |||
local function templateTitleCallback() | |||
return mw.title.getCurrentTitle().basePageTitle | |||
end | |||
obj.templates[1] = Template.new( | |||
invocationObj, | |||
templateOptions[1], | |||
templateTitleCallback | |||
) | |||
obj.templates[2] = Template.new( | |||
invocationObj, | |||
options, | |||
function () | |||
return templateTitleCallback():subPageTitle('sandbox') | |||
end | |||
) | |||
for i = 3, #templateOptions do | |||
table.insert(obj.templates, Template.new( | |||
invocationObj, | |||
templateOptions[i] | |||
)) | |||
end | |||
return obj | return obj | ||
end | end | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
Revision as of 14:09, 24 November 2014
Documentation for this module may be created at Module:Template test case/doc
-- This module provides several methods to generate test cases. local mTableTools = require('Module:TableTools') local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local TEMPLATE_NAME_MAGIC_WORD = '<TEMPLATE_NAME>' local TEMPLATE_NAME_MAGIC_WORD_ESCAPED = TEMPLATE_NAME_MAGIC_WORD:gsub('%p', '%%%0') ------------------------------------------------------------------------------- -- Template class ------------------------------------------------------------------------------- local Template = {} Template.__index = Template function Template.new(invocationObj, options, titleCallback) local obj = setmetatable({}, Template) -- Set input for k, v in pairs(options or {}) do obj[k] = v end obj.invocation = invocationObj -- Validate template if not obj.template then if titleCallback then obj.title = titleCallback() else error('no template or title callback specified', 2) end end return obj end function Template:getFullPage() if self.template then local strippedTemplate, hasColon = self.template:gsub('^:', '', 1) local ns = strippedTemplate:match('^(.-):') ns = ns and mw.site.namespaces[ns] if ns then return strippedTemplate elseif hasColon then return strippedTemplate -- Main namespace else return mw.site.namespaces[10].name .. ':' .. strippedTemplate end else return self.title.prefixedText end end function Template:getName() if self.template then return self.template else return require('Module:Template invocation').name(self.title) end end function Template:makeLink(display) if display then return string.format('[[:%s|%s]]', self:getFullPage(), display) else return string.format('[[:%s]]', self:getFullPage()) end end function Template:makeBraceLink(display) display = display or self:getName() local link = self:makeLink(display) return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}') end function Template:getInvocation(format) local invocation = self.invocation:getInvocation(self:getName()) invocation = mw.text.nowiki(invocation) if format == 'code' then invocation = '<code>' .. invocation .. '</code>' elseif format == 'pre' then invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>' invocation = mw.getCurrentFrame():preprocess(invocation) end return invocation end function Template:getOutput() return self.invocation:getOutput(self:getName()) end ------------------------------------------------------------------------------- -- TestCase class ------------------------------------------------------------------------------- local TestCase = {} TestCase.__index = TestCase function TestCase.new(invocationObj, options) local obj = setmetatable({}, TestCase) -- Validate options do local highestNum = 0 for k in pairs(options) do if type(k) == 'string' then local num = k:match('([1-9][0-9]*)$') num = tonumber(num) if num > highestNum then highestNum = num end end end for i = 3, highestNum do if not options['template' .. i] then error(string.format( "one or more options ending in '%d' were " .. "detected, but no 'template%d' option was found", i, i ), 2) end end end -- Separate general options from options for specific templates local templateOptions = mTableTools.numData(options, true) options = templateOptions.other templateOptions.other = nil -- Make the template objects obj.templates = {} local function templateTitleCallback() return mw.title.getCurrentTitle().basePageTitle end obj.templates[1] = Template.new( invocationObj, templateOptions[1], templateTitleCallback ) obj.templates[2] = Template.new( invocationObj, options, function () return templateTitleCallback():subPageTitle('sandbox') end ) for i = 3, #templateOptions do table.insert(obj.templates, Template.new( invocationObj, templateOptions[i] )) end return obj end ------------------------------------------------------------------------------- -- Nowiki invocation class ------------------------------------------------------------------------------- local NowikiInvocation = {} NowikiInvocation.__index = NowikiInvocation function NowikiInvocation.new(invocation) local obj = setmetatable({}, NowikiInvocation) obj.invocation = mw.text.unstrip(invocation) return obj end function NowikiInvocation:getInvocation(template) template = template:gsub('%%', '%%%%') -- Escape a % with %% local invocation, count = self.invocation:gsub( TEMPLATE_NAME_MAGIC_WORD_ESCAPED, template ) if count < 1 then error(string.format( "the template invocation must include '%s' in place " .. "of the template name", TEMPLATE_NAME_MAGIC_WORD )) end return invocation end function NowikiInvocation:getOutput(template) local invocation = self:getInvocation(template) return mw.getCurrentFrame():preprocess(invocation) end ------------------------------------------------------------------------------- -- Table invocation class ------------------------------------------------------------------------------- local TableInvocation = {} TableInvocation.__index = TableInvocation function TableInvocation.new(invokeArgs) local obj = setmetatable({}, TableInvocation) obj.invokeArgs = invokeArgs return obj end function TableInvocation:getInvocation(template) return require('Module:Template invocation').invocation( template, self.invokeArgs ) end function TableInvocation:getOutput(template) return mw.getCurrentFrame():expandTemplate{ title = template, args = self.invokeArgs } end ------------------------------------------------------------------------------- -- Exports ------------------------------------------------------------------------------- -- Table-based exports local function getTableArgs(frame, wrappers) return require('Module:Arguments').getArgs(frame, { wrappers = wrappers, trim = false, removeBlanks = false }) end local p = {} function p._table(args) local options, invokeArgs = {}, {} for k, v in pairs(args) do local optionKey = type(k) == 'string' and k:match('^_(.*)$') if optionKey then if type(v) == 'string' then v = v:match('^%s*(.-)%s*$') -- trim whitespace end if v ~= '' then options[optionKey] = v end else invokeArgs[k] = v end end local invocation = TableInvocation.new(invokeArgs) return invocation end function p.table(frame) return p._table(getTableArgs(frame, 'Template:Test case from arguments')) end function p.columns(frame) local args = getTableArgs(frame, 'Template:Testcase table') args._format = 'columns' return p._table(args) end function p.rows(frame) local args = getTableArgs(frame, 'Template:Testcase rows') args._format = 'rows' return p._table(args) end -- Nowiki-based exports function p._nowiki(args) local invocation = NowikiInvocation.new(args.invocation) args.invocation = nil local options = args return invocation end function p.nowiki(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:Test case from invocation' }) return p._nowiki(args) end -- Exports for testing function p._exportClasses() return { TestCase = TestCase, Invocation = Invocation, NowikiInvocation = NowikiInvocation, TableInvocation = TableInvocation } end return p