import { describe, it, expect, vi, beforeEach } from "vitest"; import { render } from "@testing-library/react"; import { TgWebAppProvider, useMainButton, useBackButton, type MainButtonOptions, } from "../hooks"; import type { TelegramWebApp } from "../webapp"; function fakeMainButton() { return { setText: vi.fn(), show: vi.fn(), hide: vi.fn(), enable: vi.fn(), disable: vi.fn(), showProgress: vi.fn(), hideProgress: vi.fn(), onClick: vi.fn(), offClick: vi.fn(), }; } function fakeBackButton() { return { show: vi.fn(), hide: vi.fn(), onClick: vi.fn(), offClick: vi.fn(), }; } function webAppWith(overrides: Partial): TelegramWebApp { return { ready: () => undefined, expand: () => undefined, initData: "", ...overrides, }; } function MainButtonHarness(props: MainButtonOptions) { useMainButton(props); return null; } function BackButtonHarness({ onBack }: { onBack: (() => void) | null }) { useBackButton(onBack); return null; } describe("useMainButton", () => { let mainButton: ReturnType; let webApp: TelegramWebApp; beforeEach(() => { mainButton = fakeMainButton(); webApp = webAppWith({ MainButton: mainButton }); }); it("configures and shows the button declaratively", () => { render( undefined} /> , ); expect(mainButton.setText).toHaveBeenCalledWith("Approve"); expect(mainButton.enable).toHaveBeenCalled(); expect(mainButton.hideProgress).toHaveBeenCalled(); expect(mainButton.show).toHaveBeenCalled(); expect(mainButton.onClick).toHaveBeenCalledTimes(1); }); it("reflects loading/disabled and invokes the latest onClick closure", () => { const first = vi.fn(); const second = vi.fn(); const { rerender } = render( , ); rerender( , ); expect(mainButton.showProgress).toHaveBeenCalled(); expect(mainButton.disable).toHaveBeenCalled(); // Same subscribed handler survives rerenders but calls the fresh closure. expect(mainButton.onClick).toHaveBeenCalledTimes(1); const handler = mainButton.onClick.mock.calls[0][0] as () => void; handler(); expect(first).not.toHaveBeenCalled(); expect(second).toHaveBeenCalledTimes(1); }); it("unhooks and hides on unmount", () => { const { unmount } = render( undefined} /> , ); unmount(); expect(mainButton.offClick).toHaveBeenCalledTimes(1); expect(mainButton.hide).toHaveBeenCalled(); }); it("no-ops without a provider (outside Telegram)", () => { expect(() => render( undefined} />, ), ).not.toThrow(); }); }); describe("useBackButton", () => { it("shows while a handler is set, hides when null, unhooks on unmount", () => { const backButton = fakeBackButton(); const webApp = webAppWith({ BackButton: backButton }); const onBack = vi.fn(); const { rerender, unmount } = render( , ); expect(backButton.show).toHaveBeenCalled(); const handler = backButton.onClick.mock.calls[0][0] as () => void; handler(); expect(onBack).toHaveBeenCalledTimes(1); rerender( , ); expect(backButton.hide).toHaveBeenCalled(); unmount(); expect(backButton.offClick).toHaveBeenCalledTimes(1); }); });