diff --git a/documentation/docs/changelog.md b/documentation/docs/changelog.md
index e9d119b1..4490f7a8 100644
--- a/documentation/docs/changelog.md
+++ b/documentation/docs/changelog.md
@@ -1,6 +1,12 @@
# Changelog
-## 2024.mm.yy - version 0.0.7-alpha
+## 2024.04.03 - version 0.0.8-alpha
+
+* Add a new support class to handle bool values
+* Add new support methods to Str
+* Reset the library instance for each test method for better mocking and less garbage between test cases
+
+## 2024.03.29 - version 0.0.7-alpha
* Add the Events facade class to serve as a way to improve event handling
* Listen to `PLAYER_LOGIN`
diff --git a/documentation/docs/resources/support/bool.md b/documentation/docs/resources/support/bool.md
new file mode 100644
index 00000000..e9d4e9aa
--- /dev/null
+++ b/documentation/docs/resources/support/bool.md
@@ -0,0 +1,28 @@
+# Bool
+
+The **Bool** support methods are focused on working and validating bool
+values.
+
+Each helper method is well documented in the support source code and won't
+be duplicated here. Please, refer to the `./src/Support/Bool.lua` for better
+reference.
+
+## Methods
+
+* `Bool:isTrue()` - Determines whether a value represents true or not.
+
+:::note Note about the absence of isFalse()
+
+Developers may notice this class has no `isFalse()` method.
+
+In terms of determining if a value is true, there's a limited
+range of values that can be considered true. On the other hand,
+anything else **shouldn't be considered false.** Consumers of this
+class can use `isTrue()` to determine if a value represents a true
+value, but using a `isFalse()` method would be a bit inconsistent.
+That said, instead of having a `isFalse()` method, consumers can
+use the `not` operator to determine if a value is false, which
+makes the code more readable, like: if this value is not true,
+then do something.
+
+:::
\ No newline at end of file
diff --git a/documentation/docs/resources/support/str.md b/documentation/docs/resources/support/str.md
index 03f23344..3aad9337 100644
--- a/documentation/docs/resources/support/str.md
+++ b/documentation/docs/resources/support/str.md
@@ -4,4 +4,29 @@ The **Str** methods are focused on manipulating strings.
Each helper method is well documented in the support source code and won't
be duplicated here. Please, refer to the `./src/Support/Src.lua` for better
-reference.
\ No newline at end of file
+reference.
+
+## Methods
+
+* `Str:isEmpty()` - Determines whether a string is empty or not.
+ * By empty, it means that the string is nil, has no characters, or has
+ only whitespace characters. This last case is important because a
+ string with only whitespace characters is not considered empty by Lua's
+ standards, but it is by this function's standards.
+* `Str:isNotEmpty()` - Determines whether a string is not empty.
+ * Read the `Str:isEmpty()` documentation above for clarification about
+ what this library considers when checking if a string is empty.
+* `Str:isQuoted()` - Determines whether a string is quoted by " or '.
+* `Str:isWrappedBy()` - Determines whether a string is wrapped by a prefix
+and a suffix.
+* `Str:removeQuotes()` - Removes quotes from a string.
+ * Addons shouldn't call `removeWrappers` twice for `"` and `'`, because
+ the string could be wrapped by one type of quote and contain the other
+ type inside it, so `removeQuotes` method first checks which type of
+ quote is wrapping the string and then removes it.
+* `Str:removeWrappers()` - Removes the wrapping strings from a string.
+* `Str:replaceAll()` - Replaces all occurrences of a substring in a string
+with another substring.
+* `Str:split()` - Splits a string in a table by breaking it where the separator is found.
+* `Str:trim()` - Removes all whitespace from the beginning and end of a
+string.
\ No newline at end of file
diff --git a/documentation/docs/testing/test-classes.md b/documentation/docs/testing/test-classes.md
new file mode 100644
index 00000000..74f31183
--- /dev/null
+++ b/documentation/docs/testing/test-classes.md
@@ -0,0 +1,69 @@
+# Test Classes
+
+Test classes in the Stormwind Library are organized in a way that makes it
+easy to run all tests at once although split into multiple files for each
+class in the src directory.
+
+The `./tests/unit.lua` file is the entry point for running all tests and it
+also defines a base test class that sets up the library before each test.
+Setting up the library before each test ensures that the library is in a
+clean state before each test is run, so mocking the library on tests won't
+affect the results of other tests.
+
+## Writing a test class
+
+In order to write a test class, it's highly recommended to follow a couple
+of standards to keep the tests organized, easy to read, and easy to
+maintain.
+
+As an example, consider the support classes in the library as they are good
+representatives of how a test class should be written.
+
+1. Start by creating a directory following the same structure as the src
+ directory, but inside tests
+1. Create a test file for each class in the src directory
+1. Define the test class starting with the **Test** prefix followed by the
+ name of the class being tested; this is important for the test runner to
+ identify the test class, otherwise it will be skipped
+1. Define a method for each test case starting with the **test** prefix
+1. Update the `./tests/unit.lua` file to include the test file, preferably
+ in alphabetical order
+
+## Getting the library instance in a test case
+
+A library instance is available in each test case through the global
+variable `__` and it's ready to be used without any further setup. However,
+if a test case requires a different setup, it's possible to instantiate a
+new library instance in the test case by doing this:
+
+```lua
+local __ = StormwindLibrary.new({
+ name = 'TestSuite'
+})
+```
+
+In the example above, `StormwindLibrary` is an alias for the library version
+being tested, so when new versions are released, it's only necessary to
+update the alias in the `./tests/unit.lua` file.
+
+## Mocking library properties and methods
+
+The library is set up before each test in the base test class, so mocking
+properties and methods of the library can be done in each test case without
+affecting other tests.
+
+To mock a property or method of the library, simply assign a new value to
+the property or method in the test case. For example, to mock the `name`
+property of the library, you can do the following:
+
+```lua
+__.name = 'MockedName'
+```
+
+To mock a method of the library, you can assign a new function to the
+method in the test case.
+
+There's no need to revert the mocked properties and methods back to their
+original values after the test case is run, unless the test method is
+expected to be called multiple times in the same test class and with
+different mocks.
\ No newline at end of file
diff --git a/src/Support/Bool.lua b/src/Support/Bool.lua
new file mode 100644
index 00000000..445412e2
--- /dev/null
+++ b/src/Support/Bool.lua
@@ -0,0 +1,39 @@
+--[[
+The Bool support class contains helper functions to manipulate boolean
+values.
+]]
+local Bool = {}
+ Bool.__index = Bool
+ Bool.__ = self
+
+ --[[
+ Determines whether a value represents true or not.
+
+ This method checks if a value is in a range of possible values that
+ represent a true value.
+
+ @NOTE: Developers may notice this class has no isFalse() method.
+ In terms of determining if a value is true, there's a limited
+ range of values that can be considered true. On the other hand,
+ anything else shouldn't be considered false. Consumers of this
+ class can use isTrue() to determine if a value represents a true
+ value, but using a isFalse() method would be a bit inconsistent.
+ That said, instead of having a isFalse() method, consumers can
+ use the not operator to determine if a value is false, which
+ makes the code more readable, like: if this value is not true,
+ then do something.
+
+ @tparam mixed value
+
+ @treturn bool
+ ]]
+ function Bool:isTrue(value)
+ local inArray, index = self.__.arr:inArray({1, "1", "true", true, "yes"}, value)
+
+ -- it returns just the first inArray result, as the second value is the index
+ -- which makes no sense in this context
+ return inArray
+ end
+-- end of Bool
+
+self.bool = Bool
\ No newline at end of file
diff --git a/src/Support/Str.lua b/src/Support/Str.lua
index e4e593b2..65f9a1e3 100644
--- a/src/Support/Str.lua
+++ b/src/Support/Str.lua
@@ -4,6 +4,120 @@ The Str support class contains helper functions to manipulate strings.
local Str = {}
Str.__index = Str
+ --[[
+ Determines whether a string is empty or not.
+
+ By empty, it means that the string is nil, has no characters, or has only
+ whitespace characters. This last case is important because a string with
+ only whitespace characters is not considered empty by Lua's standards,
+ but it is by this function's standards.
+
+ If a method shouldn't consider a string with only whitespace characters
+ as empty, please do not use this function.
+
+ @tparam string value
+
+ @treturn bool
+ ]]
+ function Str:isEmpty(value)
+ return value == nil or (string.len(self:trim(value)) == 0)
+ end
+
+ --[[
+ Determines whether a string is not empty.
+
+ This function is the opposite of Str:isEmpty.
+
+ @tparam string value
+
+ @treturn bool
+ ]]
+ function Str:isNotEmpty(value)
+ return not self:isEmpty(value)
+ end
+
+ --[[
+ Determines whether a string is quoted by " or '.
+
+ @tparam string value
+
+ @treturn bool
+ ]]
+ function Str:isQuoted(value)
+ return self:isWrappedBy(value, '"') or self:isWrappedBy(value, "'")
+ end
+
+ --[[
+ Determines whether a string is wrapped by a prefix and a suffix.
+
+ This function is useful to determine if a string is wrapped by a pair of
+ strings, like quotes, parentheses, brackets, etc.
+
+ The third parameter is optional. If it is not provided, the function will
+ assume that the prefix and suffix are the same.
+
+ Finally, this function will return true if the string contains only the
+ prefix and suffix, like "", "()", "[]", etc. That would mean that an
+ empty string is considered wrapped by something.
+
+ @tparam string value
+ @tparam string wrapper
+ @tparam string endWrapper, optional
+
+ @treturn bool
+ ]]
+ function Str:isWrappedBy(value, wrapper, endWrapper)
+ endWrapper = endWrapper or wrapper
+
+ return
+ (value ~= nil) and
+ (wrapper ~= nil) and
+ (value ~= wrapper) and
+ (value:sub(1, #wrapper) == wrapper and value:sub(-#endWrapper) == endWrapper)
+ end
+
+ --[[
+ Removes quotes from a string.
+
+ This method can't simply call removeWrappers twice for " or ', because
+ the string could be wrapped by one type of quote and contain the other
+ type inside it, so it first checks which type of quote is wrapping the
+ string and then removes it.
+
+ @tparam string value
+
+ @treturn string
+ ]]
+ function Str:removeQuotes(value)
+ if self:isWrappedBy(value, '"') then
+ return self:removeWrappers(value, '"')
+ end
+
+ return self:removeWrappers(value, "'")
+ end
+
+ --[[
+ Removes the wrapping strings from a string.
+
+ This function is useful to remove quotes, parentheses, brackets, etc,
+ from a string.
+
+ Similarly to Str:isWrappedBy, the third parameter is optional. If it is
+ not provided, the function will assume that the prefix and suffix are
+ the same.
+
+ @tparam string value
+ @tparam string wrapper
+ @tparam string endWrapper, optional
+
+ @treturn string
+ ]]
+ function Str:removeWrappers(value, wrapper, endWrapper)
+ return self:isWrappedBy(value, wrapper, endWrapper)
+ and value:sub(#wrapper + 1, -#(endWrapper or wrapper) - 1)
+ or value
+ end
+
--[[
Replaces all occurrences of a substring in a string with another
substring.
@@ -37,6 +151,17 @@ local Str = {}
end
return values
end
+
+ --[[
+ Removes all whitespace from the beginning and end of a string.
+
+ @tparam string value
+
+ @treturn string
+ ]]
+ function Str:trim(value)
+ return value and value:gsub("^%s*(.-)%s*$", "%1") or value
+ end
-- end of Str
self.str = Str
\ No newline at end of file
diff --git a/src/stormwind-library.lua b/src/stormwind-library.lua
index 5913f7ae..f6e41baf 100644
--- a/src/stormwind-library.lua
+++ b/src/stormwind-library.lua
@@ -1,6 +1,7 @@
--- Library version = '0.0.7'
+-- Library version = '0.0.8'
-- import src/Support/Arr.lua
+-- import src/Support/Bool.lua
-- import src/Support/Str.lua
-- import src/Core/AddonProperties.lua
diff --git a/tests/Commands/CommandsHandlerTest.lua b/tests/Commands/CommandsHandlerTest.lua
index c69b176c..8bb08e57 100644
--- a/tests/Commands/CommandsHandlerTest.lua
+++ b/tests/Commands/CommandsHandlerTest.lua
@@ -1,4 +1,4 @@
-TestCommandsHandler = {}
+TestCommandsHandler = BaseTestClass:new()
-- @covers StormwindLibrary:add()
function TestCommandsHandler:testAdd()
local handler = __.commands
@@ -141,15 +141,11 @@ TestCommandsHandler = {}
local outputInvoked = false
- local originalOut = __.output.out
-
function __.output:out() outputInvoked = true end
handler:printHelp()
lu.assertEquals(shouldOutput, outputInvoked)
-
- __.output.out = originalOut
end
execution(nil, false)
@@ -160,8 +156,6 @@ TestCommandsHandler = {}
-- @covers StormwindLibrary:register()
function TestCommandsHandler:testRegister()
local function execution(command, expectedGlobalSlashCommandIndex, expectedSlashCommand, expectedSlashCmdListIndex)
- -- save the current data to restore them after the test
- local currentCommand = __.addon.command
local currentSlashCmdList = SlashCmdList
-- mocks the properties for this test
@@ -179,8 +173,6 @@ TestCommandsHandler = {}
lu.assertIsFunction(SlashCmdList[expectedSlashCmdListIndex])
end
- -- restore the command after the test
- __.addon.command = currentCommand
SlashCmdList = currentSlashCmdList
end
diff --git a/tests/Commands/CommandsTest.lua b/tests/Commands/CommandsTest.lua
index 14a46931..78035713 100644
--- a/tests/Commands/CommandsTest.lua
+++ b/tests/Commands/CommandsTest.lua
@@ -1,4 +1,4 @@
-TestCommand = {}
+TestCommand = BaseTestClass:new()
-- @covers Command:setCallback()
-- @covers Command:setDescription()
-- @covers Command:setOperation()
diff --git a/tests/Core/AddonPropertiesTest.lua b/tests/Core/AddonPropertiesTest.lua
index dc8f5076..268cb7e4 100644
--- a/tests/Core/AddonPropertiesTest.lua
+++ b/tests/Core/AddonPropertiesTest.lua
@@ -1,4 +1,4 @@
-TestAddonProperties = {}
+TestAddonProperties = BaseTestClass:new()
-- @covers AddonProperties.lua
function TestAddonProperties:testPropertiesAreSet()
local library = StormwindLibrary.new({
diff --git a/tests/Core/FactoryTest.lua b/tests/Core/FactoryTest.lua
index b8e5aa44..7dd92864 100644
--- a/tests/Core/FactoryTest.lua
+++ b/tests/Core/FactoryTest.lua
@@ -1,10 +1,10 @@
-TestFactory = {}
+TestFactory = BaseTestClass:new()
--[[
@covers Factory.classes
@covers Factory:addClass()
@covers Factory:new()
]]
- function TestFactory:testCanInstantiateClasses()
+ function TestFactory:testClassInstantiation()
local MockClass = {}
MockClass.__index = MockClass
@@ -14,7 +14,9 @@ TestFactory = {}
return self
end
- local library = newLibrary()
+ local library = StormwindLibrary.new({
+ name = 'test-library'
+ })
library:addClass('MockClass', MockClass)
lu.assertNotIsNil(library.classes)
diff --git a/tests/Core/OutputTest.lua b/tests/Core/OutputTest.lua
index e32b7ea8..ddb2cfd6 100644
--- a/tests/Core/OutputTest.lua
+++ b/tests/Core/OutputTest.lua
@@ -1,14 +1,10 @@
-TestOutput = {}
+TestOutput = BaseTestClass:new()
-- @covers Output:color()
function TestOutput:testColor()
local function execution(value, color, primaryColor, expectedOutput)
- local originalPrimaryColor = __.addon.colors.primary
-
__.addon.colors.primary = primaryColor
lu.assertEquals(expectedOutput, __.output:color(value, color))
-
- __.addon.colors.primary = originalPrimaryColor
end
execution('test', nil, nil, 'test')
diff --git a/tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua b/tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua
index ab83465a..93046c86 100644
--- a/tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua
+++ b/tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua
@@ -1,4 +1,4 @@
-TestPlayerLoginEventHandler = {}
+TestPlayerLoginEventHandler = BaseTestClass:new()
-- @covers PlayerLoginEventHandler.lua
function TestPlayerLoginEventHandler:testEventNameIsSet()
lu.assertEquals('PLAYER_LOGIN', __.events.EVENT_NAME_PLAYER_LOGIN)
diff --git a/tests/Facades/EventHandlers/TargetEventHandlerTest.lua b/tests/Facades/EventHandlers/TargetEventHandlerTest.lua
index 97595f1f..09670252 100644
--- a/tests/Facades/EventHandlers/TargetEventHandlerTest.lua
+++ b/tests/Facades/EventHandlers/TargetEventHandlerTest.lua
@@ -1,4 +1,4 @@
-TestTargetEventHandler = {}
+TestTargetEventHandler = BaseTestClass:new()
-- @covers TargetEventHandler.lua
function TestTargetEventHandler:testEventNamesAreSet()
lu.assertEquals('PLAYER_TARGET', __.events.EVENT_NAME_PLAYER_TARGET)
@@ -19,8 +19,6 @@ TestTargetEventHandler = {}
-- @covers Events:playerTargetChangedListener()
function TestTargetEventHandler:testPlayerTargetChangedListener()
local function execution(playerHadTarget, playerHasTarget, expectedEvent, expectedPlayerHadTargetState)
- __ = newLibrary()
-
local targetMock = __:new('Target')
targetMock.hasTarget = function() return playerHasTarget end
__.target = targetMock
diff --git a/tests/Facades/EventsTest.lua b/tests/Facades/EventsTest.lua
index 5398634e..ea3d9019 100644
--- a/tests/Facades/EventsTest.lua
+++ b/tests/Facades/EventsTest.lua
@@ -1,4 +1,4 @@
-TestEvents = {}
+TestEvents = BaseTestClass:new()
-- @covers Events:createFrame()
function TestEvents:testCreateFrame()
local events = __:new('Events')
diff --git a/tests/Facades/TargetTest.lua b/tests/Facades/TargetTest.lua
index 765805e0..8b9ea518 100644
--- a/tests/Facades/TargetTest.lua
+++ b/tests/Facades/TargetTest.lua
@@ -1,4 +1,4 @@
-TestTarget = {}
+TestTarget = BaseTestClass:new()
-- @covers StormwindLibrary.target
function TestTarget:testGetTargetFacade()
local target = __.target
diff --git a/tests/Models/MacroTest.lua b/tests/Models/MacroTest.lua
index cb4ad66b..4517898f 100644
--- a/tests/Models/MacroTest.lua
+++ b/tests/Models/MacroTest.lua
@@ -1,4 +1,4 @@
-TestMacro = {}
+TestMacro = BaseTestClass:new()
-- @covers Macro:__construct()
function TestMacro:testInstantiate()
local macro = __:new('Macro', 'test-macro')
diff --git a/tests/Models/RaidMarkerTest.lua b/tests/Models/RaidMarkerTest.lua
index 13778572..f690c6b3 100644
--- a/tests/Models/RaidMarkerTest.lua
+++ b/tests/Models/RaidMarkerTest.lua
@@ -1,4 +1,4 @@
-TestRaidMarker = {}
+TestRaidMarker = BaseTestClass:new()
-- @covers RaidMarker::getPrintableString()
function TestRaidMarker:testGetPrintableString()
lu.assertEquals('\124TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8:0\124t', __.raidMarkers['skull']:getPrintableString())
diff --git a/tests/Support/ArrTest.lua b/tests/Support/ArrTest.lua
index 68b28d71..cf909101 100644
--- a/tests/Support/ArrTest.lua
+++ b/tests/Support/ArrTest.lua
@@ -1,6 +1,13 @@
-TestArr = {}
+TestArr = BaseTestClass:new()
+ -- @covers Arr
+ function TestArr:testArrInstanceIsSet()
+ local arr = __.arr
+
+ lu.assertNotIsNil(arr)
+ end
+
-- @covers Arr:get()
- function TestArr:testCanGet()
+ function TestArr:testGet()
local function execution(list, key, default, expectedOutput)
lu.assertEquals(expectedOutput, __.arr:get(list, key, default))
end
@@ -19,15 +26,8 @@ TestArr = {}
execution(listWithNestedKeys, 'test-a.test-b.test-c', nil, 'test')
end
- -- @covers Arr
- function TestArr:testCanGetInstance()
- local arr = __.arr
-
- lu.assertNotIsNil(arr)
- end
-
-- @covers Arr:implode()
- function TestArr:testCanImplode()
+ function TestArr:testImplode()
local arr = __.arr
local delimiter = ','
@@ -39,7 +39,7 @@ TestArr = {}
end
-- @covers Arr:implode()
- function TestArr:testCanImplodeWithNonList()
+ function TestArr:testImplodeWithNonList()
local arr = __.arr
local text = 'test'
@@ -48,61 +48,6 @@ TestArr = {}
lu.assertEquals(text, result)
end
- -- @covers Arr:insertNotInArray()
- function TestArr:testCanInsertNotInArray()
- local function execution(list, value, expectedBooleanResult, expectedListResult)
- local booleanResult = __.arr:insertNotInArray(list, value)
-
- lu.assertEquals(expectedBooleanResult, booleanResult)
- lu.assertEquals(expectedListResult, list)
- end
-
- execution('a', 'a', false, 'a')
- execution({}, 'a', true, {'a'})
- execution({'a'}, 'a', false, {'a'})
- execution({'a'}, 'b', true, {'a', 'b'})
- end
-
- -- @covers Arr:map()
- function TestArr:testCanMap()
- local function execution(list, expectedOutput)
- local arr = __.arr
-
- local results = __.arr:map(list, function (val, i)
- return val .. '-' .. i
- end)
-
- lu.assertEquals(expectedOutput, results)
- end
-
- execution({}, {})
- execution({'test', 'test', 'test'}, {'test-1', 'test-2', 'test-3'})
- execution({['a'] = 'a', ['b'] = 'b', ['c'] = 'c'}, {['a'] = 'a-a', ['b'] = 'b-b', ['c'] = 'c-c'})
- end
-
- -- @covers Arr:set()
- function TestArr:testCanSet()
- local arr = __.arr
-
- local list = {}
- list['a'] = {}
- list['a']['b'] = 'test-initial'
-
- -- sanity checks to make sure the list is consistent
- lu.assertEquals('test-initial', arr:get(list, 'a.b'))
- lu.assertIsNil(arr:get(list, 'a.c'))
- lu.assertIsNil(arr:get(list, 'x.y.z'))
-
- -- sets a couple of properties
- arr:set(list, 'a.c', 'test-with-set')
- arr:set(list, 'x.y.z', 'test-with-three-levels')
-
- -- checks if the property
- lu.assertEquals('test-with-set', arr:get(list, 'a.c'))
- lu.assertEquals('test-with-three-levels', arr:get(list, 'x.y.z'))
- lu.assertEquals('test-initial', arr:get(list, 'a.b'))
- end
-
-- @covers Arr:inArray()
function TestArr:testInArray()
local function execution(list, value, expectedResult, expectedIndex)
@@ -136,6 +81,21 @@ TestArr = {}
execution({objectA, objectB}, objectB, true, 2)
end
+ -- @covers Arr:insertNotInArray()
+ function TestArr:testInsertNotInArray()
+ local function execution(list, value, expectedBooleanResult, expectedListResult)
+ local booleanResult = __.arr:insertNotInArray(list, value)
+
+ lu.assertEquals(expectedBooleanResult, booleanResult)
+ lu.assertEquals(expectedListResult, list)
+ end
+
+ execution('a', 'a', false, 'a')
+ execution({}, 'a', true, {'a'})
+ execution({'a'}, 'a', false, {'a'})
+ execution({'a'}, 'b', true, {'a', 'b'})
+ end
+
-- @covers Arr:isArray()
function TestArr:testIsArray()
local arr = __.arr
@@ -151,6 +111,23 @@ TestArr = {}
lu.assertIsFalse(arr:isArray(tableWithStringKeys))
end
+ -- @covers Arr:map()
+ function TestArr:testMap()
+ local function execution(list, expectedOutput)
+ local arr = __.arr
+
+ local results = __.arr:map(list, function (val, i)
+ return val .. '-' .. i
+ end)
+
+ lu.assertEquals(expectedOutput, results)
+ end
+
+ execution({}, {})
+ execution({'test', 'test', 'test'}, {'test-1', 'test-2', 'test-3'})
+ execution({['a'] = 'a', ['b'] = 'b', ['c'] = 'c'}, {['a'] = 'a-a', ['b'] = 'b-b', ['c'] = 'c-c'})
+ end
+
-- @covers Arr:maybeInitialize()
function TestArr:testMaybeInitialize()
local function execution(list, key, value, expectedValue)
@@ -199,6 +176,29 @@ TestArr = {}
execution({a = 'a', b = 'b', c = 'c'}, 'a', {a = 'a', b = 'b', c = 'c'})
end
+ -- @covers Arr:set()
+ function TestArr:testSet()
+ local arr = __.arr
+
+ local list = {}
+ list['a'] = {}
+ list['a']['b'] = 'test-initial'
+
+ -- sanity checks to make sure the list is consistent
+ lu.assertEquals('test-initial', arr:get(list, 'a.b'))
+ lu.assertIsNil(arr:get(list, 'a.c'))
+ lu.assertIsNil(arr:get(list, 'x.y.z'))
+
+ -- sets a couple of properties
+ arr:set(list, 'a.c', 'test-with-set')
+ arr:set(list, 'x.y.z', 'test-with-three-levels')
+
+ -- checks if the property
+ lu.assertEquals('test-with-set', arr:get(list, 'a.c'))
+ lu.assertEquals('test-with-three-levels', arr:get(list, 'x.y.z'))
+ lu.assertEquals('test-initial', arr:get(list, 'a.b'))
+ end
+
-- @covers Arr:wrap()
function TestArr:testWrap()
local function execution(value, expectedOutput)
diff --git a/tests/Support/BoolTest.lua b/tests/Support/BoolTest.lua
new file mode 100644
index 00000000..b2f6330d
--- /dev/null
+++ b/tests/Support/BoolTest.lua
@@ -0,0 +1,27 @@
+TestBool = BaseTestClass:new()
+ -- @covers StormwindLibrary.bool
+ function TestBool:testBoolInstanceIsSet()
+ local bool = __.bool
+
+ lu.assertNotIsNil(bool)
+ end
+
+ -- @covers Bool:isTrue()
+ function TestBool:testIsTrue()
+ local bool = __.bool
+
+ lu.assertTrue(bool:isTrue(1))
+ lu.assertTrue(bool:isTrue("1"))
+ lu.assertTrue(bool:isTrue('1'))
+ lu.assertTrue(bool:isTrue("true"))
+ lu.assertTrue(bool:isTrue(true))
+ lu.assertTrue(bool:isTrue("yes"))
+
+ lu.assertFalse(bool:isTrue(0))
+ lu.assertFalse(bool:isTrue("0"))
+ lu.assertFalse(bool:isTrue("false"))
+ lu.assertFalse(bool:isTrue(false))
+ lu.assertFalse(bool:isTrue("no"))
+ lu.assertFalse(bool:isTrue(nil))
+ end
+-- end of TestBool
\ No newline at end of file
diff --git a/tests/Support/StrTest.lua b/tests/Support/StrTest.lua
index 9fc47c6f..466a5329 100644
--- a/tests/Support/StrTest.lua
+++ b/tests/Support/StrTest.lua
@@ -1,4 +1,113 @@
-TestStr = {}
+TestStr = BaseTestClass:new()
+ -- @covers Str:isEmpty()
+ -- @covers Str:isNotEmpty()
+ function TestStr:testIsEmpty()
+ local function execution(value, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:isEmpty(value))
+ lu.assertEquals(not expectedOutput, __.str:isNotEmpty(value))
+ end
+
+ execution(nil, true)
+ execution('', true)
+ execution(' ', true)
+ execution('a', false)
+ execution(' a', false)
+ execution('a ', false)
+ end
+
+ -- @covers Str:isQuoted()
+ function TestStr:testIsQuoted()
+ local function execution(value, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:isQuoted(value))
+ end
+
+ execution(nil, false)
+ execution('', false)
+ execution(' ', false)
+ execution('a', false)
+
+ execution('"a"', true)
+ execution("'a'", true)
+
+ execution('""', true)
+ execution("''", true)
+
+ execution('"a', false)
+ execution('a"', false)
+ end
+
+ -- @covers Str:isWrappedBy()
+ function TestStr:testIsWrappedBy()
+ local function execution(value, wrapper, endWrapper, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:isWrappedBy(value, wrapper, endWrapper))
+ end
+
+ execution('', '<', '>', true)
+ execution('(a>', '(', ')', false)
+ execution('""', '"', nil, true)
+ execution('"a"', '"', nil, true)
+ execution("'a'", "'", nil, true)
+ execution("''", "'", nil, true)
+ execution('"a', '"', nil, false)
+ execution('a', '', nil, false)
+
+ -- edge cases
+ execution('a', 'a', nil, false)
+ execution('', '', '', false)
+ execution(nil, '', '', false)
+ execution('', nil, '', false)
+ execution(nil, nil, nil, false)
+ end
+
+ -- @covers Str:removeQuotes()
+ function TestStr:testRemoveQuotes()
+ local function execution(value, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:removeQuotes(value))
+ end
+
+ execution(nil, nil)
+ execution('', '')
+ execution(' ', ' ')
+ execution('a', 'a')
+
+ execution('"a"', 'a')
+ execution("'a'", 'a')
+ execution('""', '')
+ execution("''", '')
+
+ -- quoted quotes
+ execution('"\'\'"', "''")
+ execution("'\"\"'", '""')
+ execution('"\'a\'"', "'a'")
+ execution("'\"a\"'", '"a"')
+ end
+
+ -- @covers Str:removeWrappers()
+ function TestStr:testRemoveWrappers()
+ local function execution(value, wrapper, endWrapper, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:removeWrappers(value, wrapper, endWrapper))
+ end
+
+ execution('', '<', '>', 'a')
+ execution('_(a)_', '_(', ')_', 'a')
+ execution('""', '"', nil, '')
+ execution('"a"', '"', nil, 'a')
+ execution("'a'", "'", nil, 'a')
+ execution("''", "'", nil, '')
+ execution('"a', '"', nil, '"a')
+ execution('a', '', nil, 'a')
+
+ -- won't remove internal wrappers
+ execution('"a"b"c"', '"', nil, 'a"b"c')
+
+ -- edge cases
+ execution('a', 'a', nil, 'a')
+ execution('', '', '', '')
+ execution(nil, '', '', nil)
+ execution('', nil, '', '')
+ execution(nil, nil, nil, nil)
+ end
+
-- @covers Str:replaceAll()
function TestStr:testReplaceAll()
local function execution(value, find, replace, expectedOutput)
@@ -32,4 +141,24 @@ TestStr = {}
execution('test-a.test-b.test-c', '.', {'test-a', 'test-b', 'test-c'})
execution('test-a test-b test-c', ' ', {'test-a', 'test-b', 'test-c'})
end
+
+ -- @covers Str:trim()
+ function TestStr:testTrim()
+ local function execution(value, expectedOutput)
+ lu.assertEquals(expectedOutput, __.str:trim(value))
+ end
+
+ execution(nil, nil)
+ execution('', '')
+ execution(' ', '')
+ execution(' ', '')
+ execution(' ', '')
+ execution('a', 'a')
+ execution(' a', 'a')
+ execution('a ', 'a')
+ execution(' a ', 'a')
+ execution(' a ', 'a')
+ execution(' a b ', 'a b')
+ execution(' a b ', 'a b')
+ end
-- end of TestStr
\ No newline at end of file
diff --git a/tests/unit.lua b/tests/unit.lua
index 341835ad..056635de 100644
--- a/tests/unit.lua
+++ b/tests/unit.lua
@@ -14,42 +14,57 @@ end
-- End
dofile('./dist/stormwind-library.lua')
-StormwindLibrary = StormwindLibrary_v0_0_7
-function newLibrary() return StormwindLibrary.new({
- name = 'TestSuite'
-}) end
+StormwindLibrary = StormwindLibrary_v0_0_8
--[[
-This allows the library to be reloaded between tests, which is useful when
-mocking it on tests could affect the results of other tests.
+This is a base test class that sets up the library before each test.
-@NOTE: LuaUnit provides a setUp() method that could be used to reset the
- library before each test, but it wasn't working properly.
+Every test class should inherit from this class to have the library set up
+before each test. That way, mocking the library on tests won't affect the
+results of other tests.
-@TODO: Use LuaUnit's setUp() method to reset the library before each test <2024.03.27>
+The setUp() method is expected to be called before each test.
]]
-function runTests(file)
- __ = newLibrary()
- dofile(file)
-end
+BaseTestClass = {
+ new = function(self)
+ local instance = {}
+ setmetatable(instance, self)
+ self.__index = self
+ return instance
+ end,
+
+ setUp = function()
+ __ = StormwindLibrary.new({
+ name = 'TestSuite'
+ })
+ end,
+
+ -- guarantees that every test class inherits from this class by forcing
+ -- the global library usages to throw an error if it's not set, so
+ -- tests that miss inheriting from this class will fail
+ tearDown = function()
+ __ = nil
+ end,
+}
-runTests('./tests/Commands/CommandsTest.lua')
-runTests('./tests/Commands/CommandsHandlerTest.lua')
+dofile('./tests/Commands/CommandsTest.lua')
+dofile('./tests/Commands/CommandsHandlerTest.lua')
-runTests('./tests/Core/AddonPropertiesTest.lua')
-runTests('./tests/Core/FactoryTest.lua')
-runTests('./tests/Core/OutputTest.lua')
+dofile('./tests/Core/AddonPropertiesTest.lua')
+dofile('./tests/Core/FactoryTest.lua')
+dofile('./tests/Core/OutputTest.lua')
-runTests('./tests/Facades/EventsTest.lua')
-runTests('./tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua')
-runTests('./tests/Facades/EventHandlers/TargetEventHandlerTest.lua')
-runTests('./tests/Facades/TargetTest.lua')
+dofile('./tests/Facades/EventsTest.lua')
+dofile('./tests/Facades/EventHandlers/PlayerLoginEventHandlerTest.lua')
+dofile('./tests/Facades/EventHandlers/TargetEventHandlerTest.lua')
+dofile('./tests/Facades/TargetTest.lua')
-runTests('./tests/Models/MacroTest.lua')
-runTests('./tests/Models/RaidMarkerTest.lua')
+dofile('./tests/Models/MacroTest.lua')
+dofile('./tests/Models/RaidMarkerTest.lua')
-runTests('./tests/Support/ArrTest.lua')
-runTests('./tests/Support/StrTest.lua')
+dofile('./tests/Support/ArrTest.lua')
+dofile('./tests/Support/BoolTest.lua')
+dofile('./tests/Support/StrTest.lua')
lu.ORDER_ACTUAL_EXPECTED=false