Module:Template test case
- Afrikaans
- العربية
- Azərbaycanca
- تۆرکجه
- Basa Bali
- বাংলা
- Bikol Central
- Corsu
- Cymraeg
- Dansk
- Ελληνικά
- فارسی
- Gaeilge
- ગુજરાતી
- 한국어
- हिन्दी
- Hrvatski
- Ilokano
- Bahasa Indonesia
- Íslenska
- ಕನ್ನಡ
- Kurdî
- Ladin
- Latina
- Latviešu
- Lietuvių
- मैथिली
- Македонски
- മലയാളം
- मराठी
- Bahasa Melayu
- Мокшень
- Монгол
- မြန်မာဘာသာ
- नेपाली
- 日本語
- Norsk bokmål
- ଓଡ଼ିଆ
- Oʻzbekcha / ўзбекча
- ਪੰਜਾਬੀ
- Pangasinan
- Papiamentu
- پښتو
- Polski
- Português
- Română
- Русский
- Shqip
- සිංහල
- Simple English
- سنڌي
- Slovenščina
- Српски / srpski
- Svenska
- Tagalog
- தமிழ்
- Татарча / tatarça
- တႆး
- ไทย
- Türkçe
- Українська
- Tiếng Việt
- 粵語
- 中文
This module provides a framework for making templates which produce a template test case. While test cases can be made manually, using Lua-based templates such as the ones provided by this module has the advantage that the template arguments only need to be input once, thus reducing the effort involved in making test cases and reducing the possibility of errors in the input.
Usage
This module should not usually be called directly. Instead, you should use one of the following templates:
Parameter-based templates:
- Template:Test case – for standard test cases
- Template:Testcase table – for test cases arranged side by side in columns
- Template:Testcase rows – for test cases arranged as rows in a table
- Template:Collapsible test case – for test cases that are collapsed by default if the results are the same
- Template:Inline test case – for test cases with small invocations and small output, that do not contain any line breaks
The only difference between these templates is their default arguments. For example, it is possible to display test cases side by side in Template:Testcase rows by specifying |_format=columns
Nowiki-based templates:
- Template:Test case nowiki – for test cases created from template code wrapped in nowiki tags (useful for displaying complex template invocations)
- Template:Nowiki template demo – for use in template documentation
It is also possible to use a format of {{#invoke:template test case|main|parameters}}. This uses the same defaults as Template:Test case; please see that page for documentation of the parameters.
There is no direct interface to this module for other Lua modules. Lua modules should generally use Lua-based test case modules such as Module:UnitTests or Module:ScribuntoUnit. If it is really necessary to use this module, you can use frame:expandTemplate with one of the templates listed above.
Configuration
This module has a configuration module at Module:Template test case/config. You can edit it to add new wrapper templates, or to change the messages that the module outputs.
Tracking categories
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
--[[ 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 localyesno=require('Module:Yesno') -- Set constants localDATA_MODULE='Module:Template test case/data' ------------------------------------------------------------------------------- -- Shared methods ------------------------------------------------------------------------------- localfunctionmessage(self,key,...) -- This method is added to classes that need to deal with messages from the -- config module. localmsg=self.cfg.msg[key] ifselect(1,...)then returnmw.message.newRawMessage(msg,...):plain() else returnmsg end end ------------------------------------------------------------------------------- -- Template class ------------------------------------------------------------------------------- localTemplate={} 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 } functionTemplate.new(invocationObj,options) localobj={} -- Set input fork,vinpairs(optionsor{})do ifnotTemplate[k]then obj[k]=v end end obj._invocation=invocationObj -- Validate input ifnotobj.templateandnotobj.titlethen error('no template or title specified',2) end -- Memoize expensive method calls localmemoFuncs={} returnsetmetatable(obj,{ __index=function(t,key) ifTemplate.memoizedMethods[key]then localfunc=memoFuncs[key] ifnotfuncthen localval=Template[key](t) func=function()returnvalend memoFuncs[key]=func end returnfunc else returnTemplate[key] end end }) end functionTemplate:getFullPage() ifnotself.templatethen returnself.title.prefixedText elseifself.template:sub(1,7)=='#invoke'then return'Module'..self.template:sub(8):gsub('|.*','') else localstrippedTemplate,hasColon=self.template:gsub('^:','',1) hasColon=hasColon>0 localns=strippedTemplate:match('^(.-):') ns=nsandmw.site.namespaces[ns] ifnsthen returnstrippedTemplate elseifhasColonthen returnstrippedTemplate-- Main namespace else returnmw.site.namespaces[10].name..':'..strippedTemplate end end end functionTemplate:getName() ifself.templatethen returnself.template else returnrequire('Module:Template invocation').name(self.title) end end functionTemplate:makeLink(display) ifdisplaythen returnstring.format('[[:%s|%s]]',self:getFullPage(),display) else returnstring.format('[[:%s]]',self:getFullPage()) end end functionTemplate:makeBraceLink(display) display=displayorself:getName() locallink=self:makeLink(display) returnmw.text.nowiki('{{')..link..mw.text.nowiki('}}') end functionTemplate:makeHeader() returnself.headingorself:makeBraceLink() end functionTemplate:getInvocation(format) localinvocation=self._invocation:getInvocation{ template=self:getName(), requireMagicWord=self.requireMagicWord, } ifformat=='code'then invocation='<code>'..mw.text.nowiki(invocation)..'</code>' elseifformat=='kbd'then invocation='<kbd>'..mw.text.nowiki(invocation)..'</kbd>' elseifformat=='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=mw.getCurrentFrame():preprocess(invocation) end returninvocation end functionTemplate:getOutput() localprotect=require('Module:Protect') -- calling self._invocation:getOutput{...} returnprotect(self._invocation.getOutput)(self._invocation,{ template=self:getName(), requireMagicWord=self.requireMagicWord, }) end ------------------------------------------------------------------------------- -- TestCase class ------------------------------------------------------------------------------- localTestCase={} 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' } functionTestCase.new(invocationObj,options,cfg) localobj=setmetatable({},TestCase) obj.cfg=cfg -- Separate general options from template options. Template options are -- numbered, whereas general options are not. localgeneralOptions,templateOptions={},{} fork,vinpairs(options)do localprefix,num iftype(k)=='string'then prefix,num=k:match('^(.-)([1-9][0-9]*)$') end ifprefixthen num=tonumber(num) templateOptions[num]=templateOptions[num]or{} templateOptions[num][prefix]=v else generalOptions[k]=v end end -- Set general options generalOptions.showcode=yesno(generalOptions.showcode) generalOptions.showheader=yesno(generalOptions.showheader)~=false 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 fornum,tinpairs(templateOptions)do ift.showtemplate~=nilthen 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[2]=templateOptions[2]or{} -- Allow the "template" option to override the "template1" option for -- backwards compatibility with [[Module:Testcase table]]. ifgeneralOptions.templatethen templateOptions[1].template=generalOptions.template end -- Add default template options iftemplateOptions[1].templateandnottemplateOptions[2].templatethen templateOptions[2].template=templateOptions[1].template.. '/'..obj.cfg.sandboxSubpage end ifnottemplateOptions[1].templatethen templateOptions[1].title=mw.title.getCurrentTitle().basePageTitle end ifnottemplateOptions[2].templatethen 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. fornum,tinpairs(templateOptions)do ift.showtemplate==falsethen templateOptions[num]=nil end end -- Check for missing template names. fornum,tinpairs(templateOptions)do ifnott.templateandnott.titlethen 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) localnums={} fornuminpairs(t)do nums[#nums+1]=num end table.sort(nums) localret={} fori,numinipairs(nums)do ret[i]=t[num] end returnret end)(templateOptions) -- Don't require the __TEMPLATENAME__ magic word for nowiki invocations if -- there is only one template being output. if#templateOptions<=1then templateOptions[1].requireMagicWord=false end mw.logObject(templateOptions) -- Make the template objects obj.templates={} fori,optionsinipairs(templateOptions)do table.insert(obj.templates,Template.new(invocationObj,options)) end -- Add tracking categories. At the moment we are only tracking templates -- that use any "heading" parameters or an "output" parameter. obj.categories={} fork,vinpairs(options)do iftype(k)=='string'andk:find('heading')then obj.categories['Test cases using heading parameters']=true elseifk=='output'then obj.categories['Test cases using output parameter']=true end end returnobj end functionTestCase:getTemplateOutput(templateObj) localoutput=templateObj:getOutput() ifself.options.resetRefsthen mw.getCurrentFrame():extensionTag('references') end returnoutput end functionTestCase:templateOutputIsEqual() -- 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. localfunctionnormaliseOutput(obj) localout=obj:getOutput() -- Remove the random parts from strip markers. out=out:gsub('(127円[^127円]*UNIQ%-%-%l+%-)%x+(%-%-?QINU[^127円]*127円)','%1%2') returnout end localfirstOutput=normaliseOutput(self.templates[1]) fori=2,#self.templatesdo localoutput=normaliseOutput(self.templates[i]) ifoutput~=firstOutputthen returnfalse end end returntrue end functionTestCase:makeCollapsible(s) localtitle=self.options.titleorself.templates[1]:makeHeader() ifself.options.titlecodethen title=self.templates[1]:getInvocation('kbd') end localisEqual=self:templateOutputIsEqual() localroot=mw.html.create('div') root :wikitext(mw.getCurrentFrame():extensionTag{ name='templatestyles', args={src='Module:Template test case/styles.css'}, }) :addClass('mw-collapsible') :addClass('test-case-collapsible') :addClass(self.options.notcollapsed==falseand'mw-collapsed'ornil) ifself.options.wantdiffthen root :tag('div') :addClass(isEqualand'test-case-collapsible-b1'or'test-case-collapsible-b2') :wikitext(title) :done() else ifself.options.notcollapsed~=trueorfalsethen root :addClass(isEqualand'mw-collapsed'ornil) end root :tag('div') :addClass(isEqualand'test-case-collapsible-b3'or'test-case-collapsible-b1') :wikitext(title) :done() end root :tag('div') :addClass('mw-collapsible-content') :newline() :wikitext(s) :newline() returntostring(root) end functionTestCase:renderColumns() localroot=mw.html.create() ifself.options.showcodethen root :wikitext(self.templates[1]:getInvocation()) :newline() end localtableroot=root:tag('table') ifself.options.showheaderthen -- Caption ifself.options.showcaptionthen tableroot :addClass(self.options.class) :cssText(self.options.style) :tag('caption') :wikitext(self.options.captionorself:message('columns-header')) end -- Headers localheaderRow=tableroot:tag('tr') ifself.options.rowheaderthen -- 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 localwidth if#self.templates>0then width=tostring(math.floor(100/#self.templates))..'%' else width='100%' end fori,objinipairs(self.templates)do headerRow :tag('th') :css('width',width) :wikitext(obj:makeHeader()) end end -- Row header localdataRow=tableroot:tag('tr'):css('vertical-align','top') ifself.options.rowheaderthen dataRow:tag('th') :attr('scope','row') :wikitext(self.options.rowheader) end -- Template output fori,objinipairs(self.templates)do ifself.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.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) :wikitext('</pre>') elseifself.options.output=='nowiki'then dataRow:tag('td') :newline() :wikitext(mw.text.nowiki(self.options.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) else dataRow:tag('td') :newline() :wikitext(self.options.before) :wikitext(self:getTemplateOutput(obj)) :wikitext(self.options.after) end end returntostring(root) end functionTestCase:renderRows() localroot=mw.html.create() ifself.options.showcodethen root :wikitext(self.templates[1]:getInvocation()) :newline() end localtableroot=root:tag('table') tableroot :addClass(self.options.class) :cssText(self.options.style) ifself.options.captionthen tableroot :tag('caption') :wikitext(self.options.caption) end for_,objinipairs(self.templates)do localdataRow=tableroot:tag('tr') -- Header ifself.options.showheaderthen ifself.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 ifself.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.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) :wikitext('</pre>') elseifself.options.output=='nowiki'then dataRow:tag('td') :newline() :wikitext(mw.text.nowiki(self.options.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) else dataRow:tag('td') :newline() :wikitext(self.options.before) :wikitext(self:getTemplateOutput(obj)) :wikitext(self.options.after) end end returntostring(root) end functionTestCase:renderInline() localarrow=mw.language.getContentLanguage():getArrow('forwards') localret={} fori,objinipairs(self.templates)do localline={} line[#line+1]=self.options.prefixor'* ' ifself.options.showcodethen line[#line+1]=obj:getInvocation('code') line[#line+1]=' ' line[#line+1]=arrow line[#line+1]=' ' end ifself.options.output=='nowiki+'then line[#line+1]=self.options.beforeor"" line[#line+1]=self:getTemplateOutput(obj) line[#line+1]=self.options.afteror"" line[#line+1]='<pre style="white-space: pre-wrap;">' line[#line+1]=mw.text.nowiki(self.options.beforeor"") line[#line+1]=mw.text.nowiki(self:getTemplateOutput(obj)) line[#line+1]=mw.text.nowiki(self.options.afteror"") line[#line+1]='</pre>' elseifself.options.output=='nowiki'then line[#line+1]=mw.text.nowiki(self.options.beforeor"") line[#line+1]=mw.text.nowiki(self:getTemplateOutput(obj)) line[#line+1]=mw.text.nowiki(self.options.afteror"") else line[#line+1]=self.options.beforeor"" line[#line+1]=self:getTemplateOutput(obj) line[#line+1]=self.options.afteror"" end ret[#ret+1]=table.concat(line) end ifself.options.addlinethen localline={} line[#line+1]=self.options.prefixor'* ' line[#line+1]=self.options.addline ret[#ret+1]=table.concat(line) end returntable.concat(ret,'\n') end functionTestCase:renderCells() localroot=mw.html.create() localdataRow=root:tag('tr') dataRow :css('vertical-align','top') :addClass(self.options.class) :cssText(self.options.style) -- Row header ifself.options.rowheaderthen dataRow:tag('th') :attr('scope','row') :newline() :wikitext(self.options.rowheaderorself:message('row-header')) end -- Caption ifself.options.showcaptionthen dataRow:tag('th') :attr('scope','row') :newline() :wikitext(self.options.captionorself:message('columns-header')) end -- Show code ifself.options.showcodethen dataRow:tag('td') :newline() :wikitext(self:getInvocation('code')) end -- Template output fori,objinipairs(self.templates)do ifself.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.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) :wikitext('</pre>') elseifself.options.output=='nowiki'then dataRow:tag('td') :newline() :wikitext(mw.text.nowiki(self.options.beforeor"")) :wikitext(mw.text.nowiki(self:getTemplateOutput(obj))) :wikitext(mw.text.nowiki(self.options.afteror"")) else dataRow:tag('td') :newline() :wikitext(self.options.before) :wikitext(self:getTemplateOutput(obj)) :wikitext(self.options.after) end end returntostring(root) end functionTestCase:renderDefault() localret={} ifself.options.showcodethen ret[#ret+1]=self.templates[1]:getInvocation() end fori,objinipairs(self.templates)do ret[#ret+1]='<div style="clear: both;"></div>' ifself.options.showheaderthen ret[#ret+1]=obj:makeHeader() end ifself.options.output=='nowiki+'then ret[#ret+1]=(self.options.beforeor"").. self:getTemplateOutput(obj).. (self.options.afteror"").. '<pre style="white-space: pre-wrap;">'.. mw.text.nowiki(self.options.beforeor"").. mw.text.nowiki(self:getTemplateOutput(obj)).. mw.text.nowiki(self.options.afteror"")..'</pre>' elseifself.options.output=='nowiki'then ret[#ret+1]=mw.text.nowiki(self.options.beforeor"").. mw.text.nowiki(self:getTemplateOutput(obj)).. mw.text.nowiki(self.options.afteror"") else ret[#ret+1]=(self.options.beforeor"").. self:getTemplateOutput(obj).. (self.options.afteror"") end end returntable.concat(ret,'\n\n') end functionTestCase:__tostring() localformat=self.options.format localmethod=formatandTestCase.renderMethods[format]or'renderDefault' localret=self[method](self) ifself.options.collapsiblethen ret=self:makeCollapsible(ret) end forcatinpairs(self.categories)do ret=ret..string.format('[[Category:%s]]',cat) end returnret end ------------------------------------------------------------------------------- -- Nowiki invocation class ------------------------------------------------------------------------------- localNowikiInvocation={} NowikiInvocation.__index=NowikiInvocation NowikiInvocation.message=message-- Add the message method functionNowikiInvocation.new(invocation,cfg) localobj=setmetatable({},NowikiInvocation) obj.cfg=cfg 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. &lt;, which is unfortunate, -- but it is the best we can do as the distinction between <, >, " and <, -- >, " is lost during the original nowiki operation. invocation=invocation:gsub('<','<') invocation=invocation:gsub('>','>') invocation=invocation:gsub('"','"') obj.invocation=invocation returnobj end functionNowikiInvocation:getInvocation(options) localtemplate=options.template:gsub('%%','%%%%')-- Escape "%" with "%%" localinvocation,count=self.invocation:gsub( self.cfg.templateNameMagicWordPattern, template ) ifoptions.requireMagicWord~=falseandcount<1then error(self:message( 'nowiki-magic-word-error', self.cfg.templateNameMagicWord )) end returninvocation end functionNowikiInvocation:getOutput(options) localinvocation=self:getInvocation(options) returnmw.getCurrentFrame():preprocess(invocation) end ------------------------------------------------------------------------------- -- Table invocation class ------------------------------------------------------------------------------- localTableInvocation={} TableInvocation.__index=TableInvocation TableInvocation.message=message-- Add the message method functionTableInvocation.new(invokeArgs,nowikiCode,cfg) localobj=setmetatable({},TableInvocation) obj.cfg=cfg obj.invokeArgs=invokeArgs obj.code=nowikiCode returnobj end functionTableInvocation:getInvocation(options) ifself.codethen localnowikiObj=NowikiInvocation.new(self.code,self.cfg) returnnowikiObj:getInvocation(options) else returnrequire('Module:Template invocation').invocation( options.template, self.invokeArgs ) end end functionTableInvocation:getOutput(options) if(options.template:sub(1,7)=='#invoke')then localmoduleCall=mw.text.split(options.template,'|',true) localargs=mw.clone(self.invokeArgs) table.insert(args,1,moduleCall[2]) returnmw.getCurrentFrame():callParserFunction(moduleCall[1],args) end returnmw.getCurrentFrame():expandTemplate{ title=options.template, args=self.invokeArgs } end ------------------------------------------------------------------------------- -- Bridge functions -- -- These functions translate template arguments into forms that can be accepted -- by the different classes, and return the results. ------------------------------------------------------------------------------- localbridge={} functionbridge.table(args,cfg) cfg=cfgormw.loadData(DATA_MODULE) localoptions,invokeArgs={},{} fork,vinpairs(args)do localoptionKey=type(k)=='string'andk:match('^_(.*)$') ifoptionKeythen iftype(v)=='string'then v=v:match('^%s*(.-)%s*$')-- trim whitespace end ifv~=''then options[optionKey]=v end else invokeArgs[k]=v end end -- Allow passing a nowiki invocation as an option. While this means users -- have to pass in the code twice, whitespace is preserved and < etc. -- will work as intended. localnowikiCode=options.code options.code=nil localinvocationObj=TableInvocation.new(invokeArgs,nowikiCode,cfg) localtestCaseObj=TestCase.new(invocationObj,options,cfg) returntostring(testCaseObj) end functionbridge.nowiki(args,cfg) cfg=cfgormw.loadData(DATA_MODULE) -- Convert args beginning with _ for consistency with the normal bridge localnewArgs={} fork,vinpairs(args)do localnormalName=type(k)=="string"andstring.match(k,"^_(.*)$") ifnormalNamethen newArgs[normalName]=v else newArgs[k]=v end end localcode=newArgs.codeornewArgs[1] localinvocationObj=NowikiInvocation.new(code,cfg) newArgs.code=nil newArgs[1]=nil -- Assume we want to see the code as we already passed it in. newArgs.showcode=newArgs.showcodeortrue localtestCaseObj=TestCase.new(invocationObj,newArgs,cfg) returntostring(testCaseObj) end ------------------------------------------------------------------------------- -- Exports ------------------------------------------------------------------------------- localp={} functionp.main(frame,cfg) cfg=cfgormw.loadData(DATA_MODULE) -- Load the wrapper config, if any. localwrapperConfig ifframe.getParentthen localtitle=frame:getParent():getTitle() localtemplate=title:gsub(cfg.sandboxSubpagePattern,'') wrapperConfig=cfg.wrappers[template] end -- Work out the function we will call, use it to generate the config for -- Module:Arguments, and use Module:Arguments to find the arguments passed -- by the user. localfunc=wrapperConfigandwrapperConfig.funcor'table' localuserArgs=require('Module:Arguments').getArgs(frame,{ parentOnly=wrapperConfig, frameOnly=notwrapperConfig, trim=func~='table', removeBlanks=func~='table' }) -- Get default args and build the args table. User-specified args overwrite -- default args. localdefaultArgs=wrapperConfigandwrapperConfig.argsor{} localargs={} fork,vinpairs(defaultArgs)do args[k]=v end fork,vinpairs(userArgs)do args[k]=v end returnbridge[func](args,cfg) end functionp._exportClasses()-- For testing return{ Template=Template, TestCase=TestCase, NowikiInvocation=NowikiInvocation, TableInvocation=TableInvocation } end returnp