Compare commits

..

No commits in common. "39231ddc16e0c1062d4ba743f1286d7c6a63f595" and "d0a8db718e6c75911de279e16e84f4b9e7ddd73c" have entirely different histories.

4 changed files with 31 additions and 605 deletions

View file

@ -61,13 +61,13 @@ export default class Kick extends Command {
const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (channelName) { if (!channelName) return;
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) { if (channel) {
await channel.send({ embeds: [ logEmbed ]}); await channel.send({ embeds: [ logEmbed ]});
} }
}
const audit = new Audit(targetUser.user!.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId); const audit = new Audit(targetUser.user!.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit); await audit.Save(Audit, audit);

View file

@ -40,13 +40,15 @@ export default class Poll extends Command {
} }
public override async execute(interaction: CommandInteraction) { public override async execute(interaction: CommandInteraction) {
const title = interaction.options.get('title', true); const title = interaction.options.get('title');
const option1 = interaction.options.get('option1', true); const option1 = interaction.options.get('option1');
const option2 = interaction.options.get('option2', true); const option2 = interaction.options.get('option2');
const option3 = interaction.options.get('option3'); const option3 = interaction.options.get('option3');
const option4 = interaction.options.get('option4'); const option4 = interaction.options.get('option4');
const option5 = interaction.options.get('option5'); const option5 = interaction.options.get('option5');
if (!title || !option1 || !option2) return;
const description = [ const description = [
option1.value as string, option1.value as string,
option2.value as string, option2.value as string,
@ -56,7 +58,15 @@ export default class Poll extends Command {
] ]
.filter(x => x != null); .filter(x => x != null);
const reactionEmojis = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣"]; const arrayOfNumbers = [
':one:',
':two:',
':three:',
':four:',
':five:',
];
const reactionEmojis = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣"];
description.forEach((value, index) => { description.forEach((value, index) => {
description[index] = `${reactionEmojis[index]} ${description[index]}`; description[index] = `${reactionEmojis[index]} ${description[index]}`;
@ -72,11 +82,10 @@ export default class Poll extends Command {
}); });
const messageResponse = await interaction.reply({ embeds: [ embed ]}); const message = await interaction.reply({ embeds: [ embed ]});
const message = await messageResponse.fetch();
description.forEach(async (value, index) => { description.forEach(async (value, index) => {
await message.react(reactionEmojis[index]); await (await message.fetch()).react(reactionEmojis[index]);
}); });
} }
} }

View file

@ -141,490 +141,19 @@ describe('Execute', () => {
expect(savedAudit?.ServerId).toBe("guildId"); expect(savedAudit?.ServerId).toBe("guildId");
}); });
test("GIVEN interaction is NOT a chat input command, EXPECT nothing to happen", async () => { test.todo("GIVEN interaction is NOT a chat input command, EXPECT nothing to happen");
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange test.todo("GIVEN interaction.guildId is null, EXPECT nothing to happen");
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = { test.todo("GIVEN interaction.guild is null, EXPECT nothing to happen");
value: "Test reason",
};
const channel = { test.todo("GIVEN reasonInput is null, EXPECT reason to be defaulted");
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = { test.todo("GIVEN reasonInput.value is undefined, EXPECT reason to be defaulted");
isChatInputCommand: jest.fn().mockReturnValue(false),
guildId: "guildId",
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
user: {
id: "moderatorId",
},
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("mod-logs"); test.todo("GIVEN user is not kickable, EXPECT insufficient permissions error");
Audit.prototype.Save = jest.fn().mockImplementation((_, audit: Audit) => { test.todo("GIVEN channels.logs.mod setting can not be found, EXPECT command to return");
savedAudit = audit;
});
// Act test.todo("GIVEN channel can not be found, EXPECT logEmbed not to be sent");
const command = new Command();
await command.execute(interaction);
// Assert
expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1);
expect(interaction.reply).not.toHaveBeenCalled();
expect(targetUser.member.kick).not.toHaveBeenCalled();
expect(Audit.prototype.Save).not.toHaveBeenCalled();
});
test("GIVEN interaction.guildId is null, EXPECT nothing to happen", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: "Test reason",
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: null,
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
expect(interaction.reply).not.toHaveBeenCalled();
expect(targetUser.member.kick).not.toHaveBeenCalled();
expect(Audit.prototype.Save).not.toHaveBeenCalled();
});
test("GIVEN interaction.guild is null, EXPECT nothing to happen", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: "Test reason",
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: null,
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
expect(interaction.reply).not.toHaveBeenCalled();
expect(targetUser.member.kick).not.toHaveBeenCalled();
expect(Audit.prototype.Save).not.toHaveBeenCalled();
});
test("GIVEN reasonInput is null, EXPECT reason to be defaulted", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(null),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
const sentEmbedReasonField = sentEmbed!.data.fields!.find(x => x.name == "Reason");
expect(sentEmbedReasonField!.value).toBe("*none*");
});
test("GIVEN reasonInput.value is undefined, EXPECT reason to be defaulted", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: undefined,
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
const sentEmbedReasonField = sentEmbed!.data.fields!.find(x => x.name == "Reason");
expect(sentEmbedReasonField!.value).toBe("*none*");
});
test("GIVEN user is not kickable, EXPECT insufficient permissions error", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: false,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: "Test reason",
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("Insufficient permissions. Please contact a moderator.");
expect(targetUser.member.kick).not.toHaveBeenCalled();
expect(Audit.prototype.Save).not.toHaveBeenCalled();
});
test("GIVEN channels.logs.mod setting can not be found, EXPECT command to return", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: "Test reason",
};
const channel = {
name: "mod-logs",
send: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
}),
} as unknown as TextChannel;
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: [ channel ],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
expect(channel.send).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(Audit.prototype.Save).toHaveBeenCalledTimes(1);
expect(targetUser.member.kick).toHaveBeenCalledTimes(1);
});
test("GIVEN channel can not be found, EXPECT logEmbed not to be sent", async () => {
let sentEmbed: EmbedBuilder | undefined;
let savedAudit: Audit | undefined;
// Arrange
const targetUser = {
member: {
kickable: true,
kick: jest.fn(),
} as unknown as GuildMember,
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
} as unknown as User,
};
const reason = {
value: "Test reason",
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: [],
},
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
reply: jest.fn(),
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 Command();
await command.execute(interaction);
// Assert
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(Audit.prototype.Save).toHaveBeenCalledTimes(1);
expect(targetUser.member.kick).toHaveBeenCalledTimes(1);
});
}); });

View file

@ -1,121 +1,9 @@
import { CommandInteraction, EmbedBuilder, InteractionResponse, Message, SlashCommandBuilder, SlashCommandStringOption } from "discord.js";
import Command from "../../src/commands/poll";
import EmbedColours from "../../src/constants/EmbedColours";
describe('Constructor', () => { describe('Constructor', () => {
test('EXPECT properties to be set', () => { test.todo('EXPECT properties to be set');
const command = new Command();
expect(command.CommandBuilder).toBeDefined();
const commandBuilder = command.CommandBuilder as SlashCommandBuilder;
expect(commandBuilder.name).toBe("poll");
expect(commandBuilder.description).toBe("Run a poll, automatically adding reaction emojis as options");
expect(commandBuilder.options.length).toBe(6);
const commandBuilderTitleOption = commandBuilder.options[0] as SlashCommandStringOption;
expect(commandBuilderTitleOption.name).toBe("title");
expect(commandBuilderTitleOption.description).toBe("Title of the poll");
expect(commandBuilderTitleOption.required).toBe(true);
const commandBuilderOption1Option = commandBuilder.options[1] as SlashCommandStringOption;
expect(commandBuilderOption1Option.name).toBe("option1");
expect(commandBuilderOption1Option.description).toBe("Option 1");
expect(commandBuilderOption1Option.required).toBe(true);
const commandBuilderOption2Option = commandBuilder.options[2] as SlashCommandStringOption;
expect(commandBuilderOption2Option.name).toBe("option2");
expect(commandBuilderOption2Option.description).toBe("Option 2");
expect(commandBuilderOption2Option.required).toBe(true);
const commandBuilderOption3Option = commandBuilder.options[3] as SlashCommandStringOption;
expect(commandBuilderOption3Option.name).toBe("option3");
expect(commandBuilderOption3Option.description).toBe("Option 3");
const commandBuilderOption4Option = commandBuilder.options[4] as SlashCommandStringOption;
expect(commandBuilderOption4Option.name).toBe("option4");
expect(commandBuilderOption4Option.description).toBe("Option 4");
const commandBuilderOption5Option = commandBuilder.options[5] as SlashCommandStringOption;
expect(commandBuilderOption5Option.name).toBe("option5");
expect(commandBuilderOption5Option.description).toBe("Option 5");
});
}); });
describe('Execute', () => { describe('Execute', () => {
test("EXPECT a poll to be created", async () => { test.todo("EXPECT a poll to be created");
let sentEmbed: EmbedBuilder | undefined;
// Arrange
const message = {
react: jest.fn(),
} as unknown as Message<boolean>;
const response = {
fetch: jest.fn().mockResolvedValue(message),
} as unknown as InteractionResponse<boolean>;
const interaction = {
options: {
get: jest.fn().mockReturnValueOnce({ value: "Title" })
.mockReturnValueOnce({ value: "Option 1" })
.mockReturnValueOnce({ value: "Option 2" })
.mockReturnValueOnce({ value: "Option 3" })
.mockReturnValueOnce({ value: "Option 4" })
.mockReturnValue({ value: "Option 5" }),
},
reply: jest.fn().mockImplementation((options: any) => {
sentEmbed = options.embeds[0];
return response;
}),
user: {
username: "username",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
} as unknown as CommandInteraction;
// Act
const command = new Command();
await command.execute(interaction);
// Assert
expect(interaction.options.get).toHaveBeenCalledTimes(6);
expect(interaction.options.get).toHaveBeenCalledWith("title", true);
expect(interaction.options.get).toHaveBeenCalledWith("option1", true);
expect(interaction.options.get).toHaveBeenCalledWith("option2", true);
expect(interaction.options.get).toHaveBeenCalledWith("option3");
expect(interaction.options.get).toHaveBeenCalledWith("option4");
expect(interaction.options.get).toHaveBeenCalledWith("option5");
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(sentEmbed).toBeDefined();
expect(sentEmbed!.data.color).toBe(EmbedColours.Ok);
expect(sentEmbed!.data.title).toBe("Title");
expect(sentEmbed!.data.description).toBe("1⃣ Option 1\n2⃣ Option 2\n3⃣ Option 3\n4⃣ Option 4\n5⃣ Option 5");
expect(sentEmbed!.data.footer).toBeDefined();
expect(sentEmbed!.data.footer!.text).toBe("Poll by username");
expect(sentEmbed!.data.footer!.icon_url).toBe("https://avatarurl.com/user.png");
expect(interaction.user.avatarURL).toHaveBeenCalledTimes(1);
expect(response.fetch).toHaveBeenCalledTimes(1);
expect(message.react).toHaveBeenCalledTimes(5);
expect(message.react).toHaveBeenCalledWith("1⃣");
expect(message.react).toHaveBeenCalledWith("2⃣");
expect(message.react).toHaveBeenCalledWith("3⃣");
expect(message.react).toHaveBeenCalledWith("4⃣");
expect(message.react).toHaveBeenCalledWith("5⃣");
});
test.todo("GIVEN title is not supplied, EXPECT nothing to happen"); test.todo("GIVEN title is not supplied, EXPECT nothing to happen");