diff --git a/src/buttonEvents/Effects.ts b/src/buttonEvents/Effects.ts index 5fb1bb8..f185009 100644 --- a/src/buttonEvents/Effects.ts +++ b/src/buttonEvents/Effects.ts @@ -12,7 +12,7 @@ export default class Effects extends ButtonEvent { await List(interaction); break; case "use": - await Use(interaction); + await Use.Execute(interaction); break; } } diff --git a/src/buttonEvents/Effects/Use.ts b/src/buttonEvents/Effects/Use.ts index 2b38bd8..5bfe2fd 100644 --- a/src/buttonEvents/Effects/Use.ts +++ b/src/buttonEvents/Effects/Use.ts @@ -5,126 +5,128 @@ import EmbedColours from "../../constants/EmbedColours"; import TimeLengthInput from "../../helpers/TimeLengthInput"; import AppLogger from "../../client/appLogger"; -export default async function Use(interaction: ButtonInteraction) { - const subaction = interaction.customId.split(" ")[2]; +export default class Use { + public static async Execute(interaction: ButtonInteraction) { + const subaction = interaction.customId.split(" ")[2]; - switch (subaction) { - case "confirm": - await UseConfirm(interaction); - break; - case "cancel": - await UseCancel(interaction); - break; + switch (subaction) { + case "confirm": + await this.UseConfirm(interaction); + break; + case "cancel": + await this.UseCancel(interaction); + break; + } + } + + private static async UseConfirm(interaction: ButtonInteraction) { + const id = interaction.customId.split(" ")[3]; + + const effectDetail = EffectDetails.get(id); + + if (!effectDetail) { + AppLogger.LogError("Button/Effects/Use", `Effect not found, ${id}`); + + await interaction.reply("Effect not found in system!"); + return; + } + + const now = new Date(); + + const whenExpires = new Date(now.getTime() + effectDetail.duration); + + const result = await EffectHelper.UseEffect(interaction.user.id, id, whenExpires); + + if (!result) { + await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); + return; + } + + const embed = new EmbedBuilder() + .setTitle("Effect Used") + .setDescription("You now have an active effect!") + .setColor(EmbedColours.Green) + .addFields([ + { + name: "Effect", + value: effectDetail.friendlyName, + inline: true, + }, + { + name: "Expires", + value: ``, + inline: true, + }, + ]); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setLabel("Confirm") + .setCustomId(`effects use confirm ${effectDetail.id}`) + .setStyle(ButtonStyle.Primary) + .setDisabled(true), + new ButtonBuilder() + .setLabel("Cancel") + .setCustomId(`effects use cancel ${effectDetail.id}`) + .setStyle(ButtonStyle.Danger) + .setDisabled(true), + ]); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); + } + + private static async UseCancel(interaction: ButtonInteraction) { + const id = interaction.customId.split(" ")[3]; + + const effectDetail = EffectDetails.get(id); + + if (!effectDetail) { + AppLogger.LogError("Button/Effects/Cancel", `Effect not found, ${id}`); + + await interaction.reply("Effect not found in system!"); + return; + } + + const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration); + + const embed = new EmbedBuilder() + .setTitle("Effect Use Cancelled") + .setDescription("The effect from your inventory has not been used") + .setColor(EmbedColours.Grey) + .addFields([ + { + name: "Effect", + value: effectDetail.friendlyName, + inline: true, + }, + { + name: "Expires", + value: timeLengthInput.GetLengthShort(), + inline: true, + }, + ]); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setLabel("Confirm") + .setCustomId(`effects use confirm ${effectDetail.id}`) + .setStyle(ButtonStyle.Primary) + .setDisabled(true), + new ButtonBuilder() + .setLabel("Cancel") + .setCustomId(`effects use cancel ${effectDetail.id}`) + .setStyle(ButtonStyle.Danger) + .setDisabled(true), + ]); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); } } - -export async function UseConfirm(interaction: ButtonInteraction) { - const id = interaction.customId.split(" ")[3]; - - const effectDetail = EffectDetails.get(id); - - if (!effectDetail) { - AppLogger.LogError("Button/Effects/Use", `Effect not found, ${id}`); - - await interaction.reply("Effect not found in system!"); - return; - } - - const now = new Date(); - - const whenExpires = new Date(now.getTime() + effectDetail.duration); - - const result = await EffectHelper.UseEffect(interaction.user.id, id, whenExpires); - - if (!result) { - await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); - return; - } - - const embed = new EmbedBuilder() - .setTitle("Effect Used") - .setDescription("You now have an active effect!") - .setColor(EmbedColours.Green) - .addFields([ - { - name: "Effect", - value: effectDetail.friendlyName, - inline: true, - }, - { - name: "Expires", - value: ``, - inline: true, - }, - ]); - - const row = new ActionRowBuilder() - .addComponents([ - new ButtonBuilder() - .setLabel("Confirm") - .setCustomId(`effects use confirm ${effectDetail.id}`) - .setStyle(ButtonStyle.Primary) - .setDisabled(true), - new ButtonBuilder() - .setLabel("Cancel") - .setCustomId(`effects use cancel ${effectDetail.id}`) - .setStyle(ButtonStyle.Danger) - .setDisabled(true), - ]); - - await interaction.update({ - embeds: [ embed ], - components: [ row ], - }); -} - -export async function UseCancel(interaction: ButtonInteraction) { - const id = interaction.customId.split(" ")[3]; - - const effectDetail = EffectDetails.get(id); - - if (!effectDetail) { - AppLogger.LogError("Button/Effects/Cancel", `Effect not found, ${id}`); - - await interaction.reply("Effect not found in system!"); - return; - } - - const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration); - - const embed = new EmbedBuilder() - .setTitle("Effect Use Cancelled") - .setDescription("The effect from your inventory has not been used") - .setColor(EmbedColours.Grey) - .addFields([ - { - name: "Effect", - value: effectDetail.friendlyName, - inline: true, - }, - { - name: "Expires", - value: timeLengthInput.GetLengthShort(), - inline: true, - }, - ]); - - const row = new ActionRowBuilder() - .addComponents([ - new ButtonBuilder() - .setLabel("Confirm") - .setCustomId(`effects use confirm ${effectDetail.id}`) - .setStyle(ButtonStyle.Primary) - .setDisabled(true), - new ButtonBuilder() - .setLabel("Cancel") - .setCustomId(`effects use cancel ${effectDetail.id}`) - .setStyle(ButtonStyle.Danger) - .setDisabled(true), - ]); - - await interaction.update({ - embeds: [ embed ], - components: [ row ], - }); -} \ No newline at end of file diff --git a/tests/buttonEvents/Effects/Use.test.ts b/tests/buttonEvents/Effects/Use.test.ts index 5cb6912..d5618cc 100644 --- a/tests/buttonEvents/Effects/Use.test.ts +++ b/tests/buttonEvents/Effects/Use.test.ts @@ -1,21 +1,148 @@ -describe("Use", () => { - test.todo("GIVEN subaction is confirm, EXPECT UseConfirm to be called"); +import { ButtonInteraction, EmbedBuilder, InteractionResponse } from "discord.js"; +import Use from "../../../src/buttonEvents/Effects/Use"; +import { mock } from "jest-mock-extended"; +import AppLogger from "../../../src/client/appLogger"; +import EffectHelper from "../../../src/helpers/EffectHelper"; - test.todo("GIVEN subaction is cancel, EXPECT UseCancel to be called"); +jest.mock("../../../src/client/appLogger"); +jest.mock("../../../src/helpers/EffectHelper"); - test.todo("GIVEN subaction is unknown, EXPECT nothing to be called"); +beforeEach(() => { + jest.resetAllMocks(); + + jest.useFakeTimers(); + jest.setSystemTime(0); +}); + +afterAll(() => { + jest.useRealTimers(); +}); + +describe("Execute", () => { + test("GIVEN subaction is unknown, EXPECT nothing to be called", async () => { + // Arrange + const interaction = mock(); + interaction.customId = "effects use invalud"; + + // Act + await Use.Execute(interaction); + + // Assert + expect(interaction.reply).not.toHaveBeenCalled(); + expect(interaction.update).not.toHaveBeenCalled(); + }); }); describe("UseConfirm", () => { - test.todo("GIVEN effectDetail is not found, EXPECT error"); + let interaction = mock(); - test.todo("GIVEN EffectHelper.UseEffect failed, EXPECT error"); + beforeEach(() => { + interaction = mock(); + interaction.customId = "effects use confirm"; + }); - test.todo("GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated"); + test("GIVEN effectDetail is not found, EXPECT error", async () => { + // Arrange + interaction.customId += " invalid"; + + // Act + await Use.Execute(interaction); + + // Assert + expect(AppLogger.LogError).toHaveBeenCalledTimes(1); + expect(AppLogger.LogError).toHaveBeenCalledWith("Button/Effects/Use", "Effect not found, invalid"); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!"); + }); + + test("GIVEN EffectHelper.UseEffect failed, EXPECT error", async () => { + // Arrange + interaction.customId += " unclaimed"; + interaction.user.id = "userId"; + + (EffectHelper.UseEffect as jest.Mock).mockResolvedValue(false); + + const whenExpires = new Date(Date.now() + 10 * 60 * 1000); + + // Act + await Use.Execute(interaction); + + // Assert + expect(EffectHelper.UseEffect).toHaveBeenCalledTimes(1); + expect(EffectHelper.UseEffect).toHaveBeenCalledWith("userId", "unclaimed", whenExpires); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); + }); + + test("GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated", async () => { + let updatedWith; + + // Arrange + interaction.customId += " unclaimed"; + interaction.user.id = "userId"; + interaction.update.mockImplementation(async (opts: any) => { + updatedWith = opts; + + return mock>(); + }); + + (EffectHelper.UseEffect as jest.Mock).mockResolvedValue(true); + + const whenExpires = new Date(Date.now() + 10 * 60 * 1000); + + // Act + await Use.Execute(interaction); + + // Assert + expect(EffectHelper.UseEffect).toHaveBeenCalledTimes(1); + expect(EffectHelper.UseEffect).toHaveBeenCalledWith("userId", "unclaimed", whenExpires); + + expect(interaction.update).toHaveBeenCalledTimes(1); + expect(updatedWith).toMatchSnapshot(); + }); }); describe("UseCancel", () => { - test.todo("GIVEN effectDetail is not found, EXPECT error"); + let interaction = mock(); - test.todo("GIVEN effectDetail is found, EXPECT interaction updated"); + beforeEach(() => { + interaction = mock(); + interaction.customId = "effects use cancel"; + }); + + test("GIVEN effectDetail is not found, EXPECT error", async () => { + // Arrange + interaction.customId += " invalid"; + + // Act + await Use.Execute(interaction); + + // Assert + expect(AppLogger.LogError).toHaveBeenCalledTimes(1); + expect(AppLogger.LogError).toHaveBeenCalledWith("Button/Effects/Cancel", "Effect not found, invalid"); + + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!"); + }); + + test("GIVEN effectDetail is found, EXPECT interaction updated", async () => { + let updatedWith; + + // Arrange + interaction.customId += " unclaimed"; + interaction.user.id = "userId"; + interaction.update.mockImplementation(async (opts: any) => { + updatedWith = opts; + + return mock>(); + }); + // Act + await Use.Execute(interaction); + + // Assert + expect(interaction.update).toHaveBeenCalledTimes(1); + expect(updatedWith).toMatchSnapshot(); + }); }); \ No newline at end of file diff --git a/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap b/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap new file mode 100644 index 0000000..6cec0f4 --- /dev/null +++ b/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UseCancel GIVEN effectDetail is found, EXPECT interaction updated 1`] = ` +{ + "components": [ + { + "components": [ + { + "custom_id": "effects use confirm unclaimed", + "disabled": true, + "emoji": undefined, + "label": "Confirm", + "style": 1, + "type": 2, + }, + { + "custom_id": "effects use cancel unclaimed", + "disabled": true, + "emoji": undefined, + "label": "Cancel", + "style": 4, + "type": 2, + }, + ], + "type": 1, + }, + ], + "embeds": [ + { + "color": 13882323, + "description": "The effect from your inventory has not been used", + "fields": [ + { + "inline": true, + "name": "Effect", + "value": "Unclaimed Chance Up", + }, + { + "inline": true, + "name": "Expires", + "value": "10m", + }, + ], + "title": "Effect Use Cancelled", + }, + ], +} +`; + +exports[`UseConfirm GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated 1`] = ` +{ + "components": [ + { + "components": [ + { + "custom_id": "effects use confirm unclaimed", + "disabled": true, + "emoji": undefined, + "label": "Confirm", + "style": 1, + "type": 2, + }, + { + "custom_id": "effects use cancel unclaimed", + "disabled": true, + "emoji": undefined, + "label": "Cancel", + "style": 4, + "type": 2, + }, + ], + "type": 1, + }, + ], + "embeds": [ + { + "color": 2263842, + "description": "You now have an active effect!", + "fields": [ + { + "inline": true, + "name": "Effect", + "value": "Unclaimed Chance Up", + }, + { + "inline": true, + "name": "Expires", + "value": "", + }, + ], + "title": "Effect Used", + }, + ], +} +`;