Module:Template test case: Difference between revisions
(if the first template is specified but the second is not, make the second the sandbox of the first rather than the sandbox of the base page; simplify the title-getting code in the process) |
(add TestCase:renderColumns and some more supporting methods) |
||
Line 20: | Line 20: | ||
-- Set input | -- Set input | ||
for k, v in pairs(options or {}) do | for k, v in pairs(options or {}) do | ||
obj[k] = v | if not Template[k] then | ||
obj[k] = v | |||
end | |||
end | end | ||
obj.invocation = invocationObj | obj.invocation = invocationObj | ||
Line 69: | Line 71: | ||
local link = self:makeLink(display) | local link = self:makeLink(display) | ||
return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}') | return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}') | ||
end | |||
function Template:makeHeading() | |||
return self.heading or self:makeBraceLink() | |||
end | end | ||
Line 144: | Line 150: | ||
return obj | return obj | ||
end | |||
function TestCase:getTemplateOutput(templateObj) | |||
local output = templateObj:getOutput() | |||
if self.options.resetRefs then | |||
mw.getCurrentFrame():extensionTag('references') | |||
end | |||
return output | |||
end | |||
function TestCase:renderColumns() | |||
local root = mw.html.create('table') | |||
root | |||
:addClass(self.options.class) | |||
:cssText(self.options.style) | |||
:tag('caption') | |||
:wikitext(self.options.caption or 'Side by side comparison') | |||
-- Headings | |||
local headingRow = root:tag('tr') | |||
if self.options.rowheader then | |||
headingRow:tag('th'):wikitext(self.options.heading0) | |||
end | |||
local width = tostring(math.floor(100 / #self.templates)) .. '%' | |||
for i, obj in ipairs(self.templates) do | |||
headingRow | |||
:tag('th') | |||
:css('width', width) | |||
:wikitext(obj:makeHeading()) | |||
end | |||
-- Row header | |||
local dataRow = root:tag('tr'):css('vertical-align', 'top') | |||
if self.options.rowheader then | |||
dataRow:tag('th') | |||
:attr('scope', 'row') | |||
:wikitext(self.options.rowheader) | |||
end | |||
-- Template output | |||
for i, obj in ipairs(self.templates) do | |||
dataRow:tag('td') | |||
:newline() | |||
:wikitext(self:getTemplateOutput(obj)) | |||
:wikitext(self.options.after) | |||
end | |||
return tostring(root) | |||
end | end | ||
Line 152: | Line 206: | ||
ret[#ret + 1] = '<div style="clear: both;"></div>' | ret[#ret + 1] = '<div style="clear: both;"></div>' | ||
ret[#ret + 1] = obj:makeBraceLink() | ret[#ret + 1] = obj:makeBraceLink() | ||
ret[#ret + 1] = | ret[#ret + 1] = self:getTemplateOutput(obj) | ||
end | end | ||
return table.concat(ret, '\n\n') | return table.concat(ret, '\n\n') |
Revision as of 07:06, 25 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) local obj = setmetatable({}, Template) -- Set input for k, v in pairs(options or {}) do if not Template[k] then obj[k] = v end end obj.invocation = invocationObj -- Validate input if not obj.template and not obj.title then error('no template or title specified', 2) 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:makeHeading() return self.heading or self:makeBraceLink() 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 and 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) obj.options = templateOptions.other or {} -- Add default template options templateOptions[1] = templateOptions[1] or {} templateOptions[2] = templateOptions[2] or {} if templateOptions[1].template and not templateOptions[2].template then templateOptions[2].template = templateOptions[1].template .. '/sandbox' end if not templateOptions[1].template then templateOptions[1].title = mw.title.getCurrentTitle().basePageTitle end if not templateOptions[2].template then templateOptions[2].title = templateOptions[1].title:subPageTitle('sandbox') end -- Make the template objects obj.templates = {} for i, t in ipairs(templateOptions) do table.insert(obj.templates, Template.new(invocationObj, t)) end return obj end function TestCase:getTemplateOutput(templateObj) local output = templateObj:getOutput() if self.options.resetRefs then mw.getCurrentFrame():extensionTag('references') end return output end function TestCase:renderColumns() local root = mw.html.create('table') root :addClass(self.options.class) :cssText(self.options.style) :tag('caption') :wikitext(self.options.caption or 'Side by side comparison') -- Headings local headingRow = root:tag('tr') if self.options.rowheader then headingRow:tag('th'):wikitext(self.options.heading0) end local width = tostring(math.floor(100 / #self.templates)) .. '%' for i, obj in ipairs(self.templates) do headingRow :tag('th') :css('width', width) :wikitext(obj:makeHeading()) end -- Row header local dataRow = root:tag('tr'):css('vertical-align', 'top') if self.options.rowheader then dataRow:tag('th') :attr('scope', 'row') :wikitext(self.options.rowheader) end -- Template output for i, obj in ipairs(self.templates) do dataRow:tag('td') :newline() :wikitext(self:getTemplateOutput(obj)) :wikitext(self.options.after) end return tostring(root) end function TestCase:renderDefault() local ret = {} ret[#ret + 1] = self.templates[1]:getInvocation('code') for i, obj in ipairs(self.templates) do ret[#ret + 1] = '<div style="clear: both;"></div>' ret[#ret + 1] = obj:makeBraceLink() ret[#ret + 1] = self:getTemplateOutput(obj) end return table.concat(ret, '\n\n') end function TestCase:__tostring() local methods = { columns = 'renderColumns', rows = 'renderRows' } local format = self.options.format local method = format and methods[format] or 'renderDefault' return self[method](self) 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 "%" 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 invocationObj = TableInvocation.new(invokeArgs) local testCaseObj = TestCase.new(invocationObj, options) return tostring(testCaseObj) 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 invocationObj = NowikiInvocation.new(args.invocation) args.invocation = nil local options = args local testCaseObj = TestCase.new(invocationObj, options) return tostring(testCaseObj) 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