Mô đun:List/testcases

-- Unit tests for Module:List. Click talk page to run tests.
local mList = require('Module:List/sandbox')
local ScribuntoUnit = require('Module:ScribuntoUnit')
local suite = ScribuntoUnit:new()
--------------------------------------------------------------------------------
-- Default values
--------------------------------------------------------------------------------
local d = {}
-- Function names
d.bulletedFunc = 'bulleted'
d.unbulletedFunc = 'unbulleted'
d.horizontalFunc = 'horizontal'
d.orderedFunc = 'ordered'
d.horizontalOrderedFunc = 'horizontal_ordered'
-- List items
d.item1 = 'Foo'
d.item2 = 'Bar'
d.itemNested = '\n*Subitem 1\n*Subitem 2'  -- The leading LF must not be trimmed from such item
-- Styles and classes
d.arbitraryStyle = 'text-align: right'
d.arbitraryValue = 'some value'
d.arbitraryClass = 'someclass'
d.arbitraryClass2 = 'someclass2'
d.hlist = 'hlist'
d.plainlist = 'plainlist'
d.templatestyles_pattern = '\127^\127*UNIQ%-%-templatestyles%-%x+%-QINU^\127*\127'
-- Parameter names
d.itemStyleParam1 = 'item1_style'
d.itemValueParam1 = 'item1_value'
-- Attributes
d.arbitraryStart = '5'
-- Tracking categories
d.trackingCategory = 
--------------------------------------------------------------------------------
-- 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
--------------------------------------------------------------------------------
-- Test makeListData
--------------------------------------------------------------------------------
function suite:testDataBlank()
	local data = mList.makeListData(d.bulletedFunc, {})
	self:assertEquals('table', type(data))
end
function suite:testDataOneItem()
	local data = mList.makeListData(d.bulletedFunc, {d.item1})
	self:assertEquals(d.item1, data.items1.content)
end
function suite:testDataTwoItems()
	local data = mList.makeListData(d.bulletedFunc, {d.item1, d.item2})
	self:assertEquals(d.item1, data.items1.content)
	self:assertEquals(d.item2, data.items2.content)
end
function suite:testDataItemStyle1()
	local data = mList.makeListData(d.bulletedFunc, {d.item1, d.itemStyleParam1 = d.arbitraryStyle})
	self:assertEquals(d.arbitraryStyle, data.items1.style)
end
function suite:testDataItemStyle1NoContent()
	local data = mList.makeListData(d.bulletedFunc, {d.itemStyleParam1 = d.arbitraryStyle})
	self:assertEquals(nil, data.items1)
end
function suite:testDataItemValue1()
	local data = mList.makeListData(d.bulletedFunc, {d.item1, d.itemValueParam1 = d.arbitraryValue})
	self:assertEquals(d.arbitraryValue, data.items1.value)
end
function suite:testDataItemValue1NoContent()
	local data = mList.makeListData(d.bulletedFunc, {d.itemValueParam1 = d.arbitraryValue})
	self:assertEquals(nil, data.items1)
end
function suite:testDataItemStyle()
	local data = mList.makeListData(d.bulletedFunc, {item_style = d.arbitraryStyle})
	self:assertEquals(d.arbitraryStyle, data.itemStyle)
end
function suite:testDataListStyle()
	local data = mList.makeListData(d.bulletedFunc, {list_style = d.arbitraryStyle})
	self:assertEquals(d.arbitraryStyle, data.listStyle)
end
function suite:testDataStart()
	local data = mList.makeListData(d.bulletedFunc, {start = d.arbitraryStart})
	self:assertEquals(d.arbitraryStart, data.start)
end
function suite:testDataHorizontalNumberingFix()
	local data = mList.makeListData(d.horizontalOrderedFunc, {start = '5'})
	self:assertEquals('5', data.start)
	self:assertEquals('listitem 4', data.counterReset)
end
function suite:testDataHorizontalNumberingFixBadInput()
	local data = mList.makeListData(d.horizontalOrderedFunc, {start = 'foo'})
	self:assertEquals('foo', data.start)
	self:assertEquals(nil, data.counterReset)
end
function suite:testDataHorizontalNumberingFixNumberInput()
	local data = mList.makeListData(d.horizontalOrderedFunc, {start = 6})
	self:assertEquals(6, data.start)
	self:assertEquals('listitem 5', data.counterReset)
end
function suite:testDataListTag()
	self:assertEquals('ul', mList.makeListData(d.bulletedFunc, {}).listTag)
	self:assertEquals('ul', mList.makeListData(d.horizontalFunc, {}).listTag)
	self:assertEquals('ul', mList.makeListData(d.unbulletedFunc, {}).listTag)
	self:assertEquals('ol', mList.makeListData(d.orderedFunc, {}).listTag)
	self:assertEquals('ol', mList.makeListData(d.horizontalOrderedFunc, {}).listTag)
end
function suite:testDataListStyleType()
	self:assertEquals('foo', mList.makeListData(d.orderedFunc, {list_style_type = 'foo'}).listStyleType)
	self:assertEquals('foo', mList.makeListData(d.horizontalOrderedFunc, {list_style_type = 'foo'}).listStyleType)
	self:assertEquals(nil, mList.makeListData(d.bulletedFunc, {list_style_type = 'foo'}).listStyleType)
end
function suite:testDataListStyleTypeAltSyntax()
	self:assertEquals('foo', mList.makeListData(d.orderedFunc, {'list-style-type' = 'foo'}).listStyleType)
	self:assertEquals('foo', mList.makeListData(d.horizontalOrderedFunc, {'list-style-type' = 'foo'}).listStyleType)
	self:assertEquals(nil, mList.makeListData(d.bulletedFunc, {'list-style-type' = 'foo'}).listStyleType)
end
function suite:testDataValidType()
	self:assertEquals('1', mList.makeListData(d.orderedFunc, {type = '1'}).type)
	self:assertEquals('A', mList.makeListData(d.orderedFunc, {type = 'A'}).type)
	self:assertEquals('a', mList.makeListData(d.orderedFunc, {type = 'a'}).type)
	self:assertEquals('I', mList.makeListData(d.orderedFunc, {type = 'I'}).type)
	self:assertEquals('i', mList.makeListData(d.orderedFunc, {type = 'i'}).type)
end
function suite:testDataInvalidType()
	local data = mList.makeListData(d.orderedFunc, {type = 'foo'})
	self:assertEquals(nil, data.type)
	self:assertEquals('foo', data.listStyleType)
end
function suite:testDataTypeByListType()
	self:assertEquals('1', mList.makeListData(d.orderedFunc, {type = '1'}).type)
	self:assertEquals('1', mList.makeListData(d.horizontalOrderedFunc, {type = '1'}).type)
	self:assertEquals(nil, mList.makeListData(d.bulletedFunc, {type = '1'}).type)
end
function suite:testDataIndent()
	self:assertEquals('1.6em', mList.makeListData(d.horizontalFunc, {indent = 1}).marginLeft)
	self:assertEquals('3.2em', mList.makeListData(d.horizontalFunc, {indent = 2}).marginLeft)
	self:assertEquals('1.6em', mList.makeListData(d.horizontalFunc, {indent = '1'}).marginLeft)
	self:assertEquals(nil, mList.makeListData(d.horizontalFunc, {}).marginLeft)
end
function suite:testDataIndentByListType()
	self:assertEquals('1.6em', mList.makeListData(d.horizontalFunc, {indent = 1}).marginLeft)
	self:assertEquals('1.6em', mList.makeListData(d.horizontalOrderedFunc, {indent = 1}).marginLeft)
	self:assertEquals(nil, mList.makeListData(d.bulletedFunc, {indent = 1}).marginLeft)
	self:assertEquals(nil, mList.makeListData(d.unbulletedFunc, {indent = 1}).marginLeft)
	self:assertEquals(nil, mList.makeListData(d.orderedFunc, {indent = 1}).marginLeft)
end
function suite:testDataStyle()
	self:assertEquals(d.arbitraryStyle, mList.makeListData(d.horizontalFunc, {style = d.arbitraryStyle}).style)
end
function suite:testDataClass()
	self:assertArrayContainsString(d.arbitraryClass, mList.makeListData(d.bulletedFunc, {class = d.arbitraryClass}).classes)
end
function suite:testDataListTypeClasses()
	self:assertArrayContainsString(d.hlist, mList.makeListData(d.horizontalFunc, {}).classes)
	self:assertArrayContainsString(d.hlist, mList.makeListData(d.horizontalOrderedFunc, {}).classes)
	self:assertArrayContainsString(d.plainlist, mList.makeListData(d.unbulletedFunc, {}).classes)
end
--------------------------------------------------------------------------------
-- Test deprecated parameters
--------------------------------------------------------------------------------
function suite:testDeprecatedItemStyle1()
	local data = mList.makeListData(d.bulletedFunc, {d.item1, item_style1 = d.arbitraryStyle})
	self:assertEquals(d.arbitraryStyle, data.items1.style)
end
function suite:testDeprecatedItemStyle1NoContent()
	local data = mList.makeListData(d.bulletedFunc, {item_style1 = d.arbitraryStyle})
	self:assertEquals(nil, data.items1)
end
function suite:testDeprecatedValueStyle1()
	local data = mList.makeListData(d.bulletedFunc, {d.item1, item_value1 = d.arbitraryStyle})
	self:assertEquals(d.arbitraryStyle, data.items1.value)
end
function suite:testDeprecatedValueStyle1NoContent()
	local data = mList.makeListData(d.bulletedFunc, {item_value1 = d.arbitraryStyle})
	self:assertEquals(nil, data.items1)
end
--------------------------------------------------------------------------------
-- Test tracking categories
--------------------------------------------------------------------------------
function suite:testTrackingCategoriesItem1Style()
	local result = mList.renderTrackingCategories{item1_style = d.arbitraryStyle}
	self:assertEquals(, result)
end
function suite:testTrackingCategoriesItemStyle1()
	local result = mList.renderTrackingCategories{item_style1 = d.arbitraryStyle}
	self:assertEquals(d.trackingCategory, result)
end
function suite:testTrackingCategoriesItemStyle4()
	local result = mList.renderTrackingCategories{item_style4 = d.arbitraryStyle}
	self:assertEquals(d.trackingCategory, result)
end
function suite:testTrackingCategoriesItemValue1()
	local result = mList.renderTrackingCategories{item_value1 = d.arbitraryValue}
	self:assertEquals(d.trackingCategory, result)
end
--------------------------------------------------------------------------------
-- Test renderList
--------------------------------------------------------------------------------
function suite.makeDataWithOneItem(args)
	local data = {
		classes = {},
		items = {},
		templatestyles = ,
	}
	data.items1 = {content = d.item1}
	for k, v in pairs(args or {}) do
		datak = v
	end
	return data
end
function suite.cleanPattern(s)
	-- Cleans a pattern so that the magic characters ()%.*+-?^$ are interpreted literally.
	s = s:gsub('(%(%)%%%.%%%*%+%-%?%^%$)', '%%%1')
	return s
end
function suite.makePattern(tag, attr, s1, s2)
	-- For making a pattern to use with assertStringContains inside a specific attribute and HTML tag.
	s1 = suite.cleanPattern(s1)
	if s2 then
		s2 = suite.cleanPattern(s2)
	end
	local ssep = 
	if s2 then
		ssep = '%s*:%s*'
	end
	s2 = s2 or 
	return string.format(
		'-%s="^">-%s%s%s^">-"^>->',
		tag,
		attr,
		s1,
		ssep,
		s2
	)
end
function suite:testRenderNoItems()
	self:assertEquals(, mList.renderList{})
	self:assertEquals(, mList.renderList{items = {}})
end
function suite:testRenderOneItem()
	local data = self.makeDataWithOneItem()
	self:assertStringContains('^%s*%s*%s*' .. self.cleanPattern(d.item1) .. '%s*%s*%s*$', mList.renderList(data))
end
function suite:testRenderTwoItems()
	local data = self.makeDataWithOneItem()
	data.items2 = {content = d.item2}
	self:assertStringContains('^%s*%s*%s*' .. self.cleanPattern(d.item1) .. '%s*%s*%s*' .. self.cleanPattern(d.item2) .. '%s*%s*%s*$', mList.renderList(data))
end
function suite:testRenderOneClass()
	local data = self.makeDataWithOneItem()
	data.classes1 = d.arbitraryClass
	self:assertStringContains(, mList.renderList(data), true)
end
function suite:testRenderTwoClasses()
	local data = self.makeDataWithOneItem()
	data.classes1 = d.arbitraryClass
	data.classes2 = d.arbitraryClass2
	local pattern = self.makePattern('div', 'class', d.arbitraryClass .. ' ' .. d.arbitraryClass2)
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderMarginLeft()
	local data = self.makeDataWithOneItem{marginLeft = '5px'}
	local pattern = self.makePattern('div', 'style', 'margin-left', '5px')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderStyle()
	local data = self.makeDataWithOneItem{style = 'foo:bar'}
	local pattern = self.makePattern('div', 'style', 'foo:bar')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderListTag()
	local data = self.makeDataWithOneItem{listTag = 'ol'}
	self:assertEquals( .. d.item1 .. , mList.renderList(data))
end
function suite:testRenderStart()
	local data = self.makeDataWithOneItem{start = '7'}
	local pattern = self.makePattern('ul', 'start', '7')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderStartNumber()
	local data = self.makeDataWithOneItem{start = 7}
	local pattern = self.makePattern('ul', 'start', '7')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderType()
	local data = self.makeDataWithOneItem{type = 'i'}
	local pattern = self.makePattern('ul', 'type', 'i')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderCounterReset()
	local data = self.makeDataWithOneItem{counterReset = 'item 3'}
	local pattern = self.makePattern('ul', 'style', 'counter-reset', 'item 3')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderListStyleType()
	local data = self.makeDataWithOneItem{listStyleType = 'roman'}
	local pattern = self.makePattern('ul', 'style', 'list-style-type', 'roman')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderListStyle()
	local data = self.makeDataWithOneItem{listStyle = 'bar:baz'}
	local pattern = self.makePattern('ul', 'style', 'bar:baz')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderItemStyle()
	local data = self.makeDataWithOneItem{itemStyle = 'qux:foo'}
	local pattern = self.makePattern('li', 'style', 'qux:foo')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderItemStyleTwoItems()
	local data = self.makeDataWithOneItem{itemStyle = 'bar:foo'}
	data.items2 = {content = d.item2}
	local tagPattern = self.makePattern('li', 'style', 'bar:foo')
	local pattern = tagPattern .. d.item1 ..  .. tagPattern .. d.item2
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderItem1Style()
	local data = self.makeDataWithOneItem()
	data.items1.style = 'baz:foo'
	local pattern = self.makePattern('li', 'style', 'baz:foo')
	self:assertStringContains(pattern, mList.renderList(data), false)
end
function suite:testRenderItem1Value()
	local data = self.makeDataWithOneItem()
	data.items1.value = d.arbitraryValue
	local pattern = self.makePattern('li', 'value', d.arbitraryValue)
	self:assertStringContains(pattern, mList.renderList(data), false)
end
--------------------------------------------------------------------------------
-- Whole-module tests
--------------------------------------------------------------------------------
function suite:testBulleted()
	local actual = mListd.bulletedFunc{d.item1}
	local pattern = '^%s*%s*%s*' .. self.cleanPattern(d.item1) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testUnbulleted()
	local actual = mListd.unbulletedFunc{d.item1}
	local pattern = '^' .. d.templatestyles_pattern .. self.makePattern('div', 'class', 'plainlist') .. '%s*%s*%s*' ..
		self.cleanPattern(d.item1) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testHorizontal()
	local actual = mListd.horizontalFunc{d.item1}
	local pattern = '^' .. d.templatestyles_pattern .. self.makePattern('div', 'class', 'hlist') .. '%s*%s*%s*' ..
		self.cleanPattern(d.item1) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testOrdered()
	local actual = mListd.orderedFunc{d.item1}
	local pattern = '^%s*%s*%s*' .. self.cleanPattern(d.item1) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testHorizontalOrdered()
	local actual = mListd.horizontalOrderedFunc{d.item1}
	local pattern = '^' .. d.templatestyles_pattern .. self.makePattern('div', 'class', 'hlist') .. '%s*%s*%s*' ..
		self.cleanPattern(d.item1) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testTrimming()
	local actual = mListd.orderedFunc{'\n Foo \n'}
	-- Now many templates expect the arguments to be trimmed
	local pattern = '^%s*%s*' .. self.cleanPattern('Foo') .. '%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
function suite:testNested()
	local actual = mListd.orderedFunc{d.itemNested}
	local pattern = '^%s*%s*%s*' .. self.cleanPattern(d.itemNested) .. '%s*%s*%s*$'
	self:assertStringContains(pattern, actual, false)
end
return suite