Module:Arguments/testcases

local getArgs = require('Module:Arguments/sandbox').getArgs local ScribuntoUnit = require('Module:ScribuntoUnit') local suite = ScribuntoUnit:new local libName = 'Arguments'

-- -- Default values --

local d = {} d.frameTitle = 'Frame title' d.parentTitle = 'Parent title'

-- Precedence-testing values d.firstFrameArg = 'first frame argument' d.firstParentArg = 'first parent argument' d.secondParentArg = 'second parent argument' d.uniqueFrameArg = 'unique frame argument' d.uniqueFrameArgKey = 'uniqueFrameArgKey' d.uniqueParentArg = 'unique parent argument' d.uniqueParentArgKey = 'uniqueParentArgKey'

-- Trimming and whitespace values. -- Whitespace gets trimmed from named parameters, so keys for these need -- to be numbers to make this a proper test. d.blankArg = '' d.blankArgKey = 100 d.spacesArg = '\n  ' d.spacesArgKey = 101 d.untrimmedArg = '\n  foo bar   ' d.untrimmedArgKey = 102 d.trimmedArg = 'foo bar' d.valueFuncValue = 'valueFuncValue' d.defaultValueFunc = function return d.valueFuncValue end d.translate = { foo = 'F00', bar = '8@r', baz = '8@z', qux = 'qUx' }

-- Helper to run all tests using sandbox version of the library from the debug console. To run against main lib, use =p.run function suite.runSandbox local frame = mw.getCurrentFrame:newChild{title='testcases', args={module=libName .. '/sandbox', displayMode='log'}} return suite.run(frame) end

-- Allow test runner to use the sandbox and the primary versions of the library with the same testcases function suite:module return require('Module:' .. (self.frame and self.frame.args.module or libName)) end

--      Library-specific tests

-- -- Helper functions --

function suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs) frameTitle = frameTitle or d.frameTitle frameArgs = frameArgs or { d.firstFrameArg, [d.uniqueFrameArgKey] = d.uniqueFrameArg, [d.blankArgKey] = d.blankArg, [d.spacesArgKey] = d.spacesArg, [d.untrimmedArgKey] = d.untrimmedArg }	parentTitle = parentTitle or d.parentTitle parentArgs = parentArgs or { d.firstParentArg, d.secondParentArg, [d.uniqueParentArgKey] = d.uniqueParentArg }	local currentFrame = mw.getCurrentFrame local parent = currentFrame:newChild{title = parentTitle, args = parentArgs} local frame = parent:newChild{title = frameTitle, args = frameArgs} return frame, parent end

function suite.getDefaultArgs(options, frameTitle, frameArgs, parentTitle, parentArgs) local frame, parent = suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs) local args = getArgs(frame, options) return args end

function suite:assertError(func, ...) -- Asserts that executing the function func results in an error. -- Parameters after func are func's arguments. local success, msg = pcall(func, ...) self:assertFalse(success) end

function suite:assertNumberOfIterations(expected, iterator, t)	local noIterations = 0 for k, v in iterator(t) do		noIterations = noIterations + 1 end self:assertEquals(expected, noIterations) end

-- -- Test precedence --

function suite:assertDefaultPrecedence(args) self:assertEquals(d.firstFrameArg, args[1]) self:assertEquals(d.secondParentArg, args[2]) self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey]) self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey]) end

function suite:testDefaultPrecedence self:assertDefaultPrecedence(suite.getDefaultArgs) end

function suite:testDefaultPrecedenceThroughWrapper self:assertDefaultPrecedence(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false}) end

function suite:testDefaultPrecedenceThroughNonWrapper self:assertDefaultPrecedence(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false}, nil, nil, 'Not the parent title')) end

function suite:assertParentFirst(args) self:assertEquals(d.firstParentArg, args[1]) self:assertEquals(d.secondParentArg, args[2]) self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey]) self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey]) end

function suite:testParentFirst self:assertParentFirst(suite.getDefaultArgs{parentFirst = true}) end

function suite:testParentFirstThroughWrapper self:assertParentFirst(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false, parentFirst = true}) end

function suite:testParentFirstThroughNonWrapper self:assertParentFirst(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false, parentFirst = true}, nil, nil, 'Not the parent title')) end

function suite:assertParentOnly(args) self:assertEquals(d.firstParentArg, args[1]) self:assertEquals(d.secondParentArg, args[2]) self:assertEquals(nil, args[d.uniqueFrameArgKey]) self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey]) end

function suite:testParentOnly self:assertParentOnly(suite.getDefaultArgs{parentOnly = true}) end

function suite:testParentOnlyThroughWrapper self:assertParentOnly(suite.getDefaultArgs{wrappers = {d.parentTitle}}) end

function suite:testParentOnlyThroughSandboxWrapper self:assertParentOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, d.parentTitle .. '/sandbox')) end

function suite:assertFrameOnly(args) self:assertEquals(d.firstFrameArg, args[1]) self:assertEquals(nil, args[2]) self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey]) self:assertEquals(nil, args[d.uniqueParentArgKey]) end

function suite:testFrameOnly self:assertFrameOnly(suite.getDefaultArgs{frameOnly = true}) end

function suite:testFrameOnlyThroughNonWrapper self:assertFrameOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, 'Not the parent title')) end

function suite:testDefaultPrecedenceWithWhitespace local frame, parent = suite.getFrames(		d.frameTitle,		{' '},		d.parentTitle,		{d.firstParentArg}	) local args = getArgs(frame) self:assertEquals(d.firstParentArg, args[1]) end

-- -- Test trimming and blank removal --

function suite:testDefaultTrimmingAndBlankRemoval local args = suite.getDefaultArgs self:assertEquals(nil, args[d.blankArgKey]) self:assertEquals(nil, args[d.spacesArgKey]) self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey]) end

function suite:testRemoveBlanksButNoTrimming local args = suite.getDefaultArgs{trim = false} self:assertEquals(nil, args[d.blankArgKey]) self:assertEquals(nil, args[d.spacesArgKey]) self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey]) end

function suite:testTrimButNoBlankRemoval local args = suite.getDefaultArgs{removeBlanks = false} self:assertEquals(d.blankArg, args[d.blankArgKey]) self:assertEquals('', args[d.spacesArgKey]) self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey]) end

function suite:testNoTrimOrBlankRemoval local args = suite.getDefaultArgs{trim = false, removeBlanks = false} self:assertEquals(d.blankArg, args[d.blankArgKey]) self:assertEquals(d.spacesArg, args[d.spacesArgKey]) self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey]) end

-- -- Test valueFunc --

function suite:testValueFunc local args = suite.getDefaultArgs{valueFunc = d.defaultValueFunc} self:assertEquals(d.valueFuncValue, args['some random key: sdfaliwyda']) end

function suite:testValueFuncPrecedence local args = suite.getDefaultArgs{ trim = false, removeBlanks = false, valueFunc = d.defaultValueFunc }	self:assertEquals(d.valueFuncValue, args[1]) self:assertEquals(d.valueFuncValue, args['some random key: gekjabawyvy']) end

function suite:testValueFuncKey local args = suite.getDefaultArgs{valueFunc = function(key, value) return 'valueFunc key: '.. key end} self:assertEquals('valueFunc key: foo', args.foo) end

function suite:testValueFuncValue local args = suite.getDefaultArgs{valueFunc = function(key, value) return 'valueFunc value: '.. value end} self:assertEquals(		'valueFunc value: ' .. d.uniqueFrameArg,		args[d.uniqueFrameArgKey]	) end

-- -- Test adding new arguments --

function suite:testAddingNewArgs local args = suite.getDefaultArgs self:assertEquals(nil, args.newKey) args.newKey = 'some new key' self:assertEquals('some new key', args.newKey) end

function suite:testAddingNewBlankArgs local args = suite.getDefaultArgs self:assertEquals(nil, args.newKey) args.newKey = '' self:assertEquals('', args.newKey) end

function suite:testAddingNewSpacesArgs local args = suite.getDefaultArgs self:assertEquals(nil, args.newKey) args.newKey = ' ' self:assertEquals(' ', args.newKey) end

function suite:testOverwriting local args = suite.getDefaultArgs self:assertEquals(d.firstFrameArg, args[1]) args[1] = 'a new first frame argument' self:assertEquals('a new first frame argument', args[1]) end

function suite:testOverwritingWithNil local args = suite.getDefaultArgs self:assertEquals(d.firstFrameArg, args[1]) args[1] = nil self:assertEquals(nil, args[1]) end

function suite:testOverwritingWithBlank local args = suite.getDefaultArgs self:assertEquals(d.firstFrameArg, args[1]) args[1] = '' self:assertEquals('', args[1]) end

function suite:testOverwritingWithSpaces local args = suite.getDefaultArgs self:assertEquals(d.firstFrameArg, args[1]) args[1] = ' ' self:assertEquals(' ', args[1]) end

function suite:testReadOnly local args = suite.getDefaultArgs{readOnly = true} local function testFunc args.newKey = 'some new value' end self:assertError(testFunc) end

function suite:testNoOverwriteExistingKey local args = suite.getDefaultArgs{noOverwrite = true} self:assertEquals(d.firstFrameArg, args[1]) local function testFunc args[1] = 'a new first frame argument' end self:assertError(testFunc) end

function suite:testNoOverwriteNewKey local args = suite.getDefaultArgs{noOverwrite = true} self:assertEquals(nil, args.newKey) args.newKey = 'some new value' self:assertEquals('some new value', args.newKey) end

-- -- Test bad input --

function suite:testBadFrameInput self:assertError(getArgs, 'foo') self:assertError(getArgs, 9) self:assertError(getArgs, true) self:assertError(getArgs, function return true end) end

function suite:testBadOptionsInput self:assertError(getArgs, {}, 'foo') self:assertError(getArgs, {}, 9) self:assertError(getArgs, {}, true) self:assertError(getArgs, {}, function return true end) end

function suite:testBadValueFuncInput self:assertError(getArgs, {}, {valueFunc = 'foo'}) self:assertError(getArgs, {}, {valueFunc = 9}) self:assertError(getArgs, {}, {valueFunc = true}) self:assertError(getArgs, {}, {valueFunc = {}}) end

-- -- Test iterator metamethods --

function suite:testPairs local args = getArgs{'foo', 'bar', baz = 'qux'} self:assertNumberOfIterations(3, pairs, args) end

function suite:testIpairs local args = getArgs{'foo', 'bar', baz = 'qux'} self:assertNumberOfIterations(2, ipairs, args) end

function suite:testNoNilsinPairs -- Test that when we use pairs, we don't iterate over any nil values -- that have been memoized. local args = getArgs{''} local temp = args[1] -- Memoizes the nil self:assertNumberOfIterations(0, pairs, args) end

function suite:testNoNilsinIpairs -- Test that when we use ipairs, we don't iterate over any nil values -- that have been memoized. local args = getArgs{''} local temp = args[1] -- Memoizes the nil self:assertNumberOfIterations(0, ipairs, args) end

function suite:testDeletedArgsInPairs -- Test that when we use pairs, we don't iterate over any values that have -- been explicitly set to nil. local args = getArgs{'foo'} args[1] = nil self:assertNumberOfIterations(0, pairs, args) end

function suite:testDeletedArgsInIpairs -- Test that when we use ipairs, we don't iterate over any values that have -- been explicitly set to nil. local args = getArgs{'foo'} args[1] = nil self:assertNumberOfIterations(0, ipairs, args) end

function suite:testNoNilsInPairsAfterIndex -- Test that when we use pairs, we don't iterate over any nils that -- might have been memoized after a value that is not present in the -- original args table is indexed. local args = getArgs{} local temp = args.someRandomValue -- Memoizes the nil self:assertNumberOfIterations(0, pairs, args) end

function suite:testNoNilsInPairsAfterNewindex -- Test that when we use pairs, we don't iterate over any nils that -- might have been memoized after a value that is not present in the -- original args table is added to the args table. local args = getArgs{} args.newKey = nil -- The nil is memoized self:assertNumberOfIterations(0, pairs, args) end

function suite:testNoTableLengthChangeWhileIterating -- Test that the number of arguments doesn't change if we index the -- args table while iterating. -- (Note that the equivalent test is not needed for new arg table	-- indexes, as that would be a user error - doing so produces	-- undetermined behaviour in Lua's next function.) local args = getArgs{'foo', 'bar', baz = 'qux'} self:assertNumberOfIterations(3, pairs, args) for k, v in pairs(args) do local temp = args[k .. 'foo'] end self:assertNumberOfIterations(3, pairs, args) end

function suite:testPairsPrecedenceWithWhitespace local frame, parent = suite.getFrames(		d.frameTitle,		{' '},		d.parentTitle,		{d.firstParentArg}	) local args = getArgs(frame) local actual for k, v in pairs(args) do		actual = v	end self:assertEquals(d.firstParentArg, actual) -- Check that we have actually iterated. self:assertNumberOfIterations(1, pairs, args) end

function suite:testPairsPrecedenceWithNil local frame, parent = suite.getFrames(		d.frameTitle,		{},		d.parentTitle,		{d.firstParentArg}	) local args = getArgs(frame) local actual for k, v in pairs(args) do		actual = v	end self:assertEquals(d.firstParentArg, actual) -- Check that we have actually iterated. self:assertNumberOfIterations(1, pairs, args) end

function suite:testIpairsEarlyExit local mt = {} function mt.__index(t, k)		if k == 1 then return 'foo' elseif k == 2 then return 'bar' elseif k == 3 then error('Expanded argument 3 unnecessarily') end end function mt.__pairs(t) error('Called pairs unnecessarily') end function mt.__ipairs(t) -- Works just like the default ipairs, except respecting __index return function(t, i)			local v = t[i + 1] if v ~= nil then return i + 1, v			end end, t, 0 end local args = getArgs(setmetatable({}, mt)) for k,v in ipairs(args) do		if v == 'bar' then break end end end

-- -- Test argument translation --

function suite:testTranslationIndex local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate}) self:assertEquals('one', args.foo) self:assertEquals('two', args.bar) self:assertEquals('three', args.baz) self:assertEquals('four', args.qux) self:assertEquals('yep', args.untranslated) end

function suite:testTranslationPairsWithAutoBacktranslate local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate}) local cleanArgs = {} for k,v in pairs(args) do		cleanArgs[k] = v	end self:assertDeepEquals(		{			foo = 'one',			bar = 'two',			baz = 'three',			qux = 'four',			untranslated = 'yep'		},		cleanArgs	) end

function suite:testTranslationPairsWithBacktranslate local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = {F00 = 'foo'}}) local cleanArgs = {} for k,v in pairs(args) do		cleanArgs[k] = v	end self:assertDeepEquals(		{			foo = 'one',			['8@r'] = 'two',			['8@z'] = 'three',			qUx = 'four',			untranslated = 'yep'		},		cleanArgs	) end

function suite:testTranslationPairsWithoutBacktranslate local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false}) local cleanArgs = {} for k,v in pairs(args) do		cleanArgs[k] = v	end self:assertDeepEquals(		{			F00 = 'one',			['8@r'] = 'two',			['8@z'] = 'three',			qUx = 'four',			foo = 'nope',			untranslated = 'yep'		},		cleanArgs	) end

function suite:testTranslationNewindex local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false}) args.foo = 'changed1' args.untranslated = 'changed2' local cleanArgs = {} for k,v in pairs(args) do		cleanArgs[k] = v	end self:assertDeepEquals(		{			F00 = 'changed1',			['8@r'] = 'two',			['8@z'] = 'three',			qUx = 'four',			foo = 'nope',			untranslated = 'changed2'		},		cleanArgs	) end

function suite:test_argument local currentFrame = mw.getCurrentFrame currentFrame.args[5] = 555; local args = getArgs(currentFrame) self:assertEquals('nil', type(args.foo)) end

return suite