Editing Module:Template test case

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. Read the Privacy Policy to learn what information we collect about you and how we use it.

If you log in or create an account, your edits will be attributed to your username, along with other benefits.

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.
  A module for generating test case templates.


  This module incorporates code from the English Wikipedia's "Testcase table"
local mTableTools = require('Module:TableTools')
  module,[1] written by Frietjes [2] with contributions by Mr. Stradivarius [3]
local libraryUtil = require('libraryUtil')
  and Jackmcbarn,[4] and the English Wikipedia's "Testcase rows" module,[5]
local checkType = libraryUtil.checkType
  written by Mr. Stradivarius.


  The "Testcase table" and "Testcase rows" modules are released under the
local TEMPLATE_NAME_MAGIC_WORD = '<TEMPLATE_NAME>'
  CC BY-SA 3.0 License [6] and the GFDL.[7]
local TEMPLATE_NAME_MAGIC_WORD_ESCAPED = TEMPLATE_NAME_MAGIC_WORD:gsub('%p', '%%%0')
 
  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
local yesno = require('Module:Yesno')
 
-- Set constants
local DATA_MODULE = 'Module:Template test case/data'
 
-------------------------------------------------------------------------------
-- Shared methods
-------------------------------------------------------------------------------
 
local function message(self, key, ...)
-- This method is added to classes that need to deal with messages from the
-- config module.
local msg = self.cfg.msg[key]
if select(1, ...) then
return mw.message.newRawMessage(msg, ...):plain()
else
return msg
end
end


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Line 48: Line 13:


local Template = {}
local Template = {}
 
Template.__index = Template
Template.memoizedMethods = {
-- Names of methods to be memoized in each object. This table should only
-- hold methods with no parameters.
getFullPage = true,
getName = true,
makeHeader = true,
getOutput = true
}


function Template.new(invocationObj, options)
function Template.new(invocationObj, options)
local obj = {}
local obj = setmetatable({}, Template)


-- Set input
-- Set input
Line 67: Line 24:
end
end
end
end
obj._invocation = invocationObj
obj.invocation = invocationObj


-- Validate input
-- Validate input
Line 74: Line 31:
end
end


-- Memoize expensive method calls
return obj
local memoFuncs = {}
return setmetatable(obj, {
__index = function (t, key)
if Template.memoizedMethods[key] then
local func = memoFuncs[key]
if not func then
local val = Template[key](t)
func = function () return val end
memoFuncs[key] = func
end
return func
else
return Template[key]
end
end
})
end
end


function Template:getFullPage()
function Template:getFullPage()
if not self.template then
if 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)
local strippedTemplate, hasColon = self.template:gsub('^:', '', 1)
hasColon = hasColon > 0
local ns = strippedTemplate:match('^(.-):')
local ns = strippedTemplate:match('^(.-):')
ns = ns and mw.site.namespaces[ns]
ns = ns and mw.site.namespaces[ns]
Line 110: Line 46:
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 73:
end
end


function Template:makeHeader()
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())
template = self:getName(),
invocation = mw.text.nowiki(invocation)
requireMagicWord = self.requireMagicWord,
}
if format == 'code' then
if format == 'code' then
invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>'
invocation = '<code>' .. invocation .. '</code>'
elseif format == 'kbd' then
elseif format == 'pre' then
invocation = '<kbd>' .. mw.text.nowiki(invocation) .. '</kbd>'
elseif format == 'plain' then
invocation = mw.text.nowiki(invocation)
else
-- Default is pre tags
invocation = mw.text.encode(invocation, '&')
invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>'
invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>'
invocation = mw.getCurrentFrame():preprocess(invocation)
invocation = mw.getCurrentFrame():preprocess(invocation)
Line 160: Line 90:


function Template:getOutput()
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
end


Line 174: Line 99:
local TestCase = {}
local TestCase = {}
TestCase.__index = TestCase
TestCase.__index = TestCase
TestCase.message = message -- add the message method
TestCase.renderMethods = {
-- Keys in this table are values of the "format" option, values are the
-- method for rendering that format.
columns = 'renderColumns',
rows = 'renderRows',
tablerows = 'renderRows',
inline = 'renderInline',
cells = 'renderCells',
default = 'renderDefault'
}


function TestCase.new(invocationObj, options, cfg)
function TestCase.new(invocationObj, options)
local obj = setmetatable({}, TestCase)
local obj = setmetatable({}, TestCase)
obj.cfg = cfg


-- Separate general options from template options. Template options are
-- Validate options
-- numbered, whereas general options are not.
do
local generalOptions, templateOptions = {}, {}
local highestNum = 0
for k, v in pairs(options) do
for k in pairs(options) do
local prefix, num
if type(k) == 'string' then
if type(k) == 'string' then
local num = k:match('([1-9][0-9]*)$')
prefix, num = k:match('^(.-)([1-9][0-9]*)$')
num = tonumber(num)
if num and num > highestNum then
highestNum = num
end
end
end
end
if prefix then
for i = 3, highestNum do
num = tonumber(num)
if not options['template' .. i] then
templateOptions[num] = templateOptions[num] or {}
error(string.format(
templateOptions[num][prefix] = v
"one or more options ending in '%d' were " ..
else
"detected, but no 'template%d' option was found",
generalOptions[k] = v
i, i
), 2)
end
end
end
end
end


-- Set general options
-- Separate general options from options for specific templates
generalOptions.showcode = yesno(generalOptions.showcode)
local templateOptions = mTableTools.numData(options, true)
generalOptions.showheader = yesno(generalOptions.showheader) ~= false
obj.options = templateOptions.other or {}
generalOptions.showcaption = yesno(generalOptions.showcaption) ~= false
generalOptions.collapsible = yesno(generalOptions.collapsible)
generalOptions.notcollapsed = yesno(generalOptions.notcollapsed)
generalOptions.wantdiff = yesno(generalOptions.wantdiff)  
obj.options = generalOptions


-- Preprocess template args
-- Add default template options
for num, t in pairs(templateOptions) do
if t.showtemplate ~= nil then
t.showtemplate = yesno(t.showtemplate)
end
end
 
-- 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[1] = templateOptions[1] or {}
templateOptions[2] = templateOptions[2] 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
if templateOptions[1].template and not templateOptions[2].template then
templateOptions[2].template = templateOptions[1].template ..
templateOptions[2].template = templateOptions[1].template .. '/sandbox'
'/' .. obj.cfg.sandboxSubpage
end
end
if not templateOptions[1].template then
if not templateOptions[1].template then
Line 245: Line 140:
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')
obj.cfg.sandboxSubpage
)
end
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
-- Check for missing template names.
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.
templateOptions = (function (t)
local nums = {}
for num in pairs(t) do
nums[#nums + 1] = num
end
table.sort(nums)
local ret = {}
for i, num in ipairs(nums) do
ret[i] = t[num]
end
return ret
end)(templateOptions)
-- Don't require the __TEMPLATENAME__ magic word for nowiki invocations if
-- there is only one template being output.
if #templateOptions <= 1 then
templateOptions[1].requireMagicWord = false
end
mw.logObject(templateOptions)


-- Make the template objects
-- Make the template objects
obj.templates = {}
obj.templates = {}
for i, options in ipairs(templateOptions) do
for i, t in ipairs(templateOptions) do
table.insert(obj.templates, Template.new(invocationObj, options))
table.insert(obj.templates, Template.new(invocationObj, t))
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
end


Line 318: Line 160:
end
end


function TestCase:templateOutputIsEqual()
function TestCase:renderColumns()
-- Returns a boolean showing whether all of the template outputs are equal.
-- The random parts of strip markers (see [[Help:Strip markers]]) are
-- removed before comparison. This means a strip marker can contain anything
-- and still be treated as equal, but it solves the problem of otherwise
-- identical wikitext not returning as exactly equal.
local function normaliseOutput(obj)
local out = obj:getOutput()
-- Remove the random parts from strip markers.
out = out:gsub('(\127\'"`UNIQ.-)%-%x+%-(QINU`"\'\127)', '%1%2')
return out
end
local firstOutput = normaliseOutput(self.templates[1])
for i = 2, #self.templates do
local output = normaliseOutput(self.templates[i])
if output ~= firstOutput then
return false
end
end
return true
end
 
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')
local root = mw.html.create('table')
if self.options.wantdiff then
root
root
:addClass('mw-collapsible')
:addClass(self.options.class)
if self.options.notcollapsed == false then
:cssText(self.options.style)
root
:tag('caption')
:addClass('mw-collapsed')
: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
end
root
local width = tostring(math.floor(100 / #self.templates)) .. '%'
:css('background-color', 'transparent')
for i, obj in ipairs(self.templates) do
:css('width', '100%')
headingRow
:css('border', 'solid silver 1px')
:tag('tr')
:tag('th')
:tag('th')
:css('background-color', isEqual and 'yellow' or '#90a8ee')
:css('width', width)
:wikitext(title)
:wikitext(obj:makeHeading())
: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%')
:css('border', 'solid silver 1px')
:tag('tr')
:tag('th')
:css('background-color', isEqual and 'lightgreen' or 'yellow')
:wikitext(title)
:done()
:done()
:tag('tr')
:tag('td')
:newline()
:wikitext(s)
:newline()
end
return tostring(root)
end
 
function TestCase:renderColumns()
local root = mw.html.create()
if self.options.showcode then
root
:wikitext(self.templates[1]:getInvocation())
:newline()
end
 
local tableroot = root:tag('table')
 
if self.options.showheader then
-- Caption
if self.options.showcaption then
tableroot
:addClass(self.options.class)
:cssText(self.options.style)
:tag('caption')
:wikitext(self.options.caption or self:message('columns-header'))
end
 
-- Headers
local headerRow = 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.
headerRow: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
headerRow
:tag('th')
:css('width', width)
:wikitext(obj:makeHeader())
end
end
end


-- Row header
-- Row header
local dataRow = tableroot:tag('tr'):css('vertical-align', 'top')
local dataRow = root:tag('tr'):css('vertical-align', 'top')
if self.options.rowheader then
if self.options.rowheader then
dataRow:tag('th')
dataRow:tag('th')
Line 450: Line 191:
-- Template output
-- Template output
for i, obj in ipairs(self.templates) do
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)
end
function TestCase:renderRows()
local root = mw.html.create()
if self.options.showcode then
root
:wikitext(self.templates[1]:getInvocation())
:newline()
end
local tableroot = root:tag('table')
tableroot
:addClass(self.options.class)
:cssText(self.options.style)
if self.options.caption then
tableroot
:tag('caption')
:wikitext(self.options.caption)
end
for _, obj in ipairs(self.templates) do
local dataRow = tableroot:tag('tr')
-- Header
if self.options.showheader then
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:makeHeader())
dataRow = tableroot:tag('tr')
end
end
-- Template output
if self.options.output == 'nowiki+' then
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')
dataRow:tag('td')
:newline()
:newline()
:wikitext(self:getInvocation('code'))
:wikitext(self:getTemplateOutput(obj))
:wikitext(self.options.after)
end
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)
return tostring(root)
end
end
Line 641: Line 202:
function TestCase:renderDefault()
function TestCase:renderDefault()
local ret = {}
local ret = {}
if self.options.showcode then
ret[#ret + 1] = self.templates[1]:getInvocation('code')
ret[#ret + 1] = self.templates[1]:getInvocation()
end
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>'
if self.options.showheader then
ret[#ret + 1] = obj:makeBraceLink()
ret[#ret + 1] = obj:makeHeader()
ret[#ret + 1] = self:getTemplateOutput(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
end
return table.concat(ret, '\n\n')
return table.concat(ret, '\n\n')
Line 661: Line 212:


function TestCase:__tostring()
function TestCase:__tostring()
local methods = {
columns = 'renderColumns',
rows = 'renderRows'
}
local format = self.options.format
local format = self.options.format
local method = format and TestCase.renderMethods[format] or 'renderDefault'
local method = format and methods[format] or 'renderDefault'
local ret = self[method](self)
return self[method](self)
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
end
end


Line 679: Line 227:
local NowikiInvocation = {}
local NowikiInvocation = {}
NowikiInvocation.__index = NowikiInvocation
NowikiInvocation.__index = NowikiInvocation
NowikiInvocation.message = message -- Add the message method


function NowikiInvocation.new(invocation, cfg)
function NowikiInvocation.new(invocation)
local obj = setmetatable({}, NowikiInvocation)
local obj = setmetatable({}, NowikiInvocation)
obj.cfg = cfg
obj.invocation = mw.text.unstrip(invocation)
invocation = mw.text.unstrip(invocation)
-- Decode HTML entities for <, >, and ". This means that HTML entities in
-- the original code must be escaped as e.g. &amp;lt;, which is unfortunate,
-- but it is the best we can do as the distinction between <, >, " and &lt;,
-- &gt;, &quot; is lost during the original nowiki operation.
invocation = invocation:gsub('&lt;', '<')
invocation = invocation:gsub('&gt;', '>')
invocation = invocation:gsub('&quot;', '"')
obj.invocation = invocation
return obj
return obj
end
end


function NowikiInvocation:getInvocation(options)
function NowikiInvocation:getInvocation(template)
local template = options.template:gsub('%%', '%%%%') -- Escape "%" with "%%"
template = template:gsub('%%', '%%%%') -- Escape "%" with "%%"
local invocation, count = self.invocation:gsub(
local invocation, count = self.invocation:gsub(
self.cfg.templateNameMagicWordPattern,
TEMPLATE_NAME_MAGIC_WORD_ESCAPED,
template
template
)
)
if options.requireMagicWord ~= false and count < 1 then
if count < 1 then
error(self:message(
error(string.format(
'nowiki-magic-word-error',
"the template invocation must include '%s' in place " ..
self.cfg.templateNameMagicWord
"of the template name",
TEMPLATE_NAME_MAGIC_WORD
))
))
end
end
Line 711: Line 250:
end
end


function NowikiInvocation:getOutput(options)
function NowikiInvocation:getOutput(template)
local invocation = self:getInvocation(options)
local invocation = self:getInvocation(template)
return mw.getCurrentFrame():preprocess(invocation)
return mw.getCurrentFrame():preprocess(invocation)
end
end
Line 722: Line 261:
local TableInvocation = {}
local TableInvocation = {}
TableInvocation.__index = TableInvocation
TableInvocation.__index = TableInvocation
TableInvocation.message = message -- Add the message method


function TableInvocation.new(invokeArgs, nowikiCode, cfg)
function TableInvocation.new(invokeArgs)
local obj = setmetatable({}, TableInvocation)
local obj = setmetatable({}, TableInvocation)
obj.cfg = cfg
obj.invokeArgs = invokeArgs
obj.invokeArgs = invokeArgs
obj.code = nowikiCode
return obj
return obj
end
end


function TableInvocation:getInvocation(options)
function TableInvocation:getInvocation(template)
if self.code then
return require('Module:Template invocation').invocation(
local nowikiObj = NowikiInvocation.new(self.code, self.cfg)
template,
return nowikiObj:getInvocation(options)
self.invokeArgs
else
)
return require('Module:Template invocation').invocation(
options.template,
self.invokeArgs
)
end
end
end


function TableInvocation:getOutput(options)
function TableInvocation:getOutput(template)
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{
return mw.getCurrentFrame():expandTemplate{
title = options.template,
title = template,
args = self.invokeArgs
args = self.invokeArgs
}
}
Line 758: Line 283:


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Bridge functions
-- Exports
--
-- These functions translate template arguments into forms that can be accepted
-- by the different classes, and return the results.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------


local bridge = {}
-- Table-based exports


function bridge.table(args, cfg)
local function getTableArgs(frame, wrappers)
cfg = cfg or mw.loadData(DATA_MODULE)
return require('Module:Arguments').getArgs(frame, {
wrappers = wrappers,
trim = false,
removeBlanks = false
})
end
 
local p = {}


function p._table(args)
local options, invokeArgs = {}, {}
local options, invokeArgs = {}, {}
for k, v in pairs(args) do
for k, v in pairs(args) do
Line 783: Line 313:
end
end
end
end
 
local invocationObj = TableInvocation.new(invokeArgs)
-- Allow passing a nowiki invocation as an option. While this means users
local testCaseObj = TestCase.new(invocationObj, options)
-- have to pass in the code twice, whitespace is preserved and &lt; etc.
-- will work as intended.
local nowikiCode = options.code
options.code = nil
 
local invocationObj = TableInvocation.new(invokeArgs, nowikiCode, cfg)
local testCaseObj = TestCase.new(invocationObj, options, cfg)
return tostring(testCaseObj)
return tostring(testCaseObj)
end
end


function bridge.nowiki(args, cfg)
function p.table(frame)
cfg = cfg or mw.loadData(DATA_MODULE)
return p._table(getTableArgs(frame, 'Template:Test case from arguments'))
end


local code = args.code or args[1]
function p.columns(frame)
local invocationObj = NowikiInvocation.new(code, cfg)
local args = getTableArgs(frame, 'Template:Testcase table')
args.code = nil
args._format = 'columns'
args[1] = nil
return p._table(args)
-- Assume we want to see the code as we already passed it in.
args.showcode = args.showcode or true
local testCaseObj = TestCase.new(invocationObj, args, cfg)
return tostring(testCaseObj)
end
end


-------------------------------------------------------------------------------
function p.rows(frame)
-- Exports
local args = getTableArgs(frame, 'Template:Testcase rows')
-------------------------------------------------------------------------------
args._format = 'rows'
return p._table(args)
end


local p = {}
-- Nowiki-based exports


function p.main(frame, cfg)
function p._nowiki(args)
cfg = cfg or mw.loadData(DATA_MODULE)
local invocationObj = NowikiInvocation.new(args.invocation)
 
args.invocation = nil
-- Load the wrapper config, if any.
local options = args
local wrapperConfig
local testCaseObj = TestCase.new(invocationObj, options)
if frame.getParent then
return tostring(testCaseObj)
local title = frame:getParent():getTitle()
end
local template = title:gsub(cfg.sandboxSubpagePattern, '')
wrapperConfig = cfg.wrappers[template]
end


-- Work out the function we will call, use it to generate the config for
function p.nowiki(frame)
-- Module:Arguments, and use Module:Arguments to find the arguments passed
local args = require('Module:Arguments').getArgs(frame, {
-- by the user.
wrappers = 'Template:Test case from invocation'
local func = wrapperConfig and wrapperConfig.func or 'table'
local userArgs = require('Module:Arguments').getArgs(frame, {
parentOnly = wrapperConfig,
frameOnly = not wrapperConfig,
trim = func ~= 'table',
removeBlanks = func ~= 'table'
})
})
return p._nowiki(args)
end


-- Get default args and build the args table. User-specified args overwrite
-- Exports for testing
-- default args.
local defaultArgs = wrapperConfig and wrapperConfig.args or {}
local args = {}
for k, v in pairs(defaultArgs) do
args[k] = v
end
for k, v in pairs(userArgs) do
args[k] = v
end
 
return bridge[func](args, cfg)
end


function p._exportClasses() -- For testing
function p._exportClasses()
return {
return {
Template = Template,
TestCase = TestCase,
TestCase = TestCase,
Invocation = Invocation,
NowikiInvocation = NowikiInvocation,
NowikiInvocation = NowikiInvocation,
TableInvocation = TableInvocation
TableInvocation = TableInvocation
Please note that all contributions to Nonbinary Wiki are considered to be released under the Creative Commons Attribution-ShareAlike (see Nonbinary Wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)

Template used on this page: