diff --git a/src/commands/warn.ts b/src/commands/warn.ts index a088187..3f48bba 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -27,21 +27,16 @@ export default class Warn extends Command { public override async execute(interaction: CommandInteraction) { if (!interaction.guild || !interaction.guildId) return; - const targetUser = interaction.options.get('target'); + const targetUser = interaction.options.get('target', true).user!; const reasonInput = interaction.options.get('reason'); - if (!targetUser || !targetUser.user || !targetUser.member) { - await interaction.reply('Fields are required.'); - return; - } - const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; const logEmbed = new EmbedBuilder() .setColor(EmbedColours.Ok) .setTitle("Member Warned") - .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) - .setThumbnail(targetUser.user.avatarURL()) + .setDescription(`<@${targetUser.id}> \`${targetUser.tag}\``) + .setThumbnail(targetUser.avatarURL()) .addFields([ { name: "Moderator", @@ -55,17 +50,17 @@ export default class Warn extends Command { const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); - if (!channelName) return; + if (channelName) { + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; - const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; - - if (channel) { - await channel.send({ embeds: [ logEmbed ]}); + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); + } } - const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId); + const audit = new Audit(targetUser.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId); await audit.Save(Audit, audit); await interaction.reply('Successfully warned user.'); } -} \ No newline at end of file +} diff --git a/tests/commands/warn.test.ts b/tests/commands/warn.test.ts index e7ef10b..1db4b8a 100644 --- a/tests/commands/warn.test.ts +++ b/tests/commands/warn.test.ts @@ -95,7 +95,7 @@ describe('Execute', () => { expect(interaction.reply).toHaveBeenCalledWith("Successfully warned user."); expect(interaction.options.get).toHaveBeenCalledTimes(2); - expect(interaction.options.get).toHaveBeenCalledWith("target"); + expect(interaction.options.get).toHaveBeenCalledWith("target", true); expect(interaction.options.get).toHaveBeenCalledWith("reason"); expect(interaction.guild!.channels.cache.find).toHaveBeenCalledTimes(1); @@ -120,21 +120,375 @@ describe('Execute', () => { }, "savedAudit"); }); - test.todo("GIVEN interaction.guild is null, EXPECT nothing to happen"); + test("GIVEN interaction.guild is null, EXPECT nothing to happen", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; - test.todo("GIVEN interaction.guildId is null, EXPECT nothing to happen"); + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; - test.todo("GIVEN targetUser is null, EXPECT error"); + const reason = { + value: "Test reason", + }; - test.todo("GIVEN targetUser.user is undefined, EXPECT error"); + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; - test.todo("GIVEN targetUser.member is undefined, EXPECT error"); + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(reason), + }, + guildId: "guildId", + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; - test.todo("GIVEN reasonInput is null, EXPECT reason to be defaulted"); + SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); - test.todo("GIVEN reasonInput.value is undefined, EXPECT reason to be defaulted"); + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); - test.todo("GIVEN channels.logs.mod setting is not found, EXPECT command to return"); + // Assert + expect(interaction.reply).not.toHaveBeenCalled(); + + expect(Audit.prototype.Save).not.toHaveBeenCalled(); + }); - test.todo("GIVEN channel is not found, EXPECT logEmbed to not be sent"); + test("GIVEN interaction.guildId is null, EXPECT nothing to happen", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; + + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; + + const reason = { + value: "Test reason", + }; + + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(reason), + }, + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(logChannel), + }, + }, + }, + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; + + SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); + + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).not.toHaveBeenCalled(); + + expect(Audit.prototype.Save).not.toHaveBeenCalled(); + }); + + test("GIVEN reasonInput is null, EXPECT reason to be defaulted", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; + + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; + + const reason = { + value: "Test reason", + }; + + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(null), + }, + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(logChannel), + }, + }, + }, + guildId: "guildId", + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; + + SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); + + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); + + // Assert + expect(sentEmbeds).toBeDefined(); + + expect(sentEmbeds![0].data.fields).toBeDefined(); + expect(sentEmbeds![0].data.fields!.length).toBe(2); + + const logEmbedReasonField = sentEmbeds![0].data.fields!.find(x => x.name == "Reason"); + + expect(logEmbedReasonField).toBeDefined(); + expect(logEmbedReasonField!.value).toBe("*none*"); + }); + + test("GIVEN reasonInput.value is undefined, EXPECT reason to be defaulted", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; + + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; + + const reason = { + value: undefined, + }; + + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(reason), + }, + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(logChannel), + }, + }, + }, + guildId: "guildId", + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; + + SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); + + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); + + // Assert + expect(sentEmbeds).toBeDefined(); + + expect(sentEmbeds![0].data.fields).toBeDefined(); + expect(sentEmbeds![0].data.fields!.length).toBe(2); + + const logEmbedReasonField = sentEmbeds![0].data.fields!.find(x => x.name == "Reason"); + + expect(logEmbedReasonField).toBeDefined(); + expect(logEmbedReasonField!.value).toBe("*none*"); + }); + + test("GIVEN channels.logs.mod setting is not found, EXPECT command to return", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; + + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; + + const reason = { + value: "Test reason", + }; + + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(reason), + }, + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(logChannel), + }, + }, + }, + guildId: "guildId", + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; + + SettingsHelper.GetSetting = jest.fn().mockResolvedValue(undefined); + + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Successfully warned user."); + + expect(Audit.prototype.Save).toHaveBeenCalledTimes(1); + + expect(logChannel.send).not.toHaveBeenCalled(); + }); + + test("GIVEN channel is not found, EXPECT logEmbed to not be sent", async () => { + let sentEmbeds: EmbedBuilder[] | undefined; + let savedAudit: Audit | undefined; + + // Arrange + const targetUser = { + user: { + id: "userId", + tag: "userTag", + avatarURL: jest.fn().mockReturnValue("https://google.com/avatar.png"), + }, + member: {}, + }; + + const reason = { + value: "Test reason", + }; + + const logChannel = { + send: jest.fn().mockImplementation((opts: any) => { + sentEmbeds = opts.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn() + .mockReturnValueOnce(targetUser) + .mockReturnValue(reason), + }, + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(undefined), + }, + }, + }, + guildId: "guildId", + user: { + id: "moderatorId", + }, + } as unknown as CommandInteraction; + + SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); + + Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { + savedAudit = audit; + }); + + // Act + const command = new Warn(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Successfully warned user."); + + expect(Audit.prototype.Save).toHaveBeenCalledTimes(1); + + expect(interaction.guild!.channels.cache.find).toHaveBeenCalledTimes(1); + + expect(logChannel.send).not.toHaveBeenCalled(); + }); });