Editing Module:Template test case
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 1: | ||
-- | -- This module provides several methods to generate test cases. | ||
-- Load required modules | -- Load required modules | ||
local yesno = require('Module:Yesno') | local yesno = require('Module:Yesno') | ||
local mTableTools = require('Module:TableTools') | |||
-- Set constants | -- Set constants | ||
local DATA_MODULE = 'Module:Template test case/data' | local DATA_MODULE = 'Module:Template test case/data' | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
Line 54: | Line 19: | ||
getFullPage = true, | getFullPage = true, | ||
getName = true, | getName = true, | ||
makeHeading = true, | |||
getOutput = true | getOutput = true | ||
} | } | ||
Line 94: | Line 59: | ||
function Template:getFullPage() | function Template:getFullPage() | ||
if | if self.template then | ||
local strippedTemplate, hasColon = self.template:gsub('^:', '', 1) | local strippedTemplate, hasColon = self.template:gsub('^:', '', 1) | ||
hasColon = hasColon > 0 | hasColon = hasColon > 0 | ||
Line 110: | Line 71: | ||
return mw.site.namespaces[10].name .. ':' .. strippedTemplate | return mw.site.namespaces[10].name .. ':' .. strippedTemplate | ||
end | end | ||
else | |||
return self.title.prefixedText | |||
end | end | ||
end | end | ||
Line 135: | Line 98: | ||
end | end | ||
function Template: | function Template:makeHeading() | ||
return self.heading or self:makeBraceLink() | return self.heading or self:makeBraceLink() | ||
end | end | ||
function Template:getInvocation(format) | function Template:getInvocation(format) | ||
local invocation = self._invocation:getInvocation | local invocation = self._invocation:getInvocation(self:getName()) | ||
if format == 'code' then | if format == 'code' then | ||
invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>' | invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>' | ||
elseif format == 'plain' then | elseif format == 'plain' then | ||
invocation = mw.text.nowiki(invocation) | invocation = mw.text.nowiki(invocation) | ||
Line 160: | Line 118: | ||
function Template:getOutput() | function Template:getOutput() | ||
return self._invocation:getOutput(self:getName()) | |||
end | end | ||
Line 174: | Line 127: | ||
local TestCase = {} | local TestCase = {} | ||
TestCase.__index = TestCase | TestCase.__index = TestCase | ||
function TestCase.new(invocationObj, options, cfg) | function TestCase.new(invocationObj, options, cfg) | ||
Line 191: | Line 132: | ||
obj.cfg = cfg | obj.cfg = cfg | ||
-- | -- 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 | end | ||
if | 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 | ||
end | end | ||
-- | -- Separate general options from options for specific templates | ||
local templateOptions = mTableTools.numData(options, true) | |||
obj.options = templateOptions.other or {} | |||
obj.options = | |||
-- | -- Normalize boolean options | ||
obj.options.showcode = yesno(obj.options.showcode) | |||
obj.options.collapsible = yesno(obj.options.collapsible) | |||
-- | -- Add default template options | ||
templateOptions[1] = templateOptions[1] or {} | templateOptions[1] = templateOptions[1] or {} | ||
templateOptions[2] = templateOptions[2] or {} | templateOptions[2] = templateOptions[2] or {} | ||
if templateOptions[1].template and not templateOptions[2].template then | if templateOptions[1].template and not templateOptions[2].template then | ||
templateOptions[2].template = templateOptions[1].template .. | templateOptions[2].template = templateOptions[1].template .. '/sandbox' | ||
end | end | ||
if not templateOptions[1].template then | if not templateOptions[1].template then | ||
Line 245: | Line 173: | ||
end | end | ||
if not templateOptions[2].template then | if not templateOptions[2].template then | ||
templateOptions[2].title = templateOptions[1].title:subPageTitle( | templateOptions[2].title = templateOptions[1].title:subPageTitle('sandbox') | ||
end | end | ||
-- Make the template objects | -- Make the template objects | ||
obj.templates = {} | obj.templates = {} | ||
for i, | for i, t in ipairs(templateOptions) do | ||
table.insert(obj.templates, Template.new(invocationObj, | table.insert(obj.templates, Template.new(invocationObj, t)) | ||
end | end | ||
Line 327: | Line 202: | ||
local out = obj:getOutput() | local out = obj:getOutput() | ||
-- Remove the random parts from strip markers. | -- Remove the random parts from strip markers. | ||
out = out:gsub('( | out = out:gsub('(%cUNIQ).-(QINU%c)', '%1%2') | ||
return out | return out | ||
end | end | ||
Line 341: | Line 216: | ||
function TestCase:makeCollapsible(s) | function TestCase:makeCollapsible(s) | ||
local isEqual = self:templateOutputIsEqual() | local isEqual = self:templateOutputIsEqual() | ||
local root = mw.html.create('table') | local root = mw.html.create('table') | ||
root | root | ||
:addClass(' | :addClass('collapsible') | ||
:addClass(isEqual and 'collapsed' or nil) | |||
:css('background-color', 'transparent') | :css('background-color', 'transparent') | ||
:css('width', '100%') | :css('width', '100%') | ||
Line 387: | Line 227: | ||
:tag('th') | :tag('th') | ||
:css('background-color', isEqual and 'lightgreen' or 'yellow') | :css('background-color', isEqual and 'lightgreen' or 'yellow') | ||
:wikitext(title) | :wikitext(self.options.title or self.templates[1]:makeHeading()) | ||
:done() | :done() | ||
:done() | :done() | ||
:tag('tr') | :tag('tr') | ||
:tag('td') | :tag('td') | ||
:wikitext(s) | :wikitext(s) | ||
return tostring(root) | return tostring(root) | ||
end | end | ||
Line 408: | Line 245: | ||
local tableroot = root:tag('table') | local tableroot = root:tag('table') | ||
tableroot | |||
:addClass(self.options.class) | |||
:cssText(self.options.style) | |||
:tag('caption') | |||
:wikitext(self.options.caption or 'Side by side comparison') | |||
-- Headings | |||
local headingRow = tableroot:tag('tr') | |||
if self.options.rowheader then | |||
-- rowheader is correct here. We need to add another th cell if | |||
-- rowheader is set further down, even if heading0 is missing. | |||
headingRow:tag('th'):wikitext(self.options.heading0) | |||
end | |||
local width | |||
if #self.templates > 0 then | |||
width = tostring(math.floor(100 / #self.templates)) .. '%' | |||
else | |||
width = '100%' | |||
end | |||
for i, obj in ipairs(self.templates) do | |||
headingRow | |||
:tag('th') | |||
:css('width', width) | |||
:wikitext(obj:makeHeading()) | |||
end | end | ||
Line 450: | Line 281: | ||
-- Template output | -- Template output | ||
for i, obj in ipairs(self.templates) do | for i, obj in ipairs(self.templates) do | ||
dataRow:tag('td') | |||
:newline() | |||
:wikitext(self:getTemplateOutput(obj)) | |||
:wikitext(self.options.after) | |||
end | end | ||
Line 499: | Line 310: | ||
for _, obj in ipairs(self.templates) do | for _, obj in ipairs(self.templates) do | ||
-- Build the row HTML | |||
tableroot | |||
-- | :tag('tr') | ||
:tag('td') | |||
:css('text-align', 'center') | :css('text-align', 'center') | ||
:css('font-weight', 'bold') | :css('font-weight', 'bold') | ||
:wikitext(obj: | :wikitext(obj:makeHeading()) | ||
:done() | |||
:done() | |||
:tag('tr') | |||
:tag('td') | |||
:newline() | |||
:wikitext(self:getTemplateOutput(obj)) | |||
: | |||
end | end | ||
return tostring(root) | return tostring(root) | ||
Line 646: | Line 335: | ||
for i, obj in ipairs(self.templates) do | for i, obj in ipairs(self.templates) do | ||
ret[#ret + 1] = '<div style="clear: both;"></div>' | ret[#ret + 1] = '<div style="clear: both;"></div>' | ||
ret[#ret + 1] = obj:makeBraceLink() | |||
ret[#ret + 1] = self:getTemplateOutput(obj) | |||
end | end | ||
return table.concat(ret, '\n\n') | return table.concat(ret, '\n\n') | ||
Line 661: | Line 342: | ||
function TestCase:__tostring() | function TestCase:__tostring() | ||
local methods = { | |||
collapsed = 'renderCollapsed', | |||
columns = 'renderColumns', | |||
rows = 'renderRows' | |||
} | |||
local format = self.options.format | local format = self.options.format | ||
local method = format and | local method = format and methods[format] or 'renderDefault' | ||
local ret = self[method](self) | local ret = self[method](self) | ||
if self.options.collapsible then | if self.options.collapsible then | ||
ret = self:makeCollapsible(ret) | ret = self:makeCollapsible(ret) | ||
end | end | ||
return ret | return ret | ||
Line 679: | Line 362: | ||
local NowikiInvocation = {} | local NowikiInvocation = {} | ||
NowikiInvocation.__index = NowikiInvocation | NowikiInvocation.__index = NowikiInvocation | ||
function NowikiInvocation.new(invocation, cfg) | function NowikiInvocation.new(invocation, cfg) | ||
Line 696: | Line 378: | ||
end | end | ||
function NowikiInvocation:getInvocation( | function NowikiInvocation:getInvocation(template) | ||
template = template:gsub('%%', '%%%%') -- Escape "%" with "%%" | |||
local invocation, count = self.invocation:gsub( | local invocation, count = self.invocation:gsub( | ||
self.cfg.templateNameMagicWordPattern, | self.cfg.templateNameMagicWordPattern, | ||
template | template | ||
) | ) | ||
if | if count < 1 then | ||
error( | error(string.format( | ||
' | "the template invocation must include '%s' in place " .. | ||
"of the template name", | |||
self.cfg.templateNameMagicWord | self.cfg.templateNameMagicWord | ||
)) | )) | ||
Line 711: | Line 394: | ||
end | end | ||
function NowikiInvocation:getOutput( | function NowikiInvocation:getOutput(template) | ||
local invocation = self:getInvocation( | local invocation = self:getInvocation(template) | ||
return mw.getCurrentFrame():preprocess(invocation) | return mw.getCurrentFrame():preprocess(invocation) | ||
end | end | ||
Line 722: | Line 405: | ||
local TableInvocation = {} | local TableInvocation = {} | ||
TableInvocation.__index = TableInvocation | TableInvocation.__index = TableInvocation | ||
function TableInvocation.new(invokeArgs, nowikiCode, cfg) | function TableInvocation.new(invokeArgs, nowikiCode, cfg) | ||
Line 732: | Line 414: | ||
end | end | ||
function TableInvocation:getInvocation( | function TableInvocation:getInvocation(template) | ||
if self.code then | if self.code then | ||
local nowikiObj = NowikiInvocation | local nowikiObj = NowikiInvocation(self.code, self.cfg) | ||
return nowikiObj:getInvocation( | return nowikiObj:getInvocation(template) | ||
else | else | ||
return require('Module:Template invocation').invocation( | return require('Module:Template invocation').invocation( | ||
template, | |||
self.invokeArgs | self.invokeArgs | ||
) | ) | ||
Line 744: | Line 426: | ||
end | end | ||
function TableInvocation:getOutput( | function TableInvocation:getOutput(template) | ||
return mw.getCurrentFrame():expandTemplate{ | return mw.getCurrentFrame():expandTemplate{ | ||
title = | title = template, | ||
args = self.invokeArgs | args = self.invokeArgs | ||
} | } | ||
Line 758: | Line 434: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
-- | -- Exports | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
local | local p = {} | ||
function | function p.table(args, cfg) | ||
cfg = cfg or mw.loadData(DATA_MODULE) | cfg = cfg or mw.loadData(DATA_MODULE) | ||
Line 795: | Line 468: | ||
end | end | ||
function | function p.nowiki(args, cfg) | ||
cfg = cfg or mw.loadData(DATA_MODULE) | cfg = cfg or mw.loadData(DATA_MODULE) | ||
local invocationObj = NowikiInvocation.new(args.code, cfg) | |||
local invocationObj = NowikiInvocation.new(code, cfg) | |||
args.code = nil | args.code = nil | ||
-- Assume we want to see the code as we already passed it in. | -- Assume we want to see the code as we already passed it in. | ||
args.showcode = args.showcode or true | args.showcode = args.showcode or true | ||
Line 807: | Line 478: | ||
return tostring(testCaseObj) | return tostring(testCaseObj) | ||
end | end | ||
function p.main(frame, cfg) | function p.main(frame, cfg) | ||
Line 847: | Line 512: | ||
end | end | ||
return | return p[func](args, cfg) | ||
end | end | ||