diff --git a/package.json b/package.json index 4a52b2f..05b181b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "test": "BOT_TOKEN=test jest --detectOpenHandles --coverage --verbose", "test:watch": "npm run test -- --watch", + "watch:test": "npm run test:watch", "build": "tsc", "start": "node -r dotenv/config dist/index.js", "watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"cyan.bold,green.bold\" \"npm run watch:ts\" \"npm run watch:node\"", diff --git a/src/commands/__snapshots__/index.test.ts.snap b/src/commands/__snapshots__/index.test.ts.snap index 0953923..636cbe0 100644 --- a/src/commands/__snapshots__/index.test.ts.snap +++ b/src/commands/__snapshots__/index.test.ts.snap @@ -6,7 +6,7 @@ exports[`_help returns a list of all commands 1`] = ` Array [ Array [ "Here is a list of available commands:", - "help, 3.5, elixir, event, item, madness, ping, prune, spell, treasure, wild", + "help, 3.5, elixir, event, item, madness, ping, prune, spell, table, treasure, wild", "Get more details with \\"_help [command]\\"", ], Object { @@ -23,7 +23,7 @@ exports[`_help returns a list of all commands 1`] = ` Array [ Array [ "Here is a list of available commands:", - "help, 3.5, elixir, event, item, madness, ping, prune, spell, treasure, wild", + "help, 3.5, elixir, event, item, madness, ping, prune, spell, table, treasure, wild", "Get more details with \\"_help [command]\\"", ], Object { diff --git a/src/commands/index.ts b/src/commands/index.ts index 3db20e6..ee113cc 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -12,6 +12,7 @@ import madness from './madness'; import ping from './ping'; import prune from './prune'; import spell from './spell'; +import table from './table'; import treasure from './treasure'; import wildMagic from './wildMagic'; @@ -83,6 +84,7 @@ commands.register(madness); commands.register(ping); commands.register(prune); commands.register(spell); +commands.register(table); commands.register(treasure); commands.register(wildMagic); diff --git a/src/commands/table.test.ts b/src/commands/table.test.ts new file mode 100644 index 0000000..660b666 --- /dev/null +++ b/src/commands/table.test.ts @@ -0,0 +1,85 @@ +import { Client, Guild, Message, TextChannel } from 'discord.js'; + +import { FriendlyError } from '../error'; +import command from './table'; + +const mocks = { + delete: jest.fn(), + reply: jest.fn(), +}; + +jest.mock('discord.js', () => ({ + Client: jest.fn(), + Guild: jest.fn(), + TextChannel: jest.fn(), + Collection: jest.fn(), + Message: jest.fn().mockImplementation(() => ({ + delete: mocks.delete, + reply: mocks.reply, + })), +})); +jest.mock('../data/table', () => ({ + a: new Array(100).fill('foo'), + b: [...new Array(98).fill('bar'), 'test', 'foobar'], + c: new Array(100).fill(() => 'baz'), +})); + +describe('_table configuration', () => { + it('should have basic command infomation', () => { + expect(command.name).toEqual('table'); + expect(command.description).toEqual('Roll on the magic item tables'); + expect(command.usage).toEqual('TABLE [ROLL]'); + }); + + it('should not have an alias', () => { + expect(command.alias).toBeUndefined(); + }); +}); + +describe('_table', () => { + let message: Message; + + beforeEach(() => { + mocks.delete.mockClear(); + mocks.reply.mockClear(); + + const client = new Client(); + const guild = new Guild(client, {}); + const channel = new TextChannel(guild, {}); + message = new Message(client, {}, channel); + }); + + it('returns a value from the given table', async () => { + await command.run(message, { $0: 'table', _: ['a'] }); + + expect(mocks.delete).toBeCalledTimes(1); + expect(mocks.reply).toBeCalledTimes(1); + expect(mocks.reply).toHaveBeenCalledWith('foo'); + }); + + it('returns a value for a dice roll', async () => { + await command.run(message, { $0: 'table', _: ['b', '99'] }); + + expect(mocks.delete).toBeCalledTimes(1); + expect(mocks.reply).toBeCalledTimes(1); + expect(mocks.reply).toHaveBeenCalledWith('test'); + }); + + it('returns a resolved value', async () => { + await command.run(message, { $0: 'table', _: ['C'] }); + + expect(mocks.delete).toBeCalledTimes(1); + expect(mocks.reply).toBeCalledTimes(1); + expect(mocks.reply).toHaveBeenCalledWith('baz'); + }); + + it('throws an error if invalid table given', async () => { + try { + await command.run(message, { $0: 'table', _: [] }); + fail('expected error to be thrown'); + } catch (err) { + expect(err instanceof FriendlyError).toBe(true); + expect(err.message).toEqual('Unknown table "-".'); + } + }); +}); diff --git a/src/commands/table.ts b/src/commands/table.ts new file mode 100644 index 0000000..056cafd --- /dev/null +++ b/src/commands/table.ts @@ -0,0 +1,31 @@ +import { Message } from 'discord.js'; +import { isString } from 'lodash'; +import { Arguments } from 'yargs'; + +import magicItemTable from '../data/table'; +import { FriendlyError } from '../error'; +import { Command } from '../types'; +import { roll } from '../utils'; + +const command: Command = { + name: 'table', + description: 'Roll on the magic item tables', + usage: 'TABLE [ROLL]', + async run(message: Message, args: Arguments) { + const index = (args._[0] || '-').toLowerCase(); + const dice = Number(args._[1] || roll('d100')); + + const table = magicItemTable[index]; + if (!table) { + throw new FriendlyError(`Unknown table "${index}".`); + } + + const item = table[dice - 1]; + + await message.delete(); + + return message.reply(isString(item) ? item : await item()); + }, +}; + +export default command; diff --git a/src/commands/treasure.ts b/src/commands/treasure.ts index 61c30d3..f1a2660 100644 --- a/src/commands/treasure.ts +++ b/src/commands/treasure.ts @@ -1,5 +1,5 @@ import { Message } from 'discord.js'; -import { sample } from 'lodash'; +import { isString, sample } from 'lodash'; import { Arguments } from 'yargs'; import { crIndex, hoard, individual } from '../data/treasure'; @@ -37,7 +37,7 @@ const command: Command = { for (let index = 0; index < roll(key); index++) { const item = sample(value) ?? ''; - const resolved = typeof item === 'string' ? item : await item(); + const resolved = isString(item) ? item : await item(); reply.push(`* ${type}: ${resolved}`); } }