Module:Template test case: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(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 |
||
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