diff --git a/.forgejo/workflows/production.yml b/.forgejo/workflows/production.yml index 31e558e..ef10e7e 100644 --- a/.forgejo/workflows/production.yml +++ b/.forgejo/workflows/production.yml @@ -12,7 +12,7 @@ jobs: runs-on: node steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v1 with: diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index 8c36636..d5da5e8 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -12,7 +12,7 @@ jobs: runs-on: node steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v1 with: diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 989ac4f..9c1ef33 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: node steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v1 with: diff --git a/package-lock.json b/package-lock.json index c7f6758..8eb69ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "emoji-regex": "^10.0.0", "jest": "^29.0.0", "jest-mock-extended": "^3.0.0", - "minimatch": "9.0.3", + "minimatch": "9.0.4", "mysql": "^2.18.1", "random-bunny": "^2.1.6", "ts-jest": "^29.0.0", @@ -5968,9 +5968,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { "brace-expansion": "^2.0.1" }, diff --git a/package.json b/package.json index d98e173..54ddd17 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "emoji-regex": "^10.0.0", "jest": "^29.0.0", "jest-mock-extended": "^3.0.0", - "minimatch": "9.0.3", + "minimatch": "9.0.4", "mysql": "^2.18.1", "random-bunny": "^2.1.6", "ts-jest": "^29.0.0", diff --git a/tests/commands/clear.test.ts b/tests/commands/clear.test.ts index 26efe44..9599dc9 100644 --- a/tests/commands/clear.test.ts +++ b/tests/commands/clear.test.ts @@ -1,23 +1,216 @@ +import { ChatInputCommandInteraction, PermissionsBitField, SlashCommandBuilder, SlashCommandNumberOption, TextChannel } from "discord.js"; +import Command from "../../src/commands/clear"; + beforeEach(() => { process.env = {}; }); describe('Constructor', () => { - test.todo("EXPECT values to be set"); + test("EXPECT values to be set", () => { + const command = new Command(); + + expect(command.CommandBuilder).toBeDefined(); + + const commandBuilder = command.CommandBuilder as SlashCommandBuilder; + + expect(commandBuilder.name).toBe("clear"); + expect(commandBuilder.description).toBe("Clears the channel of messages"); + expect(commandBuilder.default_member_permissions).toBe(PermissionsBitField.Flags.ManageMessages.toString()); + expect(commandBuilder.options.length).toBe(1); + + const commandBuilderCountOption = commandBuilder.options[0] as SlashCommandNumberOption; + + expect(commandBuilderCountOption.name).toBe("count"); + expect(commandBuilderCountOption.description).toBe("The amount to delete"); + expect(commandBuilderCountOption.required).toBe(true); + expect(commandBuilderCountOption.min_value).toBe(1); + expect(commandBuilderCountOption.max_value).toBe(100); + }); }); describe('Execute', () => { - test.todo("GIVEN input is valid, EXPECT messages to be cleared"); + test("GIVEN input is valid, EXPECT messages to be cleared", async () => { + // Arrange + const channel = { + manageable: true, + bulkDelete: jest.fn(), + } as unknown as TextChannel; - test.todo("GIVEN interaction is NOT a chat input command, EXPECT nothing to happen"); + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(50), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; - test.todo("GIVEN interaction.channel is null, EXPECT nothing to happen"); + // Act + const command = new Command(); + await command.execute(interaction); - test.todo("GIVEN totalClear input is NOT supplied, EXPECT invalid error"); + // Assert + expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1); - test.todo("GIVEN totalClear is less than or equal to 0, EXPECT invalid error"); + expect(interaction.options.getNumber).toHaveBeenCalledTimes(1); + expect(interaction.options.getNumber).toHaveBeenCalledWith("count"); - test.todo("GIVEN totalClear is greater than 100, EXPECT invalid error"); + expect(channel.bulkDelete).toHaveBeenCalledTimes(1); + expect(channel.bulkDelete).toHaveBeenCalledWith(50); - test.todo("GIVEN channel is NOT manageable, EXPECT insufficient permissions error"); + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("50 message(s) were removed."); + }); + + test("GIVEN interaction is NOT a chat input command, EXPECT nothing to happen", async () => { + // Arrange + const channel = { + manageable: true, + bulkDelete: jest.fn(), + } as unknown as TextChannel; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(false), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(50), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // Act + const command = new Command(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).not.toHaveBeenCalled(); + expect(channel.bulkDelete).not.toHaveBeenCalled(); + }); + + test("GIVEN interaction.channel is null, EXPECT nothing to happen", async () => { + // Arrange + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: null, + options: { + getNumber: jest.fn().mockReturnValue(50), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // Act + const command = new Command(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).not.toHaveBeenCalled(); + }); + + test("GIVEN totalClear input is NOT supplied, EXPECT invalid error", async () => { + // Arrange + const channel = { + manageable: true, + bulkDelete: jest.fn(), + } as unknown as TextChannel; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(null), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // Act + const command = new Command(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Please specify an amount between 1 and 100."); + + expect(channel.bulkDelete).not.toHaveBeenCalled(); + }); + + test("GIVEN totalClear is less than or equal to 0, EXPECT invalid error", async () => { + // Arrange + const channel = { + manageable: true, + bulkDelete: jest.fn(), + } as unknown as TextChannel; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(0), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // Act + const command = new Command(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Please specify an amount between 1 and 100."); + + expect(channel.bulkDelete).not.toHaveBeenCalled(); + }); + + test("GIVEN totalClear is greater than 100, EXPECT invalid error", async () => { + // Arrange + const channel = { + manageable: true, + bulkDelete: jest.fn(), + } as unknown as TextChannel; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(101), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // Act + const command = new Command(); + await command.execute(interaction); + + // Assert + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Please specify an amount between 1 and 100."); + + expect(channel.bulkDelete).not.toHaveBeenCalled(); + }); + + test("GIVEN channel is NOT manageable, EXPECT insufficient permissions error", async () => { + // Arrange + const channel = { + manageable: false, + bulkDelete: jest.fn(), + } as unknown as TextChannel; + + const interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + channel: channel, + options: { + getNumber: jest.fn().mockReturnValue(50), + }, + reply: jest.fn(), + } as unknown as ChatInputCommandInteraction; + + // 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(channel.bulkDelete).not.toHaveBeenCalled(); + }); }); \ No newline at end of file