Permanently protected module
From Wikipedia, the free encyclopedia


--[[

   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] /info/en/?search=Module:Testcase_table

   [2] /info/en/?search=User:Frietjes

   [3] /info/en/?search=User:Mr._Stradivarius

   [4] /info/en/?search=User:Jackmcbarn

   [5] /info/en/?search=Module:Testcase_rows

   [6] /info/en/?search=Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License

   [7] /info/en/?search=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.msgkey

	if select(1, ...) then

		return mw.message.newRawMessage(msg, ...):plain()

	else

		return msg

	end

end



-------------------------------------------------------------------------------

-- Template class

-------------------------------------------------------------------------------



local 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)

	local obj = {}



	-- Set input

	for k, v in pairs(options or {}) do

		if not Templatek then

			objk = v

		end

	end

	obj._invocation = invocationObj



	-- Validate input

	if not obj.template and not obj.title then

		error('no template or title specified', 2)

	end



	-- Memoize expensive method calls

	local memoFuncs = {}

	return setmetatable(obj, {

		__index = function (t, key)

			if Template.memoizedMethodskey then

				local func = memoFuncskey

				if not func then

					local val = Templatekey](t)

					func = function () return val end

					memoFuncskey = func

				end

				return func

			else

				return Templatekey

			end

		end

	})

end



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

		local ns = strippedTemplate:match('^(.-):')

		ns = ns and mw.site.namespacesns

		if ns then

			return strippedTemplate

		elseif hasColon then

			return strippedTemplate -- Main namespace

		else

			return mw.site.namespaces10].name .. ':' .. strippedTemplate

		end

	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:makeHeader()

	return self.heading or self:makeBraceLink()

end



function Template:getInvocation(format)

	local invocation = self._invocation:getInvocation{

		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)

	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

	return invocation

end



function Template:getOutput()

	local protect = require('Module:Protect')

	-- calling self._invocation:getOutput{...}

	return protect(self._invocation.getOutput)(self._invocation, {

		template = self:getName(),

		requireMagicWord = self.requireMagicWord,

	})

end



-------------------------------------------------------------------------------

-- TestCase class

-------------------------------------------------------------------------------



local 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)

	local obj = setmetatable({}, TestCase)

	obj.cfg = cfg



	-- Separate general options from template options. Template options are

	-- numbered, whereas general options are not.

	local generalOptions, templateOptions = {}, {}

	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)

			templateOptionsnum = templateOptionsnum or {}

			templateOptionsnum][prefix = v

		else

			generalOptionsk = 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

	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.

	templateOptions1 = templateOptions1 or {}

	templateOptions2 = templateOptions2 or {}



	-- Allow the "template" option to override the "template1" option for

	-- backwards compatibility with [[Module:Testcase table]].

	if generalOptions.template then

		templateOptions1].template = generalOptions.template

	end



	-- Add default template options

	if templateOptions1].template and not templateOptions2].template then

		templateOptions2].template = templateOptions1].template ..

			'/' .. obj.cfg.sandboxSubpage

	end

	if not templateOptions1].template then

		templateOptions1].title = mw.title.getCurrentTitle().basePageTitle

	end

	if not templateOptions2].template then

		templateOptions2].title = templateOptions1].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

			templateOptionsnum = 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

			reti = tnum

		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

		templateOptions1].requireMagicWord = false

	end



	mw.logObject(templateOptions)



	-- Make the template objects

	obj.templates = {}

	for i, options in ipairs(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 = {}

	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



	return obj

end



function TestCase:getTemplateOutput(templateObj)

	local output = templateObj:getOutput()

	if self.options.resetRefs then

		mw.getCurrentFrame():extensionTag('references')

	end

	return output

end



function TestCase: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.

	local function normaliseOutput(obj)

		local out = obj:getOutput()

		-- Remove the random parts from strip markers.

		out = out:gsub('(\127[^\127]*UNIQ%-%-%l+%-)%x+(%-%-?QINU[^\127]*\127)', '%1%2')

		return out

	end

	local firstOutput = normaliseOutput(self.templates1])

	for i = 2, #self.templates do

		local output = normaliseOutput(self.templatesi])

		if output ~= firstOutput then

			return false

		end

	end

	return true

end



function TestCase:makeCollapsible(s)

	local title = self.options.title or self.templates1]:makeHeader()

	if self.options.titlecode then

		title = self.templates1]:getInvocation('kbd')

	end

	local isEqual = self:templateOutputIsEqual()

	local root = mw.html.create('div')

	root

		:addClass('mw-collapsible')

		:css('width', '100%')

		:css('border', 'solid silver 1px')

		:css('padding', '0.2em')

		:css('clear', 'both')

		:addClass(self.options.notcollapsed == false and 'mw-collapsed' or nil)

	if self.options.wantdiff then

		root

			:tag('div')

				:css('background-color', isEqual and 'yellow' or '#90a8ee')

				:css('color', 'black')

				:css('font-weight', 'bold')

				:css('padding', '0.2em')

				:wikitext(title)

				:done()

	else

		if self.options.notcollapsed ~= true or false then

			root

				:addClass(isEqual and 'mw-collapsed' or nil)

		end

		root

			:tag('div')

				:css('background-color', isEqual and 'lightgreen' or 'yellow')

				:css('color', 'black')

				:css('font-weight', 'bold')

				:css('padding', '0.2em')

				:wikitext(title)

				:done()

	end

	root

		:tag('div')

			:addClass('mw-collapsible-content')

			:newline()

			:wikitext(s)

			:newline()

	return tostring(root)

end



function TestCase:renderColumns()

	local root = mw.html.create()

	if self.options.showcode then

		root

			:wikitext(self.templates1]: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



	-- Row header

	local dataRow = tableroot:tag('tr'):css('vertical-align', 'top')

	if self.options.rowheader then

		dataRow:tag('th')

			:attr('scope', 'row')

			:wikitext(self.options.rowheader)

	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)

end



function TestCase:renderRows()

	local root = mw.html.create()

	if self.options.showcode then

		root

			:wikitext(self.templates1]: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.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: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.options.before or ""

			line#line + 1 = self:getTemplateOutput(obj)

			line#line + 1 = self.options.after or ""

			line#line + 1 = '<pre style="white-space: pre-wrap;">'

			line#line + 1 = mw.text.nowiki(self.options.before or "")

			line#line + 1 = mw.text.nowiki(self:getTemplateOutput(obj))

			line#line + 1 = mw.text.nowiki(self.options.after or "")

			line#line + 1 = '</pre>'

		elseif self.options.output == 'nowiki' then

			line#line + 1 = mw.text.nowiki(self.options.before or "")

			line#line + 1 = mw.text.nowiki(self:getTemplateOutput(obj))

			line#line + 1 = mw.text.nowiki(self.options.after or "")

		else

			line#line + 1 = self.options.before or ""

			line#line + 1 = self:getTemplateOutput(obj)

			line#line + 1 = self.options.after or ""

		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)

end



function TestCase:renderDefault()

	local ret = {}

	if self.options.showcode then

		ret#ret + 1 = self.templates1]:getInvocation()

	end

	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:makeHeader()

		end

		if self.options.output == 'nowiki+' then

			ret#ret + 1 = (self.options.before or "") ..

			self:getTemplateOutput(obj) ..

			(self.options.after or "") ..

			'<pre style="white-space: pre-wrap;">' ..

			mw.text.nowiki(self.options.before or "") ..

			mw.text.nowiki(self:getTemplateOutput(obj)) ..

			mw.text.nowiki(self.options.after or "") .. '</pre>'

		elseif self.options.output == 'nowiki' then

			ret#ret + 1 = mw.text.nowiki(self.options.before or "") ..

			mw.text.nowiki(self:getTemplateOutput(obj)) ..

			mw.text.nowiki(self.options.after or "")

		else

			ret#ret + 1 = (self.options.before or "") ..

			self:getTemplateOutput(obj) ..

			(self.options.after or "")

		end

	end

	return table.concat(ret, '\n\n')

end



function TestCase:__tostring()

	local format = self.options.format

	local method = format and TestCase.renderMethodsformat or 'renderDefault'

	local ret = selfmethod](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



-------------------------------------------------------------------------------

-- Nowiki invocation class

-------------------------------------------------------------------------------



local NowikiInvocation = {}

NowikiInvocation.__index = NowikiInvocation

NowikiInvocation.message = message -- Add the message method



function NowikiInvocation.new(invocation, cfg)

	local obj = 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. &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

end



function NowikiInvocation:getInvocation(options)

	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',

			self.cfg.templateNameMagicWord

		))

	end

	return invocation

end



function NowikiInvocation:getOutput(options)

	local invocation = self:getInvocation(options)

	return mw.getCurrentFrame():preprocess(invocation)

end



-------------------------------------------------------------------------------

-- Table invocation class

-------------------------------------------------------------------------------



local TableInvocation = {}

TableInvocation.__index = TableInvocation

TableInvocation.message = message -- Add the message method



function TableInvocation.new(invokeArgs, nowikiCode, cfg)

	local obj = setmetatable({}, TableInvocation)

	obj.cfg = cfg

	obj.invokeArgs = invokeArgs

	obj.code = nowikiCode

	return obj

end



function TableInvocation:getInvocation(options)

	if self.code then

		local nowikiObj = NowikiInvocation.new(self.code, self.cfg)

		return nowikiObj:getInvocation(options)

	else

		return require('Module:Template invocation').invocation(

			options.template,

			self.invokeArgs

		)

	end

end



function TableInvocation:getOutput(options)

	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, moduleCall2])

		return mw.getCurrentFrame():callParserFunction(moduleCall1], args)

	end

	return mw.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.

-------------------------------------------------------------------------------



local bridge = {}



function bridge.table(args, cfg)

	cfg = cfg or mw.loadData(DATA_MODULE)



	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

				optionsoptionKey = v

			end

		else

			invokeArgsk = 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 &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)

end



function bridge.nowiki(args, cfg)

	cfg = cfg or mw.loadData(DATA_MODULE)

	

	-- Convert args beginning with _ for consistency with the normal bridge

	local newArgs = {}

	for k, v in pairs(args) do

		local normalName = type(k) == "string" and string.match(k, "^_(.*)$")

		if normalName then

			newArgsnormalName = v

		else

			newArgsk = v

		end

	end



	local code = newArgs.code or newArgs1

	local invocationObj = NowikiInvocation.new(code, cfg)

	newArgs.code = nil

	newArgs1 = nil

	-- Assume we want to see the code as we already passed it in.

	newArgs.showcode = newArgs.showcode or true

	local testCaseObj = TestCase.new(invocationObj, newArgs, cfg)

	return tostring(testCaseObj)

end



-------------------------------------------------------------------------------

-- Exports

-------------------------------------------------------------------------------



local p = {}



function p.main(frame, cfg)

	cfg = cfg or mw.loadData(DATA_MODULE)



	-- Load the wrapper config, if any.

	local wrapperConfig

	if frame.getParent then

		local title = frame:getParent():getTitle()

		local template = title:gsub(cfg.sandboxSubpagePattern, '')

		wrapperConfig = cfg.wrapperstemplate

	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.

	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'

	})



	-- Get default args and build the args table. User-specified args overwrite

	-- default args.

	local defaultArgs = wrapperConfig and wrapperConfig.args or {}

	local args = {}

	for k, v in pairs(defaultArgs) do

		argsk = v

	end

	for k, v in pairs(userArgs) do

		argsk = v

	end



	return bridgefunc](args, cfg)

end



function p._exportClasses() -- For testing

	return {

		Template = Template,

		TestCase = TestCase,

		NowikiInvocation = NowikiInvocation,

		TableInvocation = TableInvocation

	}

end



return p