Module:Template test case: Difference between revisions
(move the logic for finding the options into the exports - it seemed a bit unintuitive to have invocation objects handling argument code as well) |
(make a template class and add code to set template objects in the test case class) |
||
| Line 9: | Line 9: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
-- | -- Template class | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
local Template = {} | |||
Template.__index = Template | |||
-- | function Template.new(invocationObj, options, titleCallback) | ||
do | local obj = setmetatable({}, Template) | ||
-- Set input | |||
for k, v in pairs(options or {}) do | |||
obj[k] = v | |||
end | |||
obj.invocation = invocationObj | |||
-- Validate template | |||
if not obj.template then | |||
if titleCallback then | |||
obj.title = titleCallback() | |||
else | else | ||
template | error('no template or title callback specified', 2) | ||
end | end | ||
end | end | ||
return obj | |||
if | 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 | end | ||
else | |||
return self.title.prefixedText | |||
end | end | ||
end | end | ||
function Template:getName() | |||
if self.template then | |||
return self.template | |||
local | else | ||
local | return require('Module:Template invocation').name(self.title) | ||
end | |||
return | 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: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 | end | ||
| Line 65: | Line 98: | ||
TestCase.__index = TestCase | TestCase.__index = TestCase | ||
function TestCase.new(invocationObj | function TestCase.new(invocationObj, options) | ||
local obj = setmetatable({}, TestCase) | 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 > 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) | |||
options = templateOptions.other | |||
templateOptions.other = nil | |||
-- Make the template objects | |||
obj.templates = {} | |||
local function templateTitleCallback() | |||
return mw.title.getCurrentTitle().basePageTitle | |||
end | |||
obj.templates[1] = Template.new( | |||
invocationObj, | |||
templateOptions[1], | |||
templateTitleCallback | |||
) | |||
obj.templates[2] = Template.new( | |||
invocationObj, | |||
options, | |||
function () | |||
return templateTitleCallback():subPageTitle('sandbox') | |||
end | |||
) | |||
for i = 3, #templateOptions do | |||
table.insert(obj.templates, Template.new( | |||
invocationObj, | |||
templateOptions[i] | |||
)) | |||
end | |||
return obj | return obj | ||
end | end | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
Revision as of 14:09, 24 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, titleCallback)
local obj = setmetatable({}, Template)
-- Set input
for k, v in pairs(options or {}) do
obj[k] = v
end
obj.invocation = invocationObj
-- Validate template
if not obj.template then
if titleCallback then
obj.title = titleCallback()
else
error('no template or title callback specified', 2)
end
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: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 > 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)
options = templateOptions.other
templateOptions.other = nil
-- Make the template objects
obj.templates = {}
local function templateTitleCallback()
return mw.title.getCurrentTitle().basePageTitle
end
obj.templates[1] = Template.new(
invocationObj,
templateOptions[1],
templateTitleCallback
)
obj.templates[2] = Template.new(
invocationObj,
options,
function ()
return templateTitleCallback():subPageTitle('sandbox')
end
)
for i = 3, #templateOptions do
table.insert(obj.templates, Template.new(
invocationObj,
templateOptions[i]
))
end
return obj
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 a % 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 invocation = TableInvocation.new(invokeArgs)
return invocation
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 invocation = NowikiInvocation.new(args.invocation)
args.invocation = nil
local options = args
return invocation
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