-- Panagsubok para iti [[Modulo:Igid a kahon]]. Pinduten ti tungtunganna a panid tapno maipataray ti panagsubok.
local mSideBox = require('Modulo:Igid a kahon')
local ScribuntoUnit = require('Modulo:ScribuntoUnit')
local suite = ScribuntoUnit:new()

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
 
function suite:assertArrayContainsString(expected, t)
	-- This only works on arrays that only contain strings.
	local sep = '|SEPARATOR|'
	local concatenated = sep .. table.concat(t, sep) .. sep
	self:assertStringContains(sep .. expected .. sep, concatenated, true)
end

function suite:assertNotArrayContainsString(expected, t)
	-- This only works on arrays that only contain strings.
	local sep = '|SEPARATOR|'
	local concatenated = sep .. table.concat(t, sep) .. sep
	self:assertNotStringContains(sep .. expected .. sep, concatenated, true)
end

--------------------------------------------------------------------------------
-- Test makeData
--------------------------------------------------------------------------------

function suite:testDataBlank()
	self:assertEquals('table', type(mSideBox.makeData{}))
	self:assertEquals('table', type(mSideBox.makeData{}.classes))
end

function suite:testDataMetadata()
	suite:assertNotArrayContainsString('metadata', mSideBox.makeData{metadata = 'no'}.classes)
	suite:assertNotArrayContainsString('metadata', mSideBox.makeData{metadata = false}.classes)
	suite:assertArrayContainsString('metadata', mSideBox.makeData{}.classes)
	suite:assertArrayContainsString('metadata', mSideBox.makeData{metadata = 'yes'}.classes)
	suite:assertArrayContainsString('metadata', mSideBox.makeData{metadata = true}.classes)
	suite:assertArrayContainsString('metadata', mSideBox.makeData{metadata = 'foo'}.classes)
end

function suite:testDataLeft()
	suite:assertArrayContainsString('mbox-small', mSideBox.makeData{}.classes)
	suite:assertArrayContainsString('mbox-small', mSideBox.makeData{position = 'right'}.classes)
	suite:assertArrayContainsString('mbox-small', mSideBox.makeData{position = 'asdf'}.classes)
	suite:assertArrayContainsString('mbox-small-left', mSideBox.makeData{position = 'left'}.classes)
	suite:assertArrayContainsString('mbox-small-left', mSideBox.makeData{position = 'Left'}.classes)
	suite:assertArrayContainsString('mbox-small-left', mSideBox.makeData{position = 'LEFT'}.classes)
end

function suite:testDataClass()
	suite:assertArrayContainsString('some-class', mSideBox.makeData{class = 'some-class'}.classes)
end

function suite:testDataStyle()
	suite:assertEquals('foo:bar', mSideBox.makeData{style = 'foo:bar'}.style)
end

function suite:testDataTextstyle()
	suite:assertEquals('foo:bar', mSideBox.makeData{textstyle = 'foo:bar'}.textstyle)
end

function suite:testDataAbove()
	suite:assertEquals('some above text', mSideBox.makeData{above = 'some above text'}.above)
end

function suite:testDataAbovestyle()
	suite:assertEquals('foo:bar', mSideBox.makeData{abovestyle = 'foo:bar'}.abovestyle)
end

function suite:testDataText()
	suite:assertEquals('some text', mSideBox.makeData{text = 'some text'}.text)
end

function suite:testDataImage()
	suite:assertEquals('[[File:Example.png|thumb]]', mSideBox.makeData{image = '[[File:Example.png|thumb]]'}.image)
end

function suite:testDataImageNone()
	suite:assertEquals(nil, mSideBox.makeData{image = 'none'}.image)
end

function suite:testDataImageright()
	suite:assertEquals('[[File:Example.png|thumb]]', mSideBox.makeData{imageright = '[[File:Example.png|thumb]]'}.imageright)
end

function suite:testDataBelow()
	suite:assertEquals('some below text', mSideBox.makeData{below = 'some below text'}.below)
end

--------------------------------------------------------------------------------
-- Test renderSidebox
--------------------------------------------------------------------------------

function suite.cleanPattern(s)
	-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
	s = s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
	return s
end

function suite.makeHtmlPattern(tagTables)
	-- Makes a pattern for use with assertStringContains.
	-- The input is an array of tables, each of which corresponds to an opening tag,
	-- a piece of wikitext, or a closing tag.
	-- 
	-- It is also possible to use a single table as an input, rather than an
	-- array of tables.
	--
	-- Opening tags:
	-- {tag = 'tr'}
	-- {tag = 'table', attr = 'class', value = 'mbox-image'}
	-- {tag = 'table', attr = 'style', property = 'text-align', value = 'center'}
	-- {tag = 'td', attr = 'colspan', value = '3'}
	-- Properties and values are escaped so that hyphens etc. will work in patterns.
	--
	-- Wikitext:
	-- {wikitext = 'Foo'}
	--
	-- Closing tags:
	-- {tag = 'table', close = true}
	--
	-- For Example, this code:
	-- suite.makeHtmlPattern{
	--   {tag = 'div', attr = 'style', property = 'text-align', value = 'center'},
	--   {wikitext = 'Foo'},
	--   {tag = 'span'},
	--   {wikitext = 'Bar'},
	--   {tag = 'span', close = true},
	--   {tag = 'div', close = true}
	-- }
	--
	-- Produces this:
	-- <div[^>]-style="[^">]-text%-align%s*:%s*center[^">]-"[^>]->[^<]-Foo[^<]-<span[^>]->[^<]-Bar[^<]-</span>[^<]-</div>
	if type(tagTables) ~= 'table' then
		error('invalid input to makeHtmlPattern', 2)
	end
	if #tagTables == 0 then
		-- Table may be passed as a single tag table.
		tagTables = {tagTables}
	end
	local ret = {}
	for i, t in ipairs(tagTables) do
		if t.tag then
			if t.close then
				ret[#ret + 1] = string.format('</%s>', t.tag)
			elseif t.attr and t.property and t.value then
				ret[#ret + 1] = string.format(
					'<%s[^>]-%s="[^">]-%s%%s*:%%s*%s[^">]-"[^>]->',
					t.tag,
					t.attr,
					suite.cleanPattern(t.property),
					suite.cleanPattern(t.value)
				)
			elseif t.attr and t.value then
				ret[#ret + 1] = string.format(
					'<%s[^>]-%s="[^">]-%s[^">]-"[^>]->',
					t.tag,
					t.attr,
					suite.cleanPattern(t.value)
				)
			else
				ret[#ret + 1] = string.format('<%s[^>]->', t.tag)
			end
		elseif t.wikitext then
			ret[#ret + 1] = suite.cleanPattern(t.wikitext)
		end
	end
	return table.concat(ret, '[^<]-')
end

function suite:testRenderDefaultStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{},
		false
	)
end

function suite:testRenderAboveStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '2'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{above = 'some text'},
		false
	)
end

function suite:testRenderBelowStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '2'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{below = 'some below text'},
		false
	)
end

function suite:testRenderAboveAndBelowStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '2'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '2'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{above = 'some above text', below = 'some below text'},
		false
	)
end

function suite:testRenderNewlines()
	self:assertStringContains(
		'^<table[^>]->'
			.. '\n<tr>'
			.. '<td colspan="2"[^>]->[^<]-</td>'
			.. '</tr>'
			.. '\n<tr>'
			.. '\n<td[^>]->[^<]-</td>'
			.. '\n<td[^>]->[^<]-</td>'
			.. '</tr>'
			.. '\n<tr>'
			.. '<td colspan="2"[^>]->[^<]-</td>'
			.. '</tr>'
			.. '</table>$',
		mSideBox.renderSidebox{above = 'some above text', below = 'some below text'},
		false
	)
end

function suite:testRenderImagerightStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{imageright = '[[File:Example.png|thumb]]'},
		false
	)
end

function suite:testRenderAboveImagerightStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '3'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{above = 'some text', imageright = '[[File:Example.png|thumb]]'},
		false
	)
end

function suite:testRenderBelowImagerightStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '3'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{below = 'some below text', imageright = '[[File:Example.png|thumb]]'},
		false
	)
end

function suite:testRenderAboveAndBelowImagerightStructure()
	self:assertStringContains(
		'^' .. suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '3'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'td'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'tr'},
			{tag = 'td', attr = 'colspan', value = '3'},
			{tag = 'td', close = true},
			{tag = 'tr', close = true},
			{tag = 'table', close = true}
		} .. '$',
		mSideBox.renderSidebox{above = 'some above text', below = 'some below text', imageright = '[[File:Example.png|thumb]]'},
		false
	)
end

function suite:testRenderOneClass()
	local data = {classes = {'foo'}}
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'class', value = 'foo'},
		mSideBox.renderSidebox(data),
		false
	)
end

function suite:testRenderTwoClasses()
	local data = {classes = {'foo', 'bar'}}
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'class', value = 'foo'},
		mSideBox.renderSidebox(data),
		false
	)
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'class', value = 'bar'},
		mSideBox.renderSidebox(data),
		false
	)
end

function suite:testRenderDefaultTableStyles()
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'style', property = 'border', value = '1px solid #aaa'},
		mSideBox.renderSidebox{},
		false
	)
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'style', property = 'background-color', value = '#f9f9f9'},
		mSideBox.renderSidebox{},
		false
	)
end

function suite:testRenderStyle()
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'table', attr = 'style', value = 'foo:bar'},
		mSideBox.renderSidebox{style = 'foo:bar'},
		false
	)
end

function suite:testRenderAbove()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td', attr = 'colspan', value = '2'},
			{wikitext = '\nsome text'},
			{tag = 'td', close = true}
		},
		mSideBox.renderSidebox{above = 'some text'},
		false
	)
end

function suite:testRenderAboveClass()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'class', value = 'mbox-text'},
			{wikitext = 'some text'}
		},
		mSideBox.renderSidebox{above = 'some text'},
		false
	)
end

function suite:testRenderAboveTextstyle()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'style', value = 'foo:bar'},
			{wikitext = 'some text'}
		},
		mSideBox.renderSidebox{above = 'some text', textstyle = 'foo:bar'},
		false
	)
end

function suite:testRenderAbovestyle()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'table'},
			{tag = 'tr'},
			{tag = 'td', attr = 'style', value = 'bar:baz'},
			{wikitext = 'some text'}
		},
		mSideBox.renderSidebox{above = 'some text', abovestyle = 'bar:baz'},
		false
	)
end

function suite:testRenderImage()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td', attr = 'class', value = 'mbox-image'},
			{wikitext = '[[File:Example.png|thumb]]'}
		},
		mSideBox.renderSidebox{image = '[[File:Example.png|thumb]]'},
		false
	)
end

function suite:testRenderNoImage()
	self:assertStringContains(
		suite.makeHtmlPattern{tag = 'td', attr = 'style', property = 'width', value = '1px'},
		mSideBox.renderSidebox{},
		false
	)
end

function suite:testRenderText()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td'},
			{wikitext = 'the text body'},
			{tag = 'td', close = true}
		},
		mSideBox.renderSidebox{text = 'the text body'},
		false
	)
end

function suite:testRenderImageright()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td', attr = 'class', value = 'mbox-imageright'},
			{wikitext = '[[File:Example 2.png|thumb]]'}
		},
		mSideBox.renderSidebox{imageright = '[[File:Example 2.png|thumb]]'},
		false
	)
end

function suite:testRenderBelow()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td', attr = 'class', value = 'mbox-text'},
			{wikitext = 'some below text'},
			{tag = 'td', close = true}
		},
		mSideBox.renderSidebox{below = 'some below text'},
		false
	)
end

function suite:testRenderBelowTextstyle()
	self:assertStringContains(
		suite.makeHtmlPattern{
			{tag = 'td', attr = 'style', value = 'bar:foo'},
			{wikitext = 'some below text'},
			{tag = 'td', close = true}
		},
		mSideBox.renderSidebox{below = 'some below text', textstyle = 'bar:foo'},
		false
	)
end

--------------------------------------------------------------------------------
-- Whole-module tests
--------------------------------------------------------------------------------

function suite:testMain()
	local currentFrame = mw.getCurrentFrame()
	local parent = currentFrame:newChild{args = {text = 'some box text'}}
	local frame = parent:newChild{}
	local actual = mSideBox.main(frame)
	self:assertStringContains('some box text', actual, true)
end

function suite:testUnderscoreMain()
	local actual = mSideBox._main{text = 'some underscore main text'}
	self:assertStringContains('some underscore main text', actual, true)
end

return suite