-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#31 - Add Test Coverage #107
Conversation
PS: I hope Bryce sleeps a little better knowing a repository out there in the world is slightly closer to 100% coverage. 🌛
## Changes - Use Dependency Injection on Commands for CommandClasses - Add some real tests for Commands - Fully covered! 🎉 - Core now injects an a client and commands into every command - Allows for reuse of commands in help function and other functions of the future :) - Commands now take an optional 3rd parameter that includes: - client - commands - Config - commandsPathGlob now lives in the config - commandTemplateFile has a new home - If we decide to later allow for ENV override on these it will be trivial. - These could probably be removed if they're only used in one place, but I also enjoy the dependency injection for testing purposes. Even the tests are using the real file paths This test is more like an integration test I think. This is fine. - Help command is now more readable and easier to test with dependency injection. - Full coverage ;)
I decided to remove the following from the config: - commandsPathGlob - commandTemplateFile They are only being used in a single place anyways. I also made sure they'll be easy to extract later if we ever need to inject them.
Also add some padding to match the original. Now that I can see what it looks like in the snap shot I can easily check :)
Seems to just be my stuff. I need to get my lint extension running on WSL :0!
A larger effort needs to be around this to makes all of these optional since not all commands will utilize all of these in the execute interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's UGE, It's not done, it should have been smaller, but it's ready for a more tested world! ^__^
!two → Two is not just a number. | ||
!blueFish → Not a red fish. | ||
\`\`\`" | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As much as I've been told the war stories of snapshots, this is nice because it gives us an idea what it actually looks like, and covers a lot of small semantics that would otherwise be a ton of tests to write. I'm still going to keep the other unit tests in place though. I flip flopped between whether or not to do snapshots, but for now I'll do mixed even though there's some redundancies.
import Commands from '../../src/library/commands'; | ||
import { message as mockMessage, MockedMessage } from '../mocks/discord'; | ||
|
||
// TODO: These should be in a factory/mock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the best way to do this with Jest is. I know there's a mock folder that can autoload but I need to learn more about that, and probably refactor our current mixed variations of mock directories. Some may not even be considered mocks or need to be reevaluated.
Since they are factories, they probably just need their own directory and to be included was needed.
I'm just used to beautiful Rspec factories with factorybot < 3
Not sure if there's something like that for Jest...
}); | ||
|
||
let sendMock: MockedMessage; | ||
let authorSend: MockedMessage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish there was lazy evaluated variables [like rspec], there are a few JS libraries that'll do this, but then you loose typing and autocomplete/typeahead, so I would prefer this nasty over that. As long as we're defining their typining with TS here then it's much better than the typeless Vanilla JS™ route
@@ -16,6 +16,7 @@ describe('CommandLoader', () => { | |||
}); | |||
}); | |||
|
|||
// More of an integration test against real commands |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this though as it will validate the commands rather than mocking them out.
|
||
test('.names returns command names', () => { | ||
const commandNames = ['hello', 'yac']; | ||
expect(commands.names).toEqual(commandNames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I should have a test block that says that command names are transformed. I would have to double check the code, but I think that they are turned into lower case, and are converted from kebab case to camel case if I recall correctly.
Maybe those tests belong in a different test or are already tested,, would be good to verify.
Ultimately it may be implicitly tested here, but I would like a block to explicitly state the business logic behind it for the sake of documentation.
const commandsPathGlob = './src/commands/*.ts'; | ||
const commandFiles = glob.sync(commandsPathGlob); | ||
const commandClasses = CommandLoader.getCommandClasses(commandFiles); | ||
const commands = new Commands(commandClasses); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 for Dependency Injection 🎈
return command.execute(args, msg, { | ||
client: this.client, | ||
commands | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working towards optional parameters. This is the only place that passes the data down, but not all commands utilize all the parameters, so I'm working to get these into an object and have the interface allow them all to be optional for the commands that are implementing it.
An issue related to this has been opened at: #109
execute(args: string[], msg: Message, extra?: { | ||
client?: Client, | ||
commands?: Commands | ||
}): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving towards the optional interface related to #109.
After these tests get merged we can work on getting the args
and msg
into the object as an optional parameter.
test('Console logs an error', () => { | ||
const errorMessage = "Malformed Google Search Response: {}"; | ||
expect(consoleErrorMock).lastCalledWith(errorMessage); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've flip flopped between vertical spacing on this project. I think last time I removed a lot of it tests, now I'm ready to put it back 😂
Maybe there's some linting that can be done around this 🤔
Maybe related to #108
this.commandFiles = glob.sync(COMMANDS_PATH_GLOB); | ||
this.all = CommandLoader.getCommandClasses(this.commandFiles); | ||
constructor(commandClasses: ICommandClasses) { | ||
this.all = commandClasses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so much cleaner, less cohesion, and testable with Dependency Injection™ 🎉
Changes
Related Issues