Module:Template test case: Difference between revisions

m
87 revisions imported from wikipedia:Module:Template_test_case
(add the option defaults before we compress the table to fix the case where the first numbered argument is higher than 2)
m (87 revisions imported from wikipedia:Module:Template_test_case)
 
(43 intermediate revisions by 11 users not shown)
Line 1:
--[[
-- This module provides several methods to generate test cases.
A module for generating test case templates.
 
This module incorporates code from the English Wikipedia's "Testcase table"
module,[1] written by Frietjes [2] with contributions by Mr. Stradivarius [3]
and Jackmcbarn,[4] and the English Wikipedia's "Testcase rows" module,[5]
written by Mr. Stradivarius.
 
The "Testcase table" and "Testcase rows" modules are released under the
CC BY-SA 3.0 License [6] and the GFDL.[7]
 
License: CC BY-SA 3.0 and the GFDL
Author: Mr. Stradivarius
 
[1] https://en.wikipedia.org/wiki/Module:Testcase_table
[2] https://en.wikipedia.org/wiki/User:Frietjes
[3] https://en.wikipedia.org/wiki/User:Mr._Stradivarius
[4] https://en.wikipedia.org/wiki/User:Jackmcbarn
[5] https://en.wikipedia.org/wiki/Module:Testcase_rows
[6] https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License
[7] https://en.wikipedia.org/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License
]]
 
-- Load required modules
Line 33 ⟶ 54:
getFullPage = true,
getName = true,
makeHeadingmakeHeader = true,
getOutput = true
}
Line 73 ⟶ 94:
 
function Template:getFullPage()
if not self.template then
return self.title.prefixedText
elseif self.template:sub(1, 7) == '#invoke' then
return 'Module' .. self.template:sub(8):gsub('|.*', '')
else
local strippedTemplate, hasColon = self.template:gsub('^:', '', 1)
hasColon = hasColon > 0
Line 85 ⟶ 110:
return mw.site.namespaces[10].name .. ':' .. strippedTemplate
end
else
return self.title.prefixedText
end
end
Line 112 ⟶ 135:
end
 
function Template:makeHeadingmakeHeader()
return self.heading or self:makeBraceLink()
end
 
function Template:getInvocation(format)
local invocation = self._invocation:getInvocation(self:getName()){
template = self:getName(),
requireMagicWord = self.requireMagicWord,
}
if format == 'code' then
invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>'
elseif format == 'kbd' then
invocation = '<kbd>' .. mw.text.nowiki(invocation) .. '</kbd>'
elseif format == 'plain' then
invocation = mw.text.nowiki(invocation)
Line 132 ⟶ 160:
 
function Template:getOutput()
local protect = require('Module:Protect')
return self._invocation:getOutput(self:getName())
-- calling self._invocation:getOutput{...}
return protect(self._invocation.getOutput)(self._invocation, {
template = self:getName(),
requireMagicWord = self.requireMagicWord,
})
end
 
Line 148 ⟶ 181:
columns = 'renderColumns',
rows = 'renderRows',
tablerows = 'renderRows',
inline = 'renderInline',
cells = 'renderCells',
default = 'renderDefault'
}
Line 158 ⟶ 194:
-- numbered, whereas general options are not.
local generalOptions, templateOptions = {}, {}
for k, v in pairs(options) do
do
local prefix, num
local optionNum = {} -- a unique key for option numbers inside templateOptions
if type(k) == 'string' then
local rawTemplateOptions = {}
prefix, num = k:match('^(.-)([1-9][0-9]*)$')
for k, v in pairs(options) do
local prefix, num
if type(k) == 'string' then
prefix, num = k:match('^(.-)([1-9][0-9]*)$')
end
if prefix then
num = tonumber(num)
rawTemplateOptions[num] = rawTemplateOptions[num] or {}
rawTemplateOptions[num][prefix] = v
rawTemplateOptions[num][optionNum] = num -- record for use in error messages
else
generalOptions[k] = v
end
end
if prefix then
num = tonumber(num)
templateOptions[num] = templateOptions[num] or {}
templateOptions[num][prefix] = v
else
generalOptions[k] = v
end
end
 
-- AddSet default templategeneral options
generalOptions.showcode = yesno(generalOptions.showcode)
rawTemplateOptions[1] = rawTemplateOptions[1] or {}
generalOptions.showheader = yesno(generalOptions.showheader) ~= false
rawTemplateOptions[2] = rawTemplateOptions[2] or {}
generalOptions.showcaption = yesno(generalOptions.showcaption) ~= false
if rawTemplateOptions[1].template and not rawTemplateOptions[2].template then
generalOptions.collapsible = yesno(generalOptions.collapsible)
rawTemplateOptions[2].template = rawTemplateOptions[1].template ..
generalOptions.notcollapsed = yesno(generalOptions.notcollapsed)
'/' .. obj.cfg.sandboxSubpage
generalOptions.wantdiff = yesno(generalOptions.wantdiff)
obj.options = generalOptions
 
-- Preprocess template args
for num, t in pairs(templateOptions) do
if t.showtemplate ~= nil then
t.showtemplate = yesno(t.showtemplate)
end
end
if not rawTemplateOptions[1].template then
 
rawTemplateOptions[1].title = mw.title.getCurrentTitle().basePageTitle
-- Set up first two template options tables, so that if only the
-- "template3" is specified it isn't made the first template when the
-- the table options array is compressed.
templateOptions[1] = templateOptions[1] or {}
templateOptions[2] = templateOptions[2] or {}
 
-- Allow the "template" option to override the "template1" option for
-- backwards compatibility with [[Module:Testcase table]].
if generalOptions.template then
templateOptions[1].template = generalOptions.template
end
 
-- Add default template options
if templateOptions[1].template and not templateOptions[2].template then
templateOptions[2].template = templateOptions[1].template ..
'/' .. obj.cfg.sandboxSubpage
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(
obj.cfg.sandboxSubpage
)
end
 
-- Remove template options for any templates where the showtemplate
-- argument is false. This prevents any output for that template.
for num, t in pairs(templateOptions) do
if t.showtemplate == false then
templateOptions[num] = nil
end
end
if not rawTemplateOptions[2].template then
 
rawTemplateOptions[2].title = rawTemplateOptions[1].title:subPageTitle(
-- Check for missing template names.
obj.cfg.sandboxSubpage
for num, t in pairs(templateOptions) do
)
if not t.template and not t.title then
error(obj:message(
'missing-template-option-error',
num, num
), 2)
end
end
 
-- Compress templateOptions table so we can iterate over it with ipairs.
-- Remove gaps in the numbered options
templateOptions = (function (t)
local nums = {}
for num in pairs(rawTemplateOptionst) do
nums[#nums + 1] = num
end
table.sort(nums)
local ret = {}
for i, num in ipairs(nums) do
templateOptionsret[i] = rawTemplateOptionst[num]
end
return ret
end)(templateOptions)
 
-- Don't require the __TEMPLATENAME__ magic word for nowiki invocations if
-- Check that there are no missing template options.
-- there is only one template being output.
for i = 3, #templateOptions do -- Defaults have already been added for 1 and 2.
if #templateOptions <= 1 then
local t = templateOptions[i]
templateOptions[1].requireMagicWord = false
if not t.template then
local num = t[optionNum]
error(obj:message(
'missing-template-option-error',
num, num
), 2)
end
end
end
 
mw.logObject(templateOptions)
-- Set general options
generalOptions.showcode = yesno(generalOptions.showcode)
generalOptions.collapsible = yesno(generalOptions.collapsible)
obj.options = generalOptions
 
-- Make the template objects
obj.templates = {}
for i, toptions in ipairs(templateOptions) do
table.insert(obj.templates, Template.new(invocationObj, toptions))
end
 
-- Add tracking categories. At the moment we are only tracking templates
-- that use any "heading" parameters or an "output" parameter.
obj.categories = {}
for k, v in pairs(options) do
if type(k) == 'string' and k:find('heading') then
obj.categories['Test cases using heading parameters'] = true
elseif k == 'output' then
obj.categories['Test cases using output parameter'] = true
end
end
 
Line 246 ⟶ 327:
local out = obj:getOutput()
-- Remove the random parts from strip markers.
out = out:gsub('(%cUNIQ)\127\'"`UNIQ.-)%-%x+%-(QINU%c`"\'\127)', '%1%2')
return out
end
Line 260 ⟶ 341:
 
function TestCase:makeCollapsible(s)
local title = self.options.title or self.templates[1]:makeHeader()
if self.options.titlecode then
title = self.templates[1]:getInvocation('kbd')
end
local isEqual = self:templateOutputIsEqual()
local root = mw.html.create('table')
if self.options.wantdiff then
root
:addClass('mw-collapsible')
if self.options.notcollapsed == false then
root
:addClass('mw-collapsed')
end
root
:css('background-color', 'transparent')
:addClass('collapsible')
:css('width', '100%')
:addClass(isEqual and 'collapsed' or nil)
:css('border', 'solid silver 1px')
:tag('tr')
:tag('th')
:css('background-color', isEqual and 'yellow' or '#90a8ee')
:wikitext(title)
:done()
:done()
:tag('tr')
:tag('td')
:newline()
:wikitext(s)
:newline()
else
root
:addClass('mw-collapsible')
if self.options.notcollapsed == false then
root
:addClass('mw-collapsed')
end
if self.options.notcollapsed ~= true or false then
root
:addClass(isEqual and 'mw-collapsed' or nil)
end
root
:css('background-color', 'transparent')
:css('width', '100%')
Line 271 ⟶ 387:
:tag('th')
:css('background-color', isEqual and 'lightgreen' or 'yellow')
:wikitext(self.options.title or self.templates[1]:makeHeading())
:done()
:done()
:tag('tr')
:tag('td')
:newline()
:wikitext(s)
:newline()
end
return tostring(root)
end
Line 289 ⟶ 408:
 
local tableroot = root:tag('table')
tableroot
:addClass(self.options.class)
:cssText(self.options.style)
:tag('caption')
:wikitext(self.options.caption or self:message('columns-header'))
 
if self.options.showheader then
-- Headings
-- Caption
local headingRow = tableroot:tag('tr')
if self.options.rowheadershowcaption then
tableroot
-- rowheader is correct here. We need to add another th cell if
:addClass(self.options.class)
-- rowheader is set further down, even if heading0 is missing.
headingRow :tag('th'):wikitextcssText(self.options.heading0style)
:tag('caption')
end
:wikitext(self.options.caption or self:message('columns-header'))
local width
end
if #self.templates > 0 then
 
width = tostring(math.floor(100 / #self.templates)) .. '%'
-- Headers
else
local headerRow = tableroot:tag('tr')
width = '100%'
if self.options.rowheader then
end
-- rowheader is correct here. We need to add another th cell if
for i, obj in ipairs(self.templates) do
-- rowheader is set further down, even if heading0 is missing.
headingRow
headerRow:tag('th'):wikitext(self.options.heading0)
end
:css('width', width)
local width
:wikitext(obj:makeHeading())
if #self.templates > 0 then
width = tostring(math.floor(100 / #self.templates)) .. '%'
else
width = '100%'
end
for i, obj in ipairs(self.templates) do
headerRow
:tag('th')
:css('width', width)
:wikitext(obj:makeHeader())
end
end
 
Line 325 ⟶ 450:
-- Template output
for i, obj in ipairs(self.templates) do
if self.options.output == 'nowiki+' then
dataRow:tag('td')
dataRow:newlinetag('td')
:newline()
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.afterbefore)
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.after)
:wikitext('<pre style="white-space: pre-wrap;">')
:wikitext(mw.text.nowiki(self.options.before or ""))
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
:wikitext(mw.text.nowiki(self.options.after or ""))
:wikitext('</pre>')
elseif self.options.output == 'nowiki' then
dataRow:tag('td')
:newline()
:wikitext(mw.text.nowiki(self.options.before or ""))
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
:wikitext(mw.text.nowiki(self.options.after or ""))
else
dataRow:tag('td')
:newline()
:wikitext(self.options.before)
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.after)
end
end
Line 354 ⟶ 499:
 
for _, obj in ipairs(self.templates) do
local dataRow = tableroot:tag('tr')
-- Build the row HTML
tableroot
-- Header
:tag('tr')
if self.options.showheader then
:tag('td')
if self.options.format == 'tablerows' then
dataRow:tag('th')
:attr('scope', 'row')
:css('vertical-align', 'top')
:css('text-align', 'left')
:wikitext(obj:makeHeader())
dataRow:tag('td')
:css('vertical-align', 'top')
:css('padding', '0 1em')
:wikitext('→')
else
dataRow:tag('td')
:css('text-align', 'center')
:css('font-weight', 'bold')
:wikitext(obj:makeHeadingmakeHeader())
dataRow = tableroot:donetag('tr')
:done()end
end
:tag('tr')
:tag('td')
-- Template output
:newline()
if self.options.output == 'nowiki+' then
:wikitext(self:getTemplateOutput(obj))
dataRow:tag('td')
:newline()
:wikitext(self:getTemplateOutput(obj))
:wikitext('<pre style="white-space: pre-wrap;">')
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
:wikitext('</pre>')
elseif self.options.output == 'nowiki' then
dataRow:tag('td')
:newline()
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
else
dataRow:tag('td')
:newline()
:wikitext(self:getTemplateOutput(obj))
end
end
 
return tostring(root)
end
 
function TestCase:renderInline()
local arrow = mw.language.getContentLanguage():getArrow('forwards')
local ret = {}
for i, obj in ipairs(self.templates) do
local line = {}
line[#line + 1] = self.options.prefix or '* '
if self.options.showcode then
line[#line + 1] = obj:getInvocation('code')
line[#line + 1] = ' '
line[#line + 1] = arrow
line[#line + 1] = ' '
end
if self.options.output == 'nowiki+' then
line[#line + 1] = self:getTemplateOutput(obj)
line[#line + 1] = '<pre style="white-space: pre-wrap;">'
line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))
line[#line + 1] = '</pre>'
elseif self.options.output == 'nowiki' then
line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))
else
line[#line + 1] = self:getTemplateOutput(obj)
end
ret[#ret + 1] = table.concat(line)
end
if self.options.addline then
local line = {}
line[#line + 1] = self.options.prefix or '* '
line[#line + 1] = self.options.addline
ret[#ret + 1] = table.concat(line)
end
return table.concat(ret, '\n')
end
 
function TestCase:renderCells()
local root = mw.html.create()
local dataRow = root:tag('tr')
dataRow
:css('vertical-align', 'top')
:addClass(self.options.class)
:cssText(self.options.style)
 
-- Row header
if self.options.rowheader then
dataRow:tag('th')
:attr('scope', 'row')
:newline()
:wikitext(self.options.rowheader or self:message('row-header'))
end
-- Caption
if self.options.showcaption then
dataRow:tag('th')
:attr('scope', 'row')
:newline()
:wikitext(self.options.caption or self:message('columns-header'))
end
 
-- Show code
if self.options.showcode then
dataRow:tag('td')
:newline()
:wikitext(self:getInvocation('code'))
end
 
-- Template output
for i, obj in ipairs(self.templates) do
if self.options.output == 'nowiki+' then
dataRow:tag('td')
:newline()
:wikitext(self.options.before)
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.after)
:wikitext('<pre style="white-space: pre-wrap;">')
:wikitext(mw.text.nowiki(self.options.before or ""))
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
:wikitext(mw.text.nowiki(self.options.after or ""))
:wikitext('</pre>')
elseif self.options.output == 'nowiki' then
dataRow:tag('td')
:newline()
:wikitext(mw.text.nowiki(self.options.before or ""))
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
:wikitext(mw.text.nowiki(self.options.after or ""))
else
dataRow:tag('td')
:newline()
:wikitext(self.options.before)
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.after)
end
end
 
 
return tostring(root)
Line 379 ⟶ 646:
for i, obj in ipairs(self.templates) do
ret[#ret + 1] = '<div style="clear: both;"></div>'
if self.options.showheader then
ret[#ret + 1] = obj:makeBraceLink()
ret[#ret + 1] = selfobj:getTemplateOutputmakeHeader(obj)
end
if self.options.output == 'nowiki+' then
ret[#ret + 1] = self:getTemplateOutput(obj) .. '<pre style="white-space: pre-wrap;">' .. mw.text.nowiki(self:getTemplateOutput(obj)) .. '</pre>'
elseif self.options.output == 'nowiki' then
ret[#ret + 1] = mw.text.nowiki(self:getTemplateOutput(obj))
else
ret[#ret + 1] = self:getTemplateOutput(obj)
end
end
return table.concat(ret, '\n\n')
Line 391 ⟶ 666:
if self.options.collapsible then
ret = self:makeCollapsible(ret)
end
for cat in pairs(self.categories) do
ret = ret .. string.format('[[Category:%s]]', cat)
end
return ret
Line 418 ⟶ 696:
end
 
function NowikiInvocation:getInvocation(templateoptions)
local template = options.template:gsub('%%', '%%%%') -- Escape "%" with "%%"
local invocation, count = self.invocation:gsub(
self.cfg.templateNameMagicWordPattern,
template
)
if options.requireMagicWord ~= false and count < 1 then
error(self:message(
'nowiki-magic-word-error',
Line 433 ⟶ 711:
end
 
function NowikiInvocation:getOutput(templateoptions)
local invocation = self:getInvocation(templateoptions)
return mw.getCurrentFrame():preprocess(invocation)
end
Line 454 ⟶ 732:
end
 
function TableInvocation:getInvocation(templateoptions)
if self.code then
local nowikiObj = NowikiInvocation.new(self.code, self.cfg)
return nowikiObj:getInvocation(templateoptions)
else
return require('Module:Template invocation').invocation(
options.template,
self.invokeArgs
)
Line 466 ⟶ 744:
end
 
function TableInvocation:getOutput(templateoptions)
if (options.template:sub(1, 7) == '#invoke') then
local moduleCall = mw.text.split(options.template, '|', true)
local args = mw.clone(self.invokeArgs)
table.insert(args, 1, moduleCall[2])
return mw.getCurrentFrame():callParserFunction(moduleCall[1], args)
end
return mw.getCurrentFrame():expandTemplate{
title = options.template,
args = self.invokeArgs
}
Line 474 ⟶ 758:
 
-------------------------------------------------------------------------------
-- Bridge functions
-- Exports
--
-- These functions translate template arguments into forms that can be accepted
-- by the different classes, and return the results.
-------------------------------------------------------------------------------
 
local pbridge = {}
 
function pbridge.table(args, cfg)
cfg = cfg or mw.loadData(DATA_MODULE)
 
Line 508 ⟶ 795:
end
 
function pbridge.nowiki(args, cfg)
cfg = cfg or mw.loadData(DATA_MODULE)
 
local invocationObjcode = NowikiInvocation.new(args.code, cfg)or args[1]
local invocationObj = NowikiInvocation.new(code, cfg)
args.code = nil
args[1] = nil
-- Assume we want to see the code as we already passed it in.
args.showcode = args.showcode or true
Line 518 ⟶ 807:
return tostring(testCaseObj)
end
 
-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------
 
local p = {}
 
function p.main(frame, cfg)
Line 552 ⟶ 847:
end
 
return pbridge[func](args, cfg)
end