Skip to content

Commit d372ecc

Browse files
authored
Merge pull request #66 from osu-cascades/dev
RC v2.0.1
2 parents 9d491d3 + c56e970 commit d372ecc

27 files changed

+306
-181
lines changed

.env-example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#DEFAULT_CHANNEL=CHANGEME
2+
#WELCOME_CHANNEL=CHANGEME
23
DISCORD_APP_TOKEN=CHANGEME
34
GOOGLE_API_KEY=CHANGEME
45
GOOGLE_SEARCH_ENGINE_ID=CHANGEME

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# hackbot
22

3-
|branch|build status|bots|
4-
|---|---|---|
5-
|master|[![Build Status](https://travis-ci.org/osu-cascades/hackbot.svg?branch=master)](https://travis-ci.org/osu-cascades/hackbot)| [![Greenkeeper badge](https://badges.greenkeeper.io/osu-cascades/hackbot.svg)](https://greenkeeper.io/) |
6-
|staging|[![Build Status](https://travis-ci.org/osu-cascades/hackbot.svg?branch=staging)](https://travis-ci.org/osu-cascades/hackbot)|
7-
3+
|What|Badge|
4+
|---|---|
5+
|Master Build|[![Build Status](https://travis-ci.org/osu-cascades/hackbot.svg?branch=master)](https://travis-ci.org/osu-cascades/hackbot)|
6+
|Staging Build|[![Build Status](https://travis-ci.org/osu-cascades/hackbot.svg?branch=dev)](https://travis-ci.org/osu-cascades/hackbot)|
7+
|Maintainability|[![Maintainability](https://api.codeclimate.com/v1/badges/96320fe592c30381915f/maintainability)](https://codeclimate.com/github/osu-cascades/hackbot)|
8+
|Test Coverage|[![Test Coverage](https://api.codeclimate.com/v1/badges/96320fe592c30381915f/test_coverage)](https://codeclimate.com/github/osu-cascades/hackbot)|
9+
|GreenKeeper|[![Greenkeeper badge](https://badges.greenkeeper.io/osu-cascades/hackbot.svg)](https://greenkeeper.io/)|
810

911
A Discord bot for the Cascades Tech Club [Discord](http://discordapp.com) server. To add a command, see the [Commands](#commands) section below.
1012

@@ -36,7 +38,7 @@ In a nutshell, work in the [dev](https://github.com/osu-cascades/hackbot/tree/de
3638

3739
In all cases, be sure to run the test suite to make sure all tests pass. _All tests should be passing before you merge dev to master_.
3840

39-
You should embrace testing. _hackbot_ uses the [Jest](https://facebook.github.io/jest/) test framework. Have two console panes open: one for running and watching the test suite, and the other for everything else you need to do. You can run the test suite once with `npm test`. Once you get tired of running `npm test` manually, use the watcher by running `npm run test:watch`. It is sweet and people will think you are a super hacker. Look at `__tests__/commands/add.test.ts` for an example of how to test commands and their channel messages.
41+
You should embrace testing. _hackbot_ uses the [Jest](https://facebook.github.io/jest/) test framework. Have two console panes open: one for running and watching the test suite, and the other for everything else you need to do. You can run the test suite once with `npm test`. Once you get tired of running `npm test` manually, use the watcher by running `npm run test:watch`. It is sweet and people will think you are a super hacker. Look at `__tests__/commands/add.test.ts` for an example of how to test commands and their channel messages. If you use VSCode for your IDE there's a Jest extension that will run a watcher and give you inline updates on your tests, as well as a few other awesome features!
4042

4143
Please refer to [airbnb's style guide](https://github.com/airbnb/javascript) while coding.
4244

__tests__/__mocks__/axios.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default {
2+
get: jest.fn().mockResolvedValue({
3+
data: {}
4+
}),
5+
request: jest.fn().mockResolvedValue({
6+
data: {}
7+
})
8+
};

__tests__/commands/add.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Add from '../../src/commands/add';
2-
import { message as mockMessage } from '../mocks/discord';
2+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
33

4-
let sendMock: jest.Mock<any, any>;
4+
let sendMock: MockedMessage;
55
beforeEach(() => {
66
sendMock = jest.fn();
77
mockMessage.channel.send = sendMock;

__tests__/commands/gitProfile.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
import axios from 'axios';
2-
import GitProfile, { IGitProfileResponse } from '../../src/commands/gitProfile';
3-
import mocked from '../helpers/mocked';
1+
import GitProfile from '../../src/commands/gitProfile';
2+
import axiosMock from '../__mocks__/axios';
43
import mockGithubProfile from '../mockData/githubProfile';
5-
import { message as mockMessage } from '../mocks/discord';
4+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
65

76
jest.mock('axios');
87

9-
let sendMock: jest.Mock<any, any>;
8+
let sendMock: MockedMessage;
109
beforeEach(() => {
1110
jest.resetModules();
1211
sendMock = jest.fn();
1312
mockMessage.channel.send = sendMock;
1413
});
1514

1615
describe('GitProfile Command', () => {
16+
beforeEach(() => {
17+
const resolvedData = Promise.resolve({data: mockGithubProfile});
18+
axiosMock.request.mockResolvedValueOnce(resolvedData);
19+
});
1720
test('No username specified', () => {
1821
GitProfile.execute([], mockMessage);
1922
expect(sendMock).lastCalledWith('Please enter a username.');
2023
});
2124
test('Responds with profile information', async () => {
22-
mocked<any, IGitProfileResponse>(axios.request).mockResolvedValue({data: mockGithubProfile});
2325
await GitProfile.execute(['ctsstc'], mockMessage);
2426
const sentMessage = sendMock.mock.calls[0][0];
2527
expect(sentMessage).toContain(`[${mockGithubProfile.type}]`);
@@ -37,14 +39,13 @@ describe('GitProfile Command', () => {
3739
mockGithubProfile.name = null;
3840
mockGithubProfile.company = null;
3941
mockGithubProfile.location = null;
40-
mocked<any, IGitProfileResponse>(axios.request).mockResolvedValue({data: mockGithubProfile});
4142
await GitProfile.execute(['ctsstc'], mockMessage);
4243
const sentMessage = sendMock.mock.calls[0][0];
4344
expect(sentMessage.startsWith(`[${mockGithubProfile.type}] with`));
4445
});
4546
test('Requests API using username', async () => {
4647
await GitProfile.execute(['ctsstc'], mockMessage);
47-
const requestOptions = mocked<any, IGitProfileResponse>(axios.request).mock.calls[0][0];
48+
const requestOptions = axiosMock.request.mock.calls[0][0];
4849
expect(requestOptions.url).toContain('api.github.com/users/ctsstc');
4950
});
5051
});

__tests__/commands/lmgtfy.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Lmgtfy from '../../src/commands/lmgtfy';
2-
import { message as mockMessage } from '../mocks/discord';
2+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
33

4-
let sendMock: jest.Mock<any, any>;
4+
let sendMock: MockedMessage;
55
beforeEach(() => {
66
sendMock = jest.fn();
77
mockMessage.channel.send = sendMock;

__tests__/commands/magic8ball.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Magic8Ball from '../../src/commands/magic8ball';
2-
import { message as mockMessage } from '../mocks/discord';
2+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
33

4-
let sendMock: jest.Mock<any, any>;
4+
let sendMock: MockedMessage;
55
beforeEach(() => {
66
sendMock = jest.fn();
77
mockMessage.channel.send = sendMock;

__tests__/commands/rules.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Rules from '../../src/commands/rules';
22

3-
import { message as mockMessage } from '../mocks/discord';
3+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
44

5-
let sendMock: jest.Mock<any, any>;
5+
let sendMock: MockedMessage;
66
beforeEach(() => {
77
sendMock = jest.fn();
88
mockMessage.channel.send = sendMock;

__tests__/commands/say.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Say from '../../src/commands/say';
22

3-
import { message as mockMessage } from '../mocks/discord';
3+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
44

5-
let sendMock: jest.Mock<any, any>;
5+
let sendMock: MockedMessage;
66
beforeEach(() => {
77
sendMock = jest.fn();
88
mockMessage.channel.send = sendMock;

__tests__/commands/search.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Search from '../../src/commands/search';
2+
import config from '../../src/config';
3+
import axiosMock from '../__mocks__/axios';
4+
import { message as mockMessage, MockedMessage } from '../mocks/discord';
5+
import { noResults, results } from './../mockData/search';
6+
7+
jest.mock('axios');
8+
jest.mock('../../src/config.ts');
9+
10+
let sendMock: MockedMessage;
11+
beforeEach(() => {
12+
sendMock = jest.fn();
13+
mockMessage.channel.send = sendMock;
14+
mockMessage.reply = sendMock;
15+
config.googleApiKey = 'abc123';
16+
config.googleSearchEngineId = '12345678910:some-thing';
17+
});
18+
19+
const setupRequiredMessage = 'Setup Required: Configure Google API keys in the environment variables';
20+
21+
describe('Search Command', () => {
22+
describe('Environment Variables', () => {
23+
test('Setup message when missing Google API Key ENV Var', async () => {
24+
config.googleApiKey = undefined;
25+
await Search.execute(['Missing Google API Key'], mockMessage);
26+
expect(sendMock).lastCalledWith(setupRequiredMessage);
27+
});
28+
test('Setup message when missing Google Search Engine ID ENV Var', async () => {
29+
config.googleSearchEngineId = undefined;
30+
await Search.execute(['Missing Google Search Engine ID'], mockMessage);
31+
expect(sendMock).lastCalledWith(setupRequiredMessage);
32+
});
33+
});
34+
test('With no results', async () => {
35+
const mockedData = Promise.resolve({ data: noResults });
36+
axiosMock.get.mockResolvedValueOnce(mockedData);
37+
await Search.execute(['Nothing here'], mockMessage);
38+
expect(sendMock).lastCalledWith('`No results found.`');
39+
});
40+
test('With results', async () => {
41+
const mockedData = Promise.resolve({ data: results });
42+
axiosMock.get.mockResolvedValueOnce(mockedData);
43+
await Search.execute(['dingusy'], mockMessage);
44+
expect(sendMock).lastCalledWith(results.items[0].link);
45+
});
46+
test('Malformed Response', async () => {
47+
const mockedData = Promise.resolve({ data: {} });
48+
axiosMock.get.mockResolvedValueOnce(mockedData);
49+
await Search.execute(['NOPE'], mockMessage);
50+
expect(sendMock).lastCalledWith("I'm Sorry Dave, I'm afraid I can't do that...");
51+
});
52+
});

0 commit comments

Comments
 (0)