From 99694453ccd7ed32fc35457e4196d3d7671ea54c Mon Sep 17 00:00:00 2001 From: Cleboost <61158869+Cleboost@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:05:27 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20add=20tests=20for=20ModalHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added test suite `modal-handler.test.ts` for ModalHandler to improve code coverage. Added checks for basic operations including adding/setting/deleting modals, proper component registration validation, interaction execution with and without data tokens, handling of expired data, and ensuring error fallback logic (handleInteractionError) processes correctly. Mocked implementations of discord.js APIs similarly to ButtonHandler. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- packages/runtime/test/modal-handler.test.ts | 146 ++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 packages/runtime/test/modal-handler.test.ts diff --git a/packages/runtime/test/modal-handler.test.ts b/packages/runtime/test/modal-handler.test.ts new file mode 100644 index 0000000..0f1c529 --- /dev/null +++ b/packages/runtime/test/modal-handler.test.ts @@ -0,0 +1,146 @@ +import { beforeEach, describe, expect, mock, test } from "bun:test"; +import { Client } from "discord.js"; +import ModalHandler from "../handler/ModalHandler"; +import Modal from "../interaction/Modal"; + +describe("ModalHandler", () => { + let client: Client; + let handler: ModalHandler; + + beforeEach(() => { + process.env.NODE_ENV = "test"; // Use in-memory DB + client = new Client({ intents: [] }); + handler = new ModalHandler(client); + }); + + describe("registration", () => { + test("should add a modal", async () => { + const modal = new Modal().setCustomId("test-modal").run(() => {}); + handler.add(modal); + // Verify it was added by triggering it with a fake interaction + const executed = mock(() => {}); + modal.run(executed); + + const interaction = { + customId: "test-modal", + } as any; + + await handler.onModalSubmit(interaction); + expect(executed).toHaveBeenCalled(); + }); + + test("should set multiple modals", async () => { + const modal1 = new Modal().setCustomId("test-modal-1").run(() => {}); + const modal2 = new Modal().setCustomId("test-modal-2").run(() => {}); + + handler.set([modal1, modal2]); + + const executed1 = mock(() => {}); + const executed2 = mock(() => {}); + modal1.run(executed1); + modal2.run(executed2); + + await handler.onModalSubmit({ customId: "test-modal-1" } as any); + await handler.onModalSubmit({ customId: "test-modal-2" } as any); + + expect(executed1).toHaveBeenCalled(); + expect(executed2).toHaveBeenCalled(); + }); + + test("should delete a modal", async () => { + const modal = new Modal().setCustomId("test-modal").run(() => {}); + handler.add(modal); + handler.delete("test-modal"); + + const executed = mock(() => {}); + modal.run(executed); + + const interaction = { + customId: "test-modal", + } as any; + + await handler.onModalSubmit(interaction); + expect(executed).not.toHaveBeenCalled(); + }); + }); + + describe("interaction routing", () => { + test("should route modal interaction without data", async () => { + const executed = mock(() => {}); + const modal = new Modal().setCustomId("test-modal").run(executed); + + handler.add(modal); + + const interaction = { + customId: "test-modal", + } as any; + + await handler.onModalSubmit(interaction); + expect(executed).toHaveBeenCalled(); + }); + + test("should route modal interaction with data", async () => { + const executed = mock((_, data) => { + expect(data).toEqual({ id: 1 }); + }); + const modal = new Modal<{ id: number }>() + .setCustomId("data-modal") + .run(executed); + + handler.add(modal); + + const { InteractionHelper } = await import("../interaction/BaseInteraction"); + const token = InteractionHelper.storeData({ id: 1 }); + const interaction = { + customId: `data-modal:${token}`, + } as any; + + await handler.onModalSubmit(interaction); + expect(executed).toHaveBeenCalled(); + }); + + test("should handle expired interaction data", async () => { + const modal = new Modal().setCustomId("expired-modal").run(() => {}); + handler.add(modal); + + const reply = mock(() => {}); + const interaction = { + customId: "expired-modal:invalid", + reply, + } as any; + + await handler.onModalSubmit(interaction); + expect(reply).toHaveBeenCalled(); + }); + }); + + describe("error handling", () => { + test("should handle errors during execution", async () => { + const originalConsoleError = console.error; + console.error = mock(() => {}); // suppress expected error log + + const modal = new Modal() + .setCustomId("error-modal") + .run(() => { + throw new Error("Test error"); + }); + handler.add(modal); + + const reply = mock(() => {}); + const interaction = { + customId: "error-modal", + reply, + isRepliable: () => true, + replied: false, + deferred: false, + } as any; + + await handler.onModalSubmit(interaction); + + expect(reply).toHaveBeenCalled(); + expect(console.error).toHaveBeenCalled(); + + console.error = originalConsoleError; + }); + }); +});