diff --git a/package-lock.json b/package-lock.json index 99eb3be..367aa8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18232,7 +18232,7 @@ }, "packages/lest": { "name": "@taservers/lest", - "version": "2.1.0", + "version": "3.0.0", "license": "MIT", "dependencies": { "lookpath": "^1.2.2", diff --git a/packages/lest/CHANGELOG.md b/packages/lest/CHANGELOG.md index feb3cc3..d6de439 100644 --- a/packages/lest/CHANGELOG.md +++ b/packages/lest/CHANGELOG.md @@ -14,6 +14,21 @@ The valid change types are: - `Fixed` for any bug fixes. - `Security` in case of vulnerabilities. +## [3.0.0] - [#102](https://github.com/TAServers/lest/issues/102) + +### Added + +- Made it possible to set `testMatch` from the CLI + +### Changed + +- (**BREAKING**) Changed CLI option format to only support `--option=value` and `-o=value` (`--option` and `-o` are shorthand for `--option=true` and `-o=true` respectively) + +### Fixed + +- `lest.config.lua` is no longer required to run tests [#97](https://github.com/TAServers/lest/issues/97) +- Unrecognised CLI arguments no longer throw an error, fixing the `--luaCommand` option in the wrapper being unusable [#104](https://github.com/TAServers/lest/issues/104) + ## [2.1.0] - [#95](https://github.com/TAServers/lest/issues/95) ### Changed diff --git a/packages/lest/package.json b/packages/lest/package.json index 205a2d0..884af2f 100644 --- a/packages/lest/package.json +++ b/packages/lest/package.json @@ -1,6 +1,6 @@ { "name": "@taservers/lest", - "version": "2.1.0", + "version": "3.0.0", "license": "MIT", "description": "Painless Lua testing.", "homepage": "https://taservers.github.io/lest/", diff --git a/packages/lest/src/lua/cli.lua b/packages/lest/src/lua/cli.lua deleted file mode 100644 index 7075804..0000000 --- a/packages/lest/src/lua/cli.lua +++ /dev/null @@ -1,68 +0,0 @@ -local OPTIONS = { - config = { - takesArg = true, - default = "lest.config.lua", - }, - testTimeout = { - takesArg = true, - cast = function(val) - local num = tonumber(val) - assert(num, "Expected testTimeout to be a number") - return num - end, - }, -} - -local PREFIXED_OPTIONS = {} -for optionName, _ in pairs(OPTIONS) do - PREFIXED_OPTIONS["--" .. optionName] = optionName -end - -local function parseArgs(args) - local parsed = {} - - local argIdx = 1 - while argIdx <= #args do - local arg = args[argIdx] - local optionName = PREFIXED_OPTIONS[arg] - - if not optionName then - error("Unknown option: " .. arg) - end - - local option = OPTIONS[optionName] - if option.takesArg then - argIdx = argIdx + 1 - if not args[argIdx] then - error( - "Option " .. arg .. " takes one value, but none were given" - ) - end - - parsed[optionName] = args[argIdx] - else - parsed[optionName] = true - end - - argIdx = argIdx + 1 - end - - for optionName, option in pairs(OPTIONS) do - if option.takesArg then - if not parsed[optionName] then - parsed[optionName] = option.default - elseif option.cast then - parsed[optionName] = option.cast(parsed[optionName]) - end - end - end - - return parsed -end - ---- Parse command line args ----@param args string[] ----@return table -return function(args) - return parseArgs(args) -end diff --git a/packages/lest/src/lua/config.lua b/packages/lest/src/lua/config.lua new file mode 100644 index 0000000..5a2cf44 --- /dev/null +++ b/packages/lest/src/lua/config.lua @@ -0,0 +1,12 @@ +local ConfigLoader = require("src.lua.utils.configLoader") + +return ConfigLoader.new() + :registerProperty("testTimeout", { + type = "number", + default = 5000, + }) + :registerProperty("testMatch", { + type = "table", + default = { ".+%.test%.lua" }, + }) + :load(arg) diff --git a/packages/lest/src/lua/lest.lua b/packages/lest/src/lua/lest.lua index dec5cc7..7b12c31 100644 --- a/packages/lest/src/lua/lest.lua +++ b/packages/lest/src/lua/lest.lua @@ -1,12 +1,9 @@ require("src.lua.mocking") -local cli = require("src.lua.cli") local filesInFolder = require("src.lua.utils.filesInFolder") local runtime = require("src.lua.runtime") local prettyPrint = require("src.lua.prettyprint") - -local options = cli(arg) -local config = dofile(options.config) +local config = require("src.lua.config") local files = filesInFolder() local testFiles, testFilesCount = {}, 0 @@ -21,9 +18,7 @@ for _, filepath in ipairs(files) do end end -if options.testTimeout then - runtime.setDefaultTimeout(options.testTimeout) -elseif config.testTimeout then +if config.testTimeout then runtime.setDefaultTimeout(config.testTimeout) end diff --git a/packages/lest/src/lua/utils/cliParser.lua b/packages/lest/src/lua/utils/cliParser.lua new file mode 100644 index 0000000..718563a --- /dev/null +++ b/packages/lest/src/lua/utils/cliParser.lua @@ -0,0 +1,33 @@ +local OPTION_PATTERNS = { + "%-%-([%a%-]+)=([^%s]+)", + "%-%-([%a%-]+)", + "%-([%a%-]+)=([^%s]+)", + "%-([%a%-]+)", +} + +local function matchOption(arg) + for _, pattern in ipairs(OPTION_PATTERNS) do + local key, value = string.match(arg, pattern) + if key then + return key, value or "true" + end + end +end + +--- Parses command line options given as an arg array +---@param args string[] +---@return table +local function parseCliOptions(args) + local parsed = {} + + for _, arg in ipairs(args) do + local key, value = matchOption(arg) + if key and value then + parsed[key] = value + end + end + + return parsed +end + +return parseCliOptions diff --git a/packages/lest/src/lua/utils/configLoader.lua b/packages/lest/src/lua/utils/configLoader.lua new file mode 100644 index 0000000..1c67b5f --- /dev/null +++ b/packages/lest/src/lua/utils/configLoader.lua @@ -0,0 +1,101 @@ +local parseCliOptions = require("src.lua.utils.cliParser") + +---@class ConfigProperty +---@field default any +---@field type type +---@field cliOnly? boolean + +local CLI_ARGUMENT_CASTS_BY_TYPE = { + number = tonumber, + string = function(arg) + return arg + end, + boolean = function(arg) + return arg == "true" + end, + table = function(arg) + local tbl = {} + for match in arg:gmatch("([^,]+)") do + table.insert(tbl, match) + end + return tbl + end, +} + +--- Returns the appropriate value for a property given a CLI and config file value +---@param property ConfigProperty +---@param cliValue string? +---@param configFileValue any? +---@return any +local function getPropertyValue(property, cliValue, configFileValue) + if cliValue ~= nil then + local cast = CLI_ARGUMENT_CASTS_BY_TYPE[property.type] + or CLI_ARGUMENT_CASTS_BY_TYPE.string + return cast(cliValue) + end + + if configFileValue ~= nil and not property.cliOnly then + return configFileValue + end + + return property.default +end + +---@class ConfigLoader +---@field properties table +local ConfigLoader = {} +ConfigLoader.__index = ConfigLoader + +--- Create a new config loader +---@return ConfigLoader +function ConfigLoader.new() + return setmetatable({ properties = {} }, ConfigLoader) +end + +--- Registers a new configuration property +---@param name string +---@param options? ConfigProperty +---@return self +function ConfigLoader:registerProperty(name, options) + self.properties[name] = options + return self +end + +--- Loads a config with the registered properties +---@param cliArgs string[] +---@return table +function ConfigLoader:load(cliArgs) + local cliOptions = parseCliOptions(cliArgs) + + local configPath = cliOptions.config or cliOptions.c or "lest.config.lua" + local configLoaded, configFile = pcall(dofile, configPath) + if not configLoaded then + configFile = {} + end + + local mergedConfig = {} + for propertyName, property in pairs(self.properties) do + local propertyValue = getPropertyValue( + property, + cliOptions[propertyName], + configFile[propertyName] + ) + + if type(propertyValue) ~= property.type then + print( + string.format( + "Invalid config: Expected %s to be a %s", + propertyName, + property.type + ) + ) + os.exit(1) + end + + mergedConfig[propertyName] = propertyValue + end + + return mergedConfig +end + +return ConfigLoader