import { APIEmbed, CommandInteraction, CommandInteractionOption, Embed, EmbedBuilder, Guild, GuildChannel, GuildMember, InteractionReplyOptions, JSONEncodable, SlashCommandBuilder, TextChannel, User } from "discord.js"; import { mock } from "jest-mock-extended"; import Timeout from "../../src/commands/timeout"; import SettingsHelper from "../../src/helpers/SettingsHelper"; import Audit from "../../src/entity/Audit"; describe('Constructor', () => { test('EXPECT CommandBuilder to be configured', () => { const command = new Timeout(); expect(command.CommandBuilder).toBeDefined(); const commandBuilder = command.CommandBuilder as SlashCommandBuilder; expect(commandBuilder.name).toBe("timeout"); expect(commandBuilder.description).toBe("Timeouts a user out, sending them a DM with the reason if possible"); expect(commandBuilder.options.length).toBe(3); }); }); describe('execute', () => { // Happy flow test('GIVEN all checks have passed, EXPECT user to be timed out', async () => { let embeds: APIEmbed[] | undefined; const command = new Timeout(); const interactionReply = jest.fn((options: InteractionReplyOptions) => { embeds = options.embeds as APIEmbed[]; }); const getSetting = jest.spyOn(SettingsHelper, 'GetSetting').mockResolvedValue('mod-logs'); const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation(); const timeoutFunc = jest.fn(); const userInput = { user: { id: 'userId', tag: 'userTag', } as User, member: { manageable: true, timeout: timeoutFunc, } as unknown as GuildMember, } as CommandInteractionOption; const lengthInput = { value: '1s', } as CommandInteractionOption; const reasonInput = { value: 'Test reason', } as CommandInteractionOption; const logsChannel = { name: 'mod-logs', send: jest.fn(), } as unknown as TextChannel; const interaction = { guild: { channels: { cache: { find: jest.fn() .mockReturnValue(logsChannel), } } } as unknown as Guild, guildId: 'guildId', reply: interactionReply, options: { get: jest.fn() .mockReturnValueOnce(userInput) .mockReturnValueOnce(lengthInput) .mockReturnValue(reasonInput), }, user: { id: 'moderatorId' } } as unknown as CommandInteraction; await command.execute(interaction); // EXPECT user to be timed out expect(timeoutFunc).toBeCalledWith(1000, 'Test reason'); // EXPECT embeds to be sent expect(embeds).toBeDefined(); expect(embeds!.length).toBe(1); // EXPECT resultEmbed to be correctly configured const resultEmbed = embeds![0] as EmbedBuilder; expect(resultEmbed.data.description).toBe('<@userId> has been timed out'); expect(resultEmbed.data.fields).toBeDefined(); expect(resultEmbed.data.fields!.length).toBe(1); // EXPECT DM field to be configured const resultEmbedDMField = resultEmbed.data.fields![0]; expect(resultEmbedDMField.name).toBe("DM Sent"); expect(resultEmbedDMField.value).toBe("true"); // TODO: DM Embed check // TODO: Log Embed check // TODO: Audit check }); // Null checks test('GIVEN interaction.guild IS NULL, EXPECT nothing to happen', async () => { const command = new Timeout(); const interaction = { guild: null, reply: jest.fn(), } as unknown as CommandInteraction; await command.execute(interaction); expect(interaction.reply).not.toBeCalled(); }); test.todo('GIVEN interaction.guildId IS NULL, EXPECT nothing to happen'); // Validation test('GIVEN targetUser IS NULL, EXPECT validation error', async () => { const command = new Timeout(); const interaction = { reply: jest.fn(), guild: mock(), guildId: 'guildId', options: { get: jest.fn().mockReturnValue(undefined), } } as unknown as CommandInteraction; await command.execute(interaction); expect(interaction.reply).toBeCalledWith('Fields are required.'); }); test('GIVEN targetUser.user IS NULL, EXPECT validation error', async () => { const command = new Timeout(); const interactionOption = { user: undefined, member: {} as GuildMember } as CommandInteractionOption; const interaction = { reply: jest.fn(), guild: mock(), guildId: 'guildId', options: { get: jest.fn().mockReturnValue(interactionOption), } } as unknown as CommandInteraction; await command.execute(interaction); expect(interaction.reply).toBeCalledWith('Fields are required.'); }); test('GIVEN targetUser.member IS NULL, EXPECT validation error', async () => { const command = new Timeout(); const interactionOption = { user: {} as User, member: undefined } as CommandInteractionOption; const interaction = { reply: jest.fn(), guild: mock(), guildId: 'guildId', options: { get: jest.fn().mockReturnValue(interactionOption), } } as unknown as CommandInteraction; await command.execute(interaction); expect(interaction.reply).toBeCalledWith('Fields are required.'); }); test.todo('GIVEN lengthInput IS NULL, EXPECT validation error'); test.todo('GIVEN lengthInput.value IS NULL, EXPECT validation error'); test.todo('GIVEN targetMember IS NOT manageable by the bot, EXPECT insufficient permissions error'); // Reason variable test.todo('GIVEN reason IS NOT NULL, EXPECT to be ran with reason set'); test.todo('GIVEN reason IS NULL, EXPECT to be ran with empty string'); // Log embed test.todo('GIVEN channelName IS NULL, EXPECT execution to return'); test.todo('GIVEN channel IS NULL, EXPECT embed to not be sent'); // DM user test.todo('GIVEN user can NOT be messaged, EXPECT resultEmbed to contain "DM Sent = false"'); });