Skip to content

Commit

Permalink
Merge pull request #16 from adrianocastro189/release/version-0.0.8-alpha
Browse files Browse the repository at this point in the history
Release - 0.0.8-alpha
  • Loading branch information
adrianocastro189 authored Apr 3, 2024
2 parents 5cc5473 + e26f5f3 commit 19e5cb4
Show file tree
Hide file tree
Showing 22 changed files with 575 additions and 123 deletions.
8 changes: 7 additions & 1 deletion documentation/docs/changelog.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down
28 changes: 28 additions & 0 deletions documentation/docs/resources/support/bool.md
Original file line number Diff line number Diff line change
@@ -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.

:::
27 changes: 26 additions & 1 deletion documentation/docs/resources/support/str.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
69 changes: 69 additions & 0 deletions documentation/docs/testing/test-classes.md
Original file line number Diff line number Diff line change
@@ -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.
39 changes: 39 additions & 0 deletions src/Support/Bool.lua
Original file line number Diff line number Diff line change
@@ -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
125 changes: 125 additions & 0 deletions src/Support/Str.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
3 changes: 2 additions & 1 deletion src/stormwind-library.lua
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 1 addition & 9 deletions tests/Commands/CommandsHandlerTest.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
TestCommandsHandler = {}
TestCommandsHandler = BaseTestClass:new()
-- @covers StormwindLibrary:add()
function TestCommandsHandler:testAdd()
local handler = __.commands
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -179,8 +173,6 @@ TestCommandsHandler = {}
lu.assertIsFunction(SlashCmdList[expectedSlashCmdListIndex])
end

-- restore the command after the test
__.addon.command = currentCommand
SlashCmdList = currentSlashCmdList
end

Expand Down
2 changes: 1 addition & 1 deletion tests/Commands/CommandsTest.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
TestCommand = {}
TestCommand = BaseTestClass:new()
-- @covers Command:setCallback()
-- @covers Command:setDescription()
-- @covers Command:setOperation()
Expand Down
2 changes: 1 addition & 1 deletion tests/Core/AddonPropertiesTest.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
TestAddonProperties = {}
TestAddonProperties = BaseTestClass:new()
-- @covers AddonProperties.lua
function TestAddonProperties:testPropertiesAreSet()
local library = StormwindLibrary.new({
Expand Down
Loading

0 comments on commit 19e5cb4

Please sign in to comment.