From e7fde3a2ea05a63008883bd2eebe101df32c8126 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Wed, 27 Mar 2024 13:57:36 +0000 Subject: [PATCH] Add role command tests --- src/commands/Role/role.ts | 14 +- tests/commands/Role/role.test.ts | 470 +++++++++++++++++++++++++++++-- 2 files changed, 460 insertions(+), 24 deletions(-) diff --git a/src/commands/Role/role.ts b/src/commands/Role/role.ts index 09e5342..1596a69 100644 --- a/src/commands/Role/role.ts +++ b/src/commands/Role/role.ts @@ -72,20 +72,18 @@ export default class Role extends Command { const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); - - if (!assignRole) return; - - if (!assignRole.editable) { + + if (!assignRole!.editable) { await interaction.reply('Insufficient permissions. Please contact a moderator.'); return; } if (!userRole) { - await roleManager.add(assignRole); - await interaction.reply(`Gave role: \`${assignRole.name}\``); + await roleManager.add(assignRole!); + await interaction.reply(`Gave role: \`${assignRole!.name}\``); } else { - await roleManager.remove(assignRole); - await interaction.reply(`Removed role: \`${assignRole.name}\``); + await roleManager.remove(assignRole!); + await interaction.reply(`Removed role: \`${assignRole!.name}\``); } } diff --git a/tests/commands/Role/role.test.ts b/tests/commands/Role/role.test.ts index af90fe4..bb133b1 100644 --- a/tests/commands/Role/role.test.ts +++ b/tests/commands/Role/role.test.ts @@ -1,37 +1,475 @@ -beforeEach(() => { - process.env = {}; -}); +import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder, SlashCommandRoleOption, SlashCommandSubcommandBuilder } from "discord.js"; +import Command from "../../../src/commands/Role/role"; +import Role from "../../../src/database/entities/Role"; +import { EmbedBuilder } from "@discordjs/builders"; +import EmbedColours from "../../../src/constants/EmbedColours"; describe('Constructor', () => { - test.todo('EXPECT properties are set'); + test('EXPECT properties are set', () => { + const command = new Command(); + + expect(command.CommandBuilder).toBeDefined(); + + const commandBuilder = command.CommandBuilder as SlashCommandBuilder; + + expect(commandBuilder.name).toBe("role"); + expect(commandBuilder.description).toBe("Toggle your roles"); + expect(commandBuilder.options.length).toBe(2); + + const toggleSubcommand = commandBuilder.options[0] as SlashCommandSubcommandBuilder; + + expect(toggleSubcommand.name).toBe("toggle"); + expect(toggleSubcommand.description).toBe("Toggle your role"); + expect(toggleSubcommand.options.length).toBe(1); + + const toggleRoleOption = toggleSubcommand.options[0] as SlashCommandRoleOption; + + expect(toggleRoleOption.name).toBe("role"); + expect(toggleRoleOption.description).toBe("The role name"); + expect(toggleRoleOption.required).toBe(true); + + const listSubcommand = commandBuilder.options[1] as SlashCommandSubcommandBuilder; + + expect(listSubcommand.name).toBe("list"); + expect(listSubcommand.description).toBe("List togglable roles"); + }); }); describe('Execute', () => { - test.todo("GIVEN interaction is not a chat input command, EXPECT nothing to happen"); + test("GIVEN interaction is not a chat input command, EXPECT nothing to happen", async () => { + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(false), + reply: jest.fn(), + } as unknown as CommandInteraction; - test.todo("GIVEN an invalid subcommand is given, EXPECT not found error"); + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).not.toHaveBeenCalled(); + }); + + test("GIVEN an invalid subcommand is given, EXPECT not found error", async () => { + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + options: { + getSubcommand: jest.fn().mockReturnValue("other"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Subcommand not found."); + }); }); describe("toggle", () => { - test.todo("GIVEN user has the role, EXPECT role to be removed"); + test("GIVEN user has the role, EXPECT role to be removed", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; - test.todo("GIVEN user does not have the role, EXPECT role to be added") + const requestedRole = { + role: { + name: "roleName", + }, + }; - test.todo("GIVEN interaction.guild is null, EXPECT nothing to happen"); + const userRole = {}; - test.todo("GIVEN interaction.member is null, EXPECT nothing to happen"); + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; - test.todo("GIVEN requestedRole is null, EXPECT invalid error"); + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); - test.todo("GIVEN requestedRole.role is undefined, EXPECT invalid error"); + const command = new Command(); + await command.execute(interaction); - test.todo("GIVEN role is not assignable, EXPECT unassignable error"); + expect(Role.FetchAllByServerId).toHaveBeenCalledTimes(1); + expect(Role.FetchAllByServerId).toHaveBeenCalledWith("guildId"); - test.todo("GIVEN assignRole is not foundm, EXPECT nothing to happen"); + expect(interaction.guild?.roles.cache.find).toHaveBeenCalledTimes(2); + + expect(interaction.options.get).toHaveBeenCalledTimes(1); + expect(interaction.options.get).toHaveBeenCalledWith("role"); - test.todo("GIVEN assignRole is not editable, EXPECT insufficient permissions error"); + const roleManager = interaction.member!.roles as GuildMemberRoleManager; + + expect(roleManager.cache.find).toHaveBeenCalledTimes(1); + expect(roleManager.remove).toHaveBeenCalledTimes(1); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Removed role: `roleName`"); + }); + + test("GIVEN user does not have the role, EXPECT role to be added", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; + + const requestedRole = { + role: { + name: "roleName", + }, + }; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(undefined), + }, + add: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(Role.FetchAllByServerId).toHaveBeenCalledTimes(1); + expect(Role.FetchAllByServerId).toHaveBeenCalledWith("guildId"); + + expect(interaction.guild?.roles.cache.find).toHaveBeenCalledTimes(2); + + expect(interaction.options.get).toHaveBeenCalledTimes(1); + expect(interaction.options.get).toHaveBeenCalledWith("role"); + + const roleManager = interaction.member!.roles as GuildMemberRoleManager; + + expect(roleManager.cache.find).toHaveBeenCalledTimes(1); + expect(roleManager.add).toHaveBeenCalledTimes(1); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Gave role: `roleName`"); + }) + + test("GIVEN interaction.guild is null, EXPECT nothing to happen", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; + + const requestedRole = { + role: { + name: "roleName", + }, + }; + + const userRole = {}; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: null, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + add: jest.fn(), + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).not.toHaveBeenCalled(); + + const roleManager = interaction.member!.roles as GuildMemberRoleManager; + + expect(roleManager.add).not.toHaveBeenCalled(); + expect(roleManager.remove).not.toHaveBeenCalled(); + }); + + test("GIVEN interaction.member is null, EXPECT nothing to happen", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; + + const requestedRole = { + role: { + name: "roleName", + }, + }; + + const userRole = {}; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: null, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + add: jest.fn(), + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).not.toHaveBeenCalled(); + + const roleManager = interaction.member!.roles as GuildMemberRoleManager; + + expect(roleManager.add).not.toHaveBeenCalled(); + expect(roleManager.remove).not.toHaveBeenCalled(); + }); + + test("GIVEN requestedRole is null, EXPECT invalid error", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; + + const userRole = {}; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(null), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Fields are required."); + }); + + test("GIVEN requestedRole.role is undefined, EXPECT invalid error", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: true, + }; + + const userRole = {}; + + const requestedRole = { + role: undefined, + }; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Fields are required."); + }); + + test("GIVEN assignRole is not editable, EXPECT insufficient permissions error", async () => { + const role = { + id: "roleId", + name: "roleName", + editable: false, + }; + + const userRole = {}; + + const requestedRole = { + role: { + name: "roleName", + }, + }; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + member: { + roles: { + cache: { + find: jest.fn().mockReturnValue(userRole), + }, + remove: jest.fn(), + }, + }, + options: { + get: jest.fn().mockReturnValue(requestedRole), + getSubcommand: jest.fn().mockReturnValue("toggle"), + }, + reply: jest.fn(), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([{}]); + + const command = new Command(); + await command.execute(interaction); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Insufficient permissions. Please contact a moderator."); + }); }); describe("list", () => { - test.todo("EXPECT role list to be sent"); + test("EXPECT role list to be sent", async () => { + let repliedWith; + + const role = { + name: "roleName", + }; + + const interaction = { + guildId: "guildId", + guild: { + roles: { + cache: { + find: jest.fn().mockReturnValue(role), + } + } + }, + isChatInputCommand: jest.fn().mockReturnValue(true), + options: { + getSubcommand: jest.fn().mockReturnValue("list"), + }, + reply: jest.fn().mockImplementation((options) => { + repliedWith = options; + }), + } as unknown as CommandInteraction; + + Role.FetchAllByServerId = jest.fn().mockResolvedValue([role]); + + const command = new Command(); + await command.execute(interaction); + + expect(Role.FetchAllByServerId).toHaveBeenCalledTimes(1); + expect(Role.FetchAllByServerId).toHaveBeenCalledWith("guildId"); + + expect(interaction.guild?.roles.cache.find).toHaveBeenCalledTimes(1); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + + expect(repliedWith).toBeDefined(); + expect(repliedWith!.embeds).toBeDefined(); + expect(repliedWith!.embeds.length).toBe(1); + + const embed = repliedWith!.embeds[0] as EmbedBuilder; + + expect(embed.data.color).toBe(EmbedColours.Ok); + expect(embed.data.title).toBe("Roles"); + expect(embed.data.description).toBe("Roles: 1\n\nroleName"); + }); }); \ No newline at end of file