Skip to content
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

Merged
merged 13 commits into from
Apr 25, 2020
Merged

#31 - Add Test Coverage #107

merged 13 commits into from
Apr 25, 2020

Conversation

ctsstc
Copy link
Member

@ctsstc ctsstc commented Apr 11, 2020

Changes

  • Add tests for Help command
  • Refactor Help command
    • Easier to read
    • Split out some meat into a new method.
  • Mute and test console error logging from Search command
  • Change library/command to use Dependency Injection™
    • Better tests, better pizza, Papa Yongs
  • Add additional tests to library/command
  • Core now pushes a 3rd parameter that Command can optionally use. The 3rd parameter provides the client and the loaded commands.
    • Help command can now utilize the already loaded commands without instantiating a new instance.
      • More Dependency Injection.

Related Issues

ctsstc added 9 commits April 10, 2020 03:05
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 :)
@ctsstc ctsstc requested a review from BryceGraves April 11, 2020 21:08
ctsstc added 3 commits April 11, 2020 16:18
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.
Copy link
Member Author

@ctsstc ctsstc left a 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.
\`\`\`"
`;
Copy link
Member Author

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
Copy link
Member Author

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;
Copy link
Member Author

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
Copy link
Member Author

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);
Copy link
Member Author

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);
Copy link
Member Author

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
});
Copy link
Member Author

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;
Copy link
Member Author

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);
});
Copy link
Member Author

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;
Copy link
Member Author

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™ 🎉

@ctsstc ctsstc mentioned this pull request Apr 20, 2020
@ctsstc ctsstc merged commit 3d6f57e into master Apr 25, 2020
@ctsstc ctsstc deleted the 31-covered-up branch April 25, 2020 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant