Module:ScribuntoUnit
Jump to navigation
Jump to search
See Global Lua Modules/ScribuntoUnit
- Subpages
-- <nowiki>
--------------------------------------------------------------------------------
-- ScribuntoUnit assert-style unit tester for Scribunto modules. The
-- syntax for test assertions is markedly similar to the [LuaUnit
-- framework](https://luaunit.readthedocs.io), [NodeJS assert
-- module](https://nodejs.org/api/assert.html) and the [NodeJS Chai
-- assertion API](https://www.chaijs.com/api/assert/).
--
-- @classmod ScribuntoUnit
-- @release stable
-- @image ScribuntoUnit screenshot.png
-- @author [[wikipedia:User:Tgr|Tgr]] (Hungarian Wikipedia)
-- @author [[wikipedia:User:Mr. Stradivarius|Mr. Stradivarius]]
-- @author [[wikipedia:Special:PageHistory/Module:ScribuntoUnit|Other Wikipedia contributors]]
-- @author [[User:DarthKitty|DarthKitty]]
-- @author [[User:Dessamator|Dessamator]]
-- @attribution [[wikipedia:Module:ScribuntoUnit|Module:ScribuntoUnit]] (Wikipedia)
-- @see [[wikipedia:Module:ScribuntoUnit|Original module on Wikipedia]]
--------------------------------------------------------------------------------
local ScribuntoUnit = {}
-- begin dev wiki change
local inspect = require('Dev:Inspect')
-- end dev wiki change
--------------------------------------------------------------------------------
-- Configuration and all localisable strings, to make it easier to port
-- this module to another wiki.
-- @table cfg
--------------------------------------------------------------------------------
local cfg = mw.loadData('Module:ScribuntoUnit/config')
--------------------------------------------------------------------------------
-- Collection of private debugging subroutines for unit testing.
-- @table DebugHelper
-- @local
--------------------------------------------------------------------------------
local DebugHelper = {}
--------------------------------------------------------------------------------
-- Concatenates keys and values, ideal for displaying a template argument table.
-- @param {table} tbl Argument table (map of argument parameter names
-- to values).
-- @param[opt] {string} keySeparator Glue between key & value. Default:
-- `" = "`.
-- @param[opt] {string} separator Glue between different key-value pairs.
-- Default: `", "`.
-- @return Concatenated series of key-value pairs.
-- @usage DebugHelper.concatWithKeys({a = 1, b = 2, c = 3}, " => ", ", ") == "a => 1, b => 2, c => 3"
-- @private
--------------------------------------------------------------------------------
function DebugHelper.concatWithKeys(tbl, keySeparator, separator)
keySeparator = keySeparator or ' = '
separator = separator or ', '
local concatted = ''
local i = 1
local first = true
local unnamedArguments = true
for k, v in pairs(tbl) do
if first then
first = false
else
concatted = concatted .. separator
end
if k == i and unnamedArguments then
i = i + 1
concatted = concatted .. tostring(v)
else
unnamedArguments = false
concatted = concatted .. tostring(k) .. keySeparator .. tostring(v)
end
end
return concatted
end
--------------------------------------------------------------------------------
-- Compares two tables recursively (non-table values are handled correctly as
-- well).
-- @function DebugHelper.deepCompare
-- @param {table} t1 Expected table from testcase, generally hardcoded
-- into the test suite.
-- @param {table} t2 Actual table from testcase, generated by code in
-- the test module or test suite.
-- @param[opt] {boolean} ignoreMetatable If false, the `__eq` metamethod in
-- `t1` is used for the comparison.
-- @private
--------------------------------------------------------------------------------
function DebugHelper.deepCompare(t1, t2, ignoreMetatable)
local type1 = type(t1)
local type2 = type(t2)
if type1 ~= type2 then
return false
end
if type1 ~= 'table' then
return t1 == t2
end
local metatable = getmetatable(t1)
if not ignoreMetatable and metatable and metatable.__eq then
return t1 == t2
end
for k1, v1 in pairs(t1) do
local v2 = t2[k1]
if v2 == nil or not DebugHelper.deepCompare(v1, v2) then
return false
end
end
for k2, v2 in pairs(t2) do
if t1[k2] == nil then
return false
end
end
return true
end
--------------------------------------------------------------------------------
-- Raises an error with stack information.
-- @function DebugHelper.raise
-- @param {table} details A table with error details.
-- @param {string} details.text The error message to display.
-- @param {string} details.trace Traceback information to insert in
-- stack data.
-- @param {string} details.source File and line number.
-- @param[opt] {number} level Zero-indexed traceback level. For example, if
-- the `level` is 1, @{DebugHelper.raise} will not appear in
-- the traceback.
-- @error {table} A table with details for @{ScribuntoUnit:runTest} to
-- capture and display to the user.
-- @note In future, a metatable will be added for error handling.
-- @private
--------------------------------------------------------------------------------
function DebugHelper.raise(details, level)
level = (level or 1) + 1
details.trace = debug.traceback('', level)
details.source = string.match(details.trace, '^%s*stack traceback:%s*(%S*: )')
-- setmetatable(details, {
-- __tostring: function() return details.text end
-- })
error(details, level)
end
-- ScribuntoUnit's package class, with assertions API.
--------------------------------------------------------------------------------
-- Invocation entry point for running the tests.
-- This method can be called without a frame, in which case it will use
-- @{mw.log} for output. However, the following assertion methods require
-- template or wikitext expansions and will be skipped:
-- * @{ScribuntoUnit:assertResultEquals} - requires @{frame:preprocess}
-- * @{ScribuntoUnit:assertSameResult} - requires @{frame:preprocess}
-- * @{ScribuntoUnit:assertTemplateEquals} - requires @{frame:expandTemplate}
-- @param {string} displayMode Accepts:
-- * `"table"` - shows result report as a wikitext
-- table that displays which test(s) failed and the
-- failure message.
-- * `"log"` - shows result as a console log for
-- debugging purposes. Skips frame-dependent assertions.
-- * `"short"` - displays result report as a short sentence.
-- @usage {{testunit|"scrib"}}
-- @usage {{#invoke:ScribuntoUnit|run|displayMode = table/log}}
--------------------------------------------------------------------------------
function ScribuntoUnit:run(suite, frame)
local testData = self:runSuite(suite, frame)
if frame then
return self:displayResults(testData, frame.args.displayMode or 'table')
else
return self:displayResults(testData, 'log')
end
end
--------------------------------------------------------------------------------
-- Instantiates a new `ScribuntoUnit` for a test suite module.
-- The developer can call this method with a table constructor/upvalue, or they
-- can attach their methods to the returned instance's table.
-- @param[opt] {table} o a table with test functions.
-- @return {ScribuntoUnit} Self-executable instance of ScribuntoUnit.
-- @constructor
-- @usage local suite = require('Module:ScribuntoUnit'):new()
-- @usage return require('Module:ScribuntoUnit'):new(suite)
--------------------------------------------------------------------------------
function ScribuntoUnit:new(o)
o = o or {}
setmetatable(o, {__index = self})
o.run = function(frame) return self:run(o, frame) end
return o
end
--------------------------------------------------------------------------------
-- When used in a test, that test gets ignored, and the skipped count increases
-- by one.
-- @function ScribuntoUnit:markTestSkipped
-- @param[opt] {string} message Optional description.
-- @error {table} ScribuntoUnit error with field `skipped` as `true`.
--------------------------------------------------------------------------------
function ScribuntoUnit:markTestSkipped(
-- begin dev wiki change
message
-- end dev wiki change
)
DebugHelper.raise({
ScribuntoUnit = true,
skipped = true,
-- begin dev wiki change
message = message,
-- end dev wiki change
}, 3)
end
--------------------------------------------------------------------------------
-- Assertion checking that the input evaluates as `true` as a Lua expression.
-- In Lua, anything that isn't the `false` or `nil` primitives is "truthy".
-- @function ScribuntoUnit:assertTrue
-- @param actual A Lua expression - potentially an upvalue or
-- condition evaluated by the test suite.
-- @param[opt] {string} message Optional description of the test.
-- @error[opt] {table} ScribuntoUnit error with assertion failure, if the
-- `expression` value is falsy.
-- @usage suite:assertTrue(2 + 2 == 4)
-- @usage suite:assertTrue('foo')
--------------------------------------------------------------------------------
function ScribuntoUnit:assertTrue(actual, message)
if not actual then
DebugHelper.raise({ScribuntoUnit = true, text = string.format("Failed to assert that %s is true", tostring(actual)), message = message}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion checking that the input evaluates as `false` as a Lua expression.
-- In Lua, only the `false` and `nil` primitives are "falsy".
-- @param actual A Lua expression - potentially a variable or
-- condition evaluated by the test suite.
-- @param[opt] {string} message Optional description of the test.
-- @error[opt] {table} ScribuntoUnit error with assertion failure, if the
-- `expression` value is truthy.
-- @usage suite:assertFalse(2 + 2 == 5)
-- @usage suite:assertFalse(false)
--------------------------------------------------------------------------------
function ScribuntoUnit:assertFalse(actual, message)
if actual then
DebugHelper.raise({ScribuntoUnit = true, text = string.format("Failed to assert that %s is false", tostring(actual)), message = message}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion testing for the presence of an expected pattern or string in an
-- input string.
-- If the string is not found, the error message shows the values of `pattern`
-- and `str`. If `str` is more than 70 characters long, a truncated version
-- is displayed in the error message. This method is useful for testing
-- specific behaviours in complex wikitext.
-- @param[opt] {boolean} pattern Pattern or string to perform lookup with.
-- @param[opt] {boolean} str Target string to perform lookup on.
-- @param[opt] {boolean} plain Search with a plain string instead of a
-- ustring pattern. Default: `false`.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertStringContains("foo", "foobar")
-- @usage suite:assertStringContains("foo[", "foo[bar", true)
--------------------------------------------------------------------------------
function ScribuntoUnit:assertStringContains(pattern, str, plain, message)
if type(pattern) ~= 'string' then
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format("Pattern type error (expected string, got %s)", type(pattern)),
message = message
}, 2)
end
if type(str) ~= 'string' then
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format("String type error (expected string, got %s)", type(str)),
message = message
}, 2)
end
if not mw.ustring.find(str, pattern, nil, plain) then
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format('Failed to find %s "%s" in string "%s"', plain and "plain string" or "pattern", pattern, str),
message = message
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion testing for the absence of an expected pattern or string in an
-- input string.
-- If the string is not found, the error message shows the values of `pattern`
-- and `str`. This test method has similar validation and rendering behaviour
-- to @{ScribuntoUnit:assertStringContains}.
-- @param[opt] {boolean} pattern Pattern or string to perform lookup with.
-- @param[opt] {boolean} str Target string to perform lookup on.
-- @param[opt] {boolean} plain Search with a plain string instead of a
-- ustring pattern. Default: `false`.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertNotStringContains("baz", "foobar")
-- @usage suite:assertNotStringContains("[qux", "baz]qux", true)
--------------------------------------------------------------------------------
function ScribuntoUnit:assertNotStringContains(pattern, str, plain, message)
if type(pattern) ~= 'string' then
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format("Pattern type error (expected string, got %s)", type(pattern)),
message = message
}, 2)
end
if type(str) ~= 'string' then
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format("String type error (expected string, got %s)", type(str)),
message = message
}, 2)
end
local i, j = mw.ustring.find(str, pattern, nil, plain)
if i then
local match = mw.ustring.sub(str, i, j)
DebugHelper.raise({
ScribuntoUnit = true,
text = mw.ustring.format('Found match "%s" for %s "%s"', match, plain and "plain string" or "pattern", pattern),
message = message
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for the strict equality of an input upvalue against an expected
-- value. This method uses `==` instead of @{rawequal}, thus supporting `__eq`
-- metamethods where applicable. Tables and functions are compared by reference
-- in the assertion (i.e. by whether they reference the same object).
--
-- If both parameters are numbers, the values are instead compared using
-- @{ScribuntoUnit:assertWithinDelta} with delta `1e-8` (`0.00000001`) since
-- Lua numbers are represented as [[wikipedia:Floating-point arithmetic|
-- floating-point numbers]] with limited precision.
--
-- @param expected Target Lua upvalue for comparison, generated by the
-- test module.
-- @param actual Value to compare against in the comparison, provided
-- in the test suite.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertEquals(4, calculator._add(2, 2), "2 + 2 should be 4")
--------------------------------------------------------------------------------
function ScribuntoUnit:assertEquals(expected, actual, message)
if type(expected) == 'number' and type(actual) == 'number' then
self:assertWithinDelta(expected, actual, 1e-8, message)
elseif expected ~= actual then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %s equals expected %s", tostring(actual), tostring(expected)),
actual = actual,
expected = expected,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for the numerical equivalence of a input upvalue against an
-- expected numerical value, within a closed interval or "distance" (`delta`).
--
-- Lua non-integer numbers are represented as [[wikipedia:Double-precision
-- floating-point format|double-precision floating-point numbers]] with limited
-- precision. Generally, Lua numerical comparisons involve a rounding step by
-- <math>2^{-55}</math> when one of the values is irrational. The slight error
-- between the compared numbers means that Lua does not consider them equal.
-- This necessitates a ranged comparison using a delta when handling these
-- numbers.
--
-- @param {number} expected Numerical value to compare against,
-- provided in the test suite.
-- @param {number} actual Target Lua upvalue for comparison, generated
-- by the test module.
-- @param {number} delta Closed interval range to permit value
-- difference within. Should range between `1e-8` and `1e-16`.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertWithinDelta(0.1, calculator._subtract(0.3, 0.2), 1e-10)
--------------------------------------------------------------------------------
function ScribuntoUnit:assertWithinDelta(expected, actual, delta, message)
if type(expected) ~= "number" then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Expected value %s is not a number", tostring(expected)),
actual = actual,
expected = expected,
message = message,
}, 2)
end
if type(actual) ~= "number" then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Actual value %s is not a number", tostring(actual)),
actual = actual,
expected = expected,
message = message,
}, 2)
end
local diff = expected - actual
if diff < 0 then diff = - diff end -- instead of importing math.abs
if diff > delta then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %f is within %f of expected %f", actual, delta, expected),
actual = actual,
expected = expected,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for the recursive equivalence of two tables. This function works
-- by computing the path to every primitive in the tables, then comparing them
-- by reference. Respects the `__eq` metamethod of the `expected` table.
--
-- If a non-table value is provided for `expected` and `actual`, the values
-- are assumed to be primitives and compared by reference.
--
-- @param {table} expected Target Lua upvalue for recursive comparison,
-- provided in the test suite.
-- @param {table} actual Value to compare against in the recursive
-- comparison, generated by the test module.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertDeepEquals({ { 1, 3 }, { 2, 4 } }, calculator._partition("odd", { 1, 2, 3, 4 }))
--------------------------------------------------------------------------------
function ScribuntoUnit:assertDeepEquals(expected, actual, message)
if not DebugHelper.deepCompare(expected, actual) then
if type(expected) == 'table' then
-- begin dev wiki change
expected = inspect(expected)
-- end dev wiki change
end
if type(actual) == 'table' then
-- begin dev wiki change
actual = inspect(actual)
-- end dev wiki change
end
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %s equals expected %s", tostring(actual), tostring(expected)),
actual = actual,
expected = expected,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for processing of unprocessed wikitext against an expected result.
-- @param {string} expected Processed wikitext output for comparison.
-- @param {string} text Unprocessed wikitext to compare with.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertResultEquals("4", "{{calculator|addition|2|2}}")
--------------------------------------------------------------------------------
function ScribuntoUnit:assertResultEquals(expected, text, message)
local frame = self.frame
-- begin dev wiki change
if not frame then
self:markTestSkipped()
end
-- end dev wiki change
local actual = frame:preprocess(text)
if expected ~= actual then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %s equals expected %s after preprocessing", text, tostring(expected)),
actual = actual,
actualRaw = text,
expected = expected,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for comparison of two unprocessed wikitext strings.
-- @param {string} text1 Unprocessed wikitext for comparison.
-- @param {string} text2 Unprocessed wikitext to compare with.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertSameResult("{{#expr: 2 + 2}}", "{{calculator|addition|2|2}}")
--------------------------------------------------------------------------------
function ScribuntoUnit:assertSameResult(text1, text2, message)
local frame = self.frame
-- begin dev wiki change
if not frame then
self:markTestSkipped()
end
-- end dev wiki change
local processed1 = frame:preprocess(text1)
local processed2 = frame:preprocess(text2)
if processed1 ~= processed2 then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %s equals expected %s after preprocessing", processed1, processed2),
actual = processed1,
actualRaw = text1,
expected = processed2,
expectedRaw = text2,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for a template call comparison against expected output.
-- @param {string} expected Processed output to compare against.
-- @param {string} template Template name, following MediaWiki
-- transclusion syntax (e.g. `:` prefix for mainspace pages).
-- @param[opt] {table} args Table of template arguments to pass.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertTemplateEquals("4", "calculator", {"add", "2", "2"})
function ScribuntoUnit:assertTemplateEquals(expected, template, args, message)
local frame = self.frame
-- begin dev wiki change
if not frame then
self:markTestSkipped()
end
-- end dev wiki change
local actual = frame:expandTemplate{ title = template, args = args}
if expected ~= actual then
DebugHelper.raise({
ScribuntoUnit = true,
text = string.format("Failed to assert that %s with args %s equals expected %s after preprocessing",
DebugHelper.concatWithKeys(args), template, expected),
actual = actual,
actualRaw = template,
expected = expected,
message = message,
}, 2)
end
end
--------------------------------------------------------------------------------
-- Assertion for an uncaught exception for a function call.
-- @param {function} fn The function to be tested via a protected call
-- with no arguments.
-- @param[opt] {boolean} expectedMessage Expected message to compare the
-- thrown exception against.
-- @param[opt] {string} message Optional description of the test.
-- @usage suite:assertThrows(error, "unknown error")
function ScribuntoUnit:assertThrows(fn, expectedMessage, message)
local succeeded, actualMessage = pcall(fn)
if succeeded then
DebugHelper.raise({
ScribuntoUnit = true,
text = 'Expected exception but none was thrown',
message = message,
}, 2)
end
-- For strings, strip the line number added to the error message
actualMessage = type(actualMessage) == 'string'
and string.match(actualMessage, 'Module:[^:]*:[0-9]*: (.*)')
or actualMessage
local messagesMatch = DebugHelper.deepCompare(expectedMessage, actualMessage)
if expectedMessage and not messagesMatch then
DebugHelper.raise({
ScribuntoUnit = true,
expected = expected,
actual = actual,
text = string.format('Expected exception with message %s, but got message %s',
tostring(expectedMessage), tostring(actualMessage)
),
message = message
}, 2)
end
end
--------------------------------------------------------------------------------
-- Resets global counters for the test suite execution process.
-- @param[opt] {table} frame Frame for use in running the tests.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:init(frame)
self.frame = frame
self.successCount = 0
self.failureCount = 0
self.skipCount = 0
self.results = {}
end
--------------------------------------------------------------------------------
-- Runs a single testcase (suite test method).
-- @param {table} suite Suite table the test method is attached to.
-- @param {string} name Test method name.
-- @param {function} test Test method itself, containing assertions.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:runTest(suite, name, test)
local success, details = pcall(test, suite)
if success then
self.successCount = self.successCount + 1
table.insert(self.results, {name = name, success = true})
elseif type(details) ~= 'table' or not details.ScribuntoUnit then -- a real error, not a failed assertion
self.failureCount = self.failureCount + 1
table.insert(self.results, {name = name, error = true, message = 'Lua error -- ' .. tostring(details)})
elseif details.skipped then
self.skipCount = self.skipCount + 1
table.insert(self.results, {
name = name,
skipped = true,
-- begin dev wiki change
message = details.message,
-- end dev wiki change
})
else
self.failureCount = self.failureCount + 1
local message = details.source
if details.message then
message = message .. details.message .. "\n"
end
message = message .. details.text
table.insert(self.results, {name = name, error = true, message = message, expected = details.expected, actual = details.actual})
end
end
--------------------------------------------------------------------------------
-- Test engine, running all tests and generating execution data.
-- @param {table} suite Suite table the test methods are attached to.
-- @param[opt] {table} frame Frame for use in running the tests.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:runSuite(suite, frame)
self:init(frame)
local names = {}
for name in pairs(suite) do
if name:find('^test') then
table.insert(names, name)
end
end
table.sort(names) -- Put tests in alphabetical order.
for i, name in ipairs(names) do
local func = suite[name]
self:runTest(suite, name, func)
end
return {
successCount = self.successCount,
failureCount = self.failureCount,
skipCount = self.skipCount,
results = self.results,
}
end
--------------------------------------------------------------------------------
-- Displays test results from test engine execution.
-- @param {table} testData Test suite execution data.
-- @param {string} displayMode See @{ScribuntoUnit:run}.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:displayResults(testData, displayMode)
if displayMode == 'table' then
return self:displayResultsAsTable(testData)
elseif displayMode == 'log' then
return self:displayResultsAsLog(testData)
elseif displayMode == 'short' then
return self:displayResultsAsShort(testData)
else
error('unknown display mode')
end
end
--------------------------------------------------------------------------------
-- Generates event trace for test results.
-- @param {table} testData Test suite execution data to render.
-- @param {number} testData.successCount Number of successful test cases.
-- @param {number} testData.failureCount Number of failed test cases.
-- @param {number} testData.skipCount Number of skipped test cases.
-- @param {table} testData.results Array of execution results.
-- @private
function ScribuntoUnit:displayResultsAsLog(testData)
if testData.failureCount > 0 then
mw.log('FAILURES!!!')
elseif testData.skipCount > 0 then
mw.log('Some tests could not be executed without a frame and have been skipped. Invoke this test suite as a template to run all tests.')
end
mw.log(string.format('Assertions: success: %d, error: %d, skipped: %d', testData.successCount, testData.failureCount, testData.skipCount))
mw.log('------------------------------------------------------------------------')
for _, result in ipairs(testData.results) do
if result.error
-- begin dev wiki change
or (result.skipped and result.message)
-- end dev wiki change
then
mw.log(string.format('%s: %s', result.name, result.message))
end
end
end
--------------------------------------------------------------------------------
-- Displays test result data as short statement.
-- @param {table} testData Test suite execution data to render.
-- @param {number} testData.successCount Number of successful test cases.
-- @param {number} testData.failureCount Number of failed test cases.
-- @param {number} testData.skipCount Number of skipped test cases.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:displayResultsAsShort(testData)
local text = string.format(cfg.shortResultsFormat, testData.successCount, testData.failureCount, testData.skipCount)
if testData.failureCount > 0 then
text = '<span class="error">' .. text .. '</span>'
end
return text
end
--------------------------------------------------------------------------------
-- Displays test result data as table.
-- @param {table} testData Test suite execution data to render.
-- @param {number} testData.failureCount Number of failed test cases.
-- @param {table} testData.results Array of execution results.
-- @private
--------------------------------------------------------------------------------
function ScribuntoUnit:displayResultsAsTable(testData)
local successIcon, failIcon = self.frame:preprocess(cfg.successIndicator), self.frame:preprocess(cfg.failureIndicator)
-- begin dev wiki change
local skippedIcon = self.frame:preprocess(cfg.skippedIndicator)
-- end dev wiki change
local text = ''
if testData.failureCount > 0 then
local msg = mw.message.newRawMessage(cfg.failureSummary, testData.failureCount):plain()
msg = self.frame:preprocess(msg)
if cfg.failureCategory then
msg = cfg.failureCategory .. msg
end
text = text .. failIcon .. ' ' .. msg .. '\n'
-- begin dev wiki change
if testData.skipCount > 0 then
local msg = mw.message.newRawMessage(cfg.skippedSummary, testData.skipCount):plain()
msg = self.frame:preprocess(msg)
if cfg.skippedCategory then
msg = cfg.skippedCategory .. msg
end
text = text .. skippedIcon .. ' ' .. msg .. '\n'
end
elseif testData.skipCount > 0 then
local msg = mw.message.newRawMessage(cfg.skippedSummary, testData.skipCount):plain()
msg = self.frame:preprocess(msg)
if cfg.skippedCategory then
msg = cfg.skippedCategory .. msg
end
text = text .. skippedIcon .. ' ' .. msg .. '\n'
-- end dev wiki change
else
text = text .. successIcon .. ' ' .. cfg.successSummary .. '\n'
end
text = text .. '{| class="wikitable scribunto-test-table"\n'
text = text .. '!\n! ' .. cfg.nameString .. '\n! ' .. cfg.expectedString .. '\n! ' .. cfg.actualString .. '\n'
for _, result in ipairs(testData.results) do
text = text .. '|-\n'
if result.error then
text = text .. '| ' .. failIcon .. '\n| ' .. result.name .. '\n| '
if (result.expected and result.actual) then
text = text .. mw.text.nowiki(tostring(result.expected)) .. '\n| ' .. mw.text.nowiki(tostring(result.actual)) .. '\n'
else
text = text .. ' colspan="2" | ' .. mw.text.nowiki(result.message) .. '\n'
end
-- begin dev wiki change
elseif result.skipped then
text = text .. '| ' .. skippedIcon .. '\n| ' .. result.name .. '\n|'
if result.message then
text = text .. ' colspan="2" | ' .. mw.text.nowiki(result.message) .. '\n'
else
text = text .. '\n|\n'
end
-- end dev wiki change
else
text = text .. '| ' .. successIcon .. '\n| ' .. result.name .. '\n|\n|\n'
end
end
text = text .. '|}\n'
-- begin dev wiki change
return text .. '[[Category:Lua test suites]]'
-- end dev wiki change
end
return ScribuntoUnit