feat(spec): Create visual error for widget layout tests (#3530)
The assert for `widget:layout` now visualizes the expected and actual widget structure for comparison. Signed-off-by: Lucas Schwiderski <lucas@lschwiderski.de>
This commit is contained in:
parent
f353499230
commit
1ab0f892d2
|
@ -39,21 +39,128 @@ end
|
||||||
say:set("assertion.widget_fit.positive", "Offering (%s, %s) to widget and expected (%s, %s), but got (%s, %s)")
|
say:set("assertion.widget_fit.positive", "Offering (%s, %s) to widget and expected (%s, %s), but got (%s, %s)")
|
||||||
assert:register("assertion", "widget_fit", widget_fit, "assertion.widget_fit.positive", "assertion.widget_fit.positive")
|
assert:register("assertion", "widget_fit", widget_fit, "assertion.widget_fit.positive", "assertion.widget_fit.positive")
|
||||||
|
|
||||||
|
local function draw_layout_comparison(actual, expected)
|
||||||
|
local lines = {
|
||||||
|
"Widget layout does not match:\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
local left, right = {}, {}
|
||||||
|
|
||||||
|
local function times(char, len)
|
||||||
|
local chars = {}
|
||||||
|
for _ = 1, len do
|
||||||
|
table.insert(chars, char)
|
||||||
|
end
|
||||||
|
return table.concat(chars, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function wrap_line(width, x, line)
|
||||||
|
-- Two columns occupied by the `|` characters
|
||||||
|
local buffer = width - 2 - #line
|
||||||
|
local buffer_left = math.floor(buffer / 2)
|
||||||
|
local buffer_right = buffer - buffer_left
|
||||||
|
|
||||||
|
return string.format(
|
||||||
|
"%s|%s%s%s|",
|
||||||
|
times(" ", x), times(" ", buffer_left), line, times(" ", buffer_right)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw_widget(widget)
|
||||||
|
local height = widget._height
|
||||||
|
local width = widget._width
|
||||||
|
local x = widget._matrix.x0
|
||||||
|
-- Two lines removed for the horizontal lines,
|
||||||
|
-- three for the lines with text
|
||||||
|
local fixed = 5
|
||||||
|
local buffer_top = math.floor((height - fixed) / 2)
|
||||||
|
local buffer_bottom = height - fixed - buffer_top
|
||||||
|
|
||||||
|
local widget_lines = {times("-", width)}
|
||||||
|
for _ = 1, buffer_top do
|
||||||
|
table.insert(widget_lines, wrap_line(width, x, " "))
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(widget_lines, wrap_line(width, x, widget._name or "Widget"))
|
||||||
|
table.insert(widget_lines, wrap_line(width, x, string.format(
|
||||||
|
"Size %d,%d",
|
||||||
|
widget._width, widget._height
|
||||||
|
)))
|
||||||
|
table.insert(widget_lines, wrap_line(width, x, string.format(
|
||||||
|
"Pos %d,%d",
|
||||||
|
widget._matrix.x0, widget._matrix.y0
|
||||||
|
)))
|
||||||
|
|
||||||
|
for _ = 1, buffer_bottom do
|
||||||
|
table.insert(widget_lines, wrap_line(width, x, " "))
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(widget_lines, times("-", width))
|
||||||
|
|
||||||
|
return widget_lines
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_len = 0
|
||||||
|
|
||||||
|
for _, widget in ipairs(actual) do
|
||||||
|
for _, line in ipairs(draw_widget(widget)) do
|
||||||
|
max_len = math.max(max_len, #line)
|
||||||
|
table.insert(left, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, widget in ipairs(expected) do
|
||||||
|
for _, line in ipairs(draw_widget(widget)) do
|
||||||
|
table.insert(right, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(lines, string.format(
|
||||||
|
"%-"..max_len.."s %-"..max_len.."s",
|
||||||
|
"Actual:", "Expected:"
|
||||||
|
))
|
||||||
|
|
||||||
|
for i = 1, math.max(#left, #right) do
|
||||||
|
table.insert(lines, string.format("%-"..max_len.."s %s", left[i] or "", right[i] or ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Force one additional newline. Due to how the error message has to be set
|
||||||
|
-- up, we want to force `say`'s trailing quote into a new line.
|
||||||
|
table.insert(lines, "")
|
||||||
|
|
||||||
|
return table.concat(lines, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
local function widget_layout(state, arguments)
|
local function widget_layout(state, arguments)
|
||||||
if #arguments ~= 3 then
|
if #arguments ~= 3 then
|
||||||
error("Have " .. #arguments .. " arguments, but need 3")
|
error("Have " .. #arguments .. " arguments, but need 3")
|
||||||
end
|
end
|
||||||
|
|
||||||
local widget = arguments[1]
|
local widget = arguments[1]
|
||||||
local given = arguments[2]
|
local width_actual, height_actual = arguments[2][1], arguments[2][2]
|
||||||
local expected = arguments[3]
|
local expected = arguments[3]
|
||||||
local children = widget.layout and widget:layout({ "fake context" }, given[1], given[2]) or {}
|
|
||||||
|
|
||||||
local fits = true
|
if not widget.layout then
|
||||||
if #children ~= #expected then
|
arguments[1] = "Method `layout` is missing. Is this even a widget?"
|
||||||
fits = false
|
return false
|
||||||
else
|
end
|
||||||
for i = 1, #children do
|
|
||||||
|
local children = widget:layout({"fake context"}, width_actual, height_actual)
|
||||||
|
|
||||||
|
if type(children) ~= "table" then
|
||||||
|
arguments[1] = string.format(
|
||||||
|
"Expected table from widget:layout(), got %s",
|
||||||
|
type(children)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local num_children = #children
|
||||||
|
|
||||||
|
local fits = num_children == #expected
|
||||||
|
-- If the numbers don't match, we can skip checking them individually
|
||||||
|
if fits then
|
||||||
|
for i = 1, num_children do
|
||||||
local child, exp = children[i], expected[i]
|
local child, exp = children[i], expected[i]
|
||||||
if child._widget ~= exp._widget or
|
if child._widget ~= exp._widget or
|
||||||
child._width ~= exp._width or
|
child._width ~= exp._width or
|
||||||
|
@ -69,15 +176,13 @@ local function widget_layout(state, arguments)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- For proper error message, mess with the arguments
|
arguments[1] = draw_layout_comparison(children, expected)
|
||||||
arguments[1] = expected
|
|
||||||
arguments[2] = children
|
|
||||||
arguments[3] = given[1]
|
|
||||||
arguments[4] = given[2]
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
say:set("assertion.widget_layout.positive", "Expected:\n%s\nbut got:\n%s\nwhen offering (%s, %s) to widget")
|
|
||||||
|
-- Hack the API by using the first argument as arbitrary string.
|
||||||
|
-- This gives actual flexibility in the error message.
|
||||||
|
say:set("assertion.widget_layout.positive", "%s")
|
||||||
assert:register("assertion",
|
assert:register("assertion",
|
||||||
"widget_layout",
|
"widget_layout",
|
||||||
widget_layout,
|
widget_layout,
|
||||||
|
|
Loading…
Reference in New Issue