import { mock } from "jest-mock-extended"; import { GuildMember, Message, Role, TextChannel, User } from "discord.js"; import Mute from "../../src/commands/mute"; import { ICommandContext } from "../../src/contracts/ICommandContext"; beforeEach(() => { process.env = {}; }); describe('Constructor', () => { test('Expect properties to be set', () => { process.env = { ROLES_MODERATOR: 'Moderator' }; const mute = new Mute(); expect(mute._category).toBe("Moderation"); expect(mute._roles.length).toBe(1); expect(mute._roles[0]).toBe('Moderator'); }); }); describe('Execute', () => { test('Given user has permission, expect user to be given muted role', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).toBeCalledTimes(1); expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); expect(messageChannelSend).toBeCalledTimes(1); expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: Test Reason'); expect(result.embeds.length).toBe(2); // Log Embed const logEmbed = result.embeds[0]; expect(logEmbed.title).toBe('Member Muted'); expect(logEmbed.fields.length).toBe(3); // Log Embed -> User Field const logEmbedUserField = logEmbed.fields[0]; expect(logEmbedUserField.name).toBe('User'); expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); expect(logEmbedUserField.inline).toBeTruthy(); // Log Embed -> Moderator Field const logEmbedModeratorField = logEmbed.fields[1]; expect(logEmbedModeratorField.name).toBe('Moderator'); expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); // Log Embed -> Reason Field const logEmbedFieldReason = logEmbed.fields[2]; expect(logEmbedFieldReason.name).toBe('Reason'); expect(logEmbedFieldReason.value).toBe('Test Reason'); // Public Embed const publicEmbed = result.embeds[1]; expect(publicEmbed.title).toBe(''); expect(publicEmbed.description).toBe('[object Object] has been muted'); }); test('Given moderator did not supply a reason, expect default reason used', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).toBeCalledTimes(1); expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); expect(messageChannelSend).toBeCalledTimes(1); expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: *none*'); expect(result.embeds.length).toBe(2); // Log Embed const logEmbed = result.embeds[0]; expect(logEmbed.title).toBe('Member Muted'); expect(logEmbed.fields.length).toBe(3); // Log Embed -> User Field const logEmbedUserField = logEmbed.fields[0]; expect(logEmbedUserField.name).toBe('User'); expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); expect(logEmbedUserField.inline).toBeTruthy(); // Log Embed -> Moderator Field const logEmbedModeratorField = logEmbed.fields[1]; expect(logEmbedModeratorField.name).toBe('Moderator'); expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); // Log Embed -> Reason Field const logEmbedFieldReason = logEmbed.fields[2]; expect(logEmbedFieldReason.name).toBe('Reason'); expect(logEmbedFieldReason.value).toBe('*none*'); // Public Embed const publicEmbed = result.embeds[1]; expect(publicEmbed.title).toBe(''); expect(publicEmbed.description).toBe('[object Object] has been muted'); }); test('Given user did not mention a user, expect user not to exist', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(null); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).not.toBeCalled(); expect(messageGuildRolesCacheFind).not.toBeCalled(); expect(messageGuildChannelsCacheFind).not.toBeCalled(); expect(messageChannelSend).toBeCalledTimes(1); expect(result.embeds.length).toBe(1); // Error Embed const errorEmbed = result.embeds[0]; expect(errorEmbed.description).toBe('User does not exist'); }); test('Given member can not be found from user, expect user to not be in server', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(null); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).not.toBeCalled(); expect(messageGuildChannelsCacheFind).not.toBeCalled(); expect(messageChannelSend).toBeCalledTimes(1); expect(result.embeds.length).toBe(1); // Error Embed const errorEmbed = result.embeds[0]; expect(errorEmbed.description).toBe('User is not in this server'); }); test('Given guild is unavailable, expect execution to stop', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: false, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).not.toBeCalled(); expect(messageGuildChannelsCacheFind).not.toBeCalled(); expect(messageChannelSend).not.toBeCalled(); expect(result.embeds.length).toBe(0); }); test('Given client can not manage user, expect insufficient permissions', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: false, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockImplementation((callback): Role | undefined => { const result = callback(role); if (!result) { return undefined; } return role; }); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).not.toBeCalled(); expect(messageGuildChannelsCacheFind).not.toBeCalled(); expect(messageChannelSend).toBeCalledTimes(1); expect(result.embeds.length).toBe(1); // Error Embed const errorEmbed = result.embeds[0]; expect(errorEmbed.description).toBe('Unable to do this action, am I missing permissions?'); }); test('Given muted role can not be found, expect role not found', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs', ROLES_MUTED: 'Muted' }; const user = { displayAvatarURL: jest.fn(), tag: 'USERTAG' } as unknown as User; const messageAuthor = { tag: 'AUTHORTAG' } as unknown as User; const member = { manageable: true, roles: { add: jest.fn() } } as unknown as GuildMember; const role = { name: 'Muted' } as unknown as Role; const logChannel = { name: 'mod-logs', send: jest.fn() } as unknown as TextChannel; const messageMentionsUsersFirst = jest.fn() .mockReturnValue(user); const messageGuildMember = jest.fn() .mockReturnValue(member); const messageGuildRolesCacheFind = jest.fn() .mockReturnValue(undefined); const messageChannelSend = jest.fn(); const messageGuildChannelsCacheFind = jest.fn() .mockImplementation((callback): TextChannel | undefined => { const result = callback(logChannel); if (!result) { return undefined; } return logChannel; }); const message = { mentions: { users: { first: messageMentionsUsersFirst } }, guild: { member: messageGuildMember, available: true, roles: { cache: { find: messageGuildRolesCacheFind } }, channels: { cache: { find: messageGuildChannelsCacheFind } } }, channel: { send: messageChannelSend }, author: messageAuthor } as unknown as Message; const context: ICommandContext = { name: 'mute', args: ['USER', 'Test', 'Reason'], message: message }; const mute = new Mute(); const result = await mute.execute(context); expect(messageMentionsUsersFirst).toBeCalledTimes(1); expect(messageGuildMember).toBeCalledWith(user); expect(messageGuildRolesCacheFind).toBeCalledTimes(1); expect(messageGuildChannelsCacheFind).not.toBeCalled(); expect(messageChannelSend).toBeCalledTimes(1); expect(result.embeds.length).toBe(1); // Error Embed const errorEmbed = result.embeds[0]; expect(errorEmbed.description).toBe('Unable to find role'); }); });