From 56b38d04e0383fb30a112bc80f8dacb454f067e5 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 11:07:59 +0000 Subject: [PATCH] chore(tests): improve test runner system * Implements an extension to frktest's `lune_console_reporter` for displaying status of individual running test cases and suites -- with colors :O! * Include mechanism for running select tests using the test runner script. --- .lune/test.luau | 55 ++++++++++++++++++++++++++++++++-------- tests/_reporter.luau | 55 ++++++++++++++++++++++++++++++++++++++++ tests/cmp.luau | 3 +-- tests/parse_invalid.luau | 3 +-- tests/parse_valid.luau | 3 +-- tests/tostring.luau | 3 +-- 6 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 tests/_reporter.luau diff --git a/.lune/test.luau b/.lune/test.luau index 344ab13..6bfb579 100644 --- a/.lune/test.luau +++ b/.lune/test.luau @@ -4,21 +4,54 @@ local fs = require("@lune/fs") local process = require("@lune/process") local frktest = require("@pkg/frktest") +local reporter = require("../tests/_reporter") -- HACK: Cast require to allow for dynamic paths in strict mode -- A more proper solution would be to use luau.load instead, but -- frktest requires its global state to be modified by test suites -local require = require :: (path: string) -> () -> () +local require = require :: ( + path: string +) -> ( + test: typeof(setmetatable( + {} :: { + case: (name: string, fn: () -> nil) -> (), + suite: (name: string, fn: () -> ()) -> (), + }, + { __index = frktest.test } + )) +) -> () -if process.args[1] ~= nil then - require("../tests/" .. process.args[1])() -else - for _, test in fs.readDir("tests") do - require("../tests/" .. test)() - end +local allowed_tests = process.args +for _, test in fs.readDir("tests") do + -- If we are given any arguments, we only run those tests, otherwise, + -- we run all the tests + + -- So, to include only a certain set of test files, you can provide either + -- the full path to the test file (with or without the extension) or the test + -- file name + local withoutExt = string.sub(test, 1, -6) + local is_allowed = #process.args == 0 + or table.find(allowed_tests, `tests/{test}`) + or table.find(allowed_tests, withoutExt) + or table.find(allowed_tests, `tests/{withoutExt}`) + + local constructors = { + case = frktest.test.case, + suite = frktest.test.suite, + } + + if not is_allowed then + constructors.case = frktest.test.skip.case + constructors.suite = frktest.test.skip.suite + end + + -- Ignore files starting with underscores, eg: _reporter.luau + if string.sub(test, 1, 1) == "_" then + continue + end + + require("../tests/" .. test)(setmetatable(constructors, { __index = frktest.test })) end -frktest._reporters.lune_console_reporter.init() -if not frktest.run() then - process.exit(1) -end +reporter.init() +process.exit(tonumber(frktest.run())) diff --git a/tests/_reporter.luau b/tests/_reporter.luau new file mode 100644 index 0000000..48dccc9 --- /dev/null +++ b/tests/_reporter.luau @@ -0,0 +1,55 @@ +local stdio = require("@lune/stdio") + +local frktest = require("@pkg/frktest") +local Reporter = frktest._reporters.lune_console_reporter + +local STYLE = table.freeze({ + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` + end, + + report = function(name: string, state: "run" | "success" | "error" | "skip") + local state_color: stdio.Color = if state == "run" + then "white" + elseif state == "success" then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( + "reset" + )} {name}` + end, +}) + +--- Clears a the previous line, and moves to its beginning +local function clear_last_line(): () + return stdio.write("\x1b[A\x1b[K\x1b[0G\x1b[?25h") +end + +local ReporterExt = {} +function ReporterExt.init() + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) + + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) + + frktest.test.on_test_enter(function(test) + print(STYLE.report(test.name, "run")) + end) + + frktest.test.on_test_leave(function(test) + clear_last_line() + print(STYLE.report(test.name, if test.failed then "error" else "success")) + end) + + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip")) + end) + + Reporter.init() +end + +return setmetatable(ReporterExt, { __index = Reporter }) diff --git a/tests/cmp.luau b/tests/cmp.luau index 170e517..b27ef1c 100644 --- a/tests/cmp.luau +++ b/tests/cmp.luau @@ -1,5 +1,4 @@ local frktest = require("@pkg/frktest") -local test = frktest.test local check = frktest.assert.check local Option = require("../luau_packages/option") @@ -7,7 +6,7 @@ type Option = Option.Option local Semver = require("../lib") -return function() +return function(test: typeof(frktest.test)) test.suite("Semver comparison tests", function() test.case("Basic version comparisons", function() local v1 = Semver.parse("1.2.3"):unwrap() diff --git a/tests/parse_invalid.luau b/tests/parse_invalid.luau index 194ed64..768eea0 100644 --- a/tests/parse_invalid.luau +++ b/tests/parse_invalid.luau @@ -1,10 +1,9 @@ local frktest = require("@pkg/frktest") -local test = frktest.test local check = frktest.assert.check local Semver = require("../lib") -return function() +return function(test: typeof(frktest.test)) test.suite("Invalid semver parsing tests", function() test.case("Rejects missing components", function() local res = Semver.parse("1.2") diff --git a/tests/parse_valid.luau b/tests/parse_valid.luau index fa8225f..e781126 100644 --- a/tests/parse_valid.luau +++ b/tests/parse_valid.luau @@ -1,5 +1,4 @@ local frktest = require("@pkg/frktest") -local test = frktest.test local check = frktest.assert.check local Option = require("../luau_packages/option") @@ -7,7 +6,7 @@ type Option = Option.Option local Semver = require("../lib") -return function() +return function(test: typeof(frktest.test)) test.suite("Basic tests", function() test.case("Semver creates valid version objects", function() local res = Semver.parse("1.2.3-beta.1") diff --git a/tests/tostring.luau b/tests/tostring.luau index 4118e3f..6a74634 100644 --- a/tests/tostring.luau +++ b/tests/tostring.luau @@ -1,5 +1,4 @@ local frktest = require("@pkg/frktest") -local test = frktest.test local check = frktest.assert.check local Option = require("../luau_packages/option") @@ -7,7 +6,7 @@ type Option = Option.Option local Semver = require("../lib") -return function() +return function(test: typeof(frktest.test)) test.suite("Stringification tests", function() test.case("A version constructed with new() when stringified should match expected string", function() local versionMap: { [string]: Semver.Version } = {