mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat: Telegram V3 — Mini App cockpit (initData auth + /tg surface) (#554)
* feat(telegram): Mini App auth — initData validation mints the cloud-auth session cookie * feat(panel): /tg Mini App cockpit — approvals, inbox, read-only board, A2A chat * fix(telegram,panel): unconditional webapp-auth rate limit, future-dated initData rejection, anchored /tg matcher * docs(map,rag): Telegram Mini App auth route, initData validator, (tg) surface --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
|
||||
const { waitForTelegramWebApp } = vi.hoisted(() => ({
|
||||
waitForTelegramWebApp: vi.fn(),
|
||||
}));
|
||||
vi.mock("@/lib/telegram/webapp", () => ({ waitForTelegramWebApp }));
|
||||
|
||||
const { post } = vi.hoisted(() => ({ post: vi.fn() }));
|
||||
vi.mock("@/lib/api/client", () => ({
|
||||
default: { post },
|
||||
getErrorMessage: (err: unknown) =>
|
||||
(err as { message?: string } | undefined)?.message ?? "Unknown error",
|
||||
}));
|
||||
|
||||
// The cockpit tabs each fetch their own data (queue cards, tasks,
|
||||
// notifications, A2A) — stubbed out here since this test only exercises the
|
||||
// bootstrap state machine, not tab content (each tab gets its own coverage).
|
||||
vi.mock("@/components/tg/tg-tab-bar", () => ({
|
||||
TgTabBar: () => <div data-testid="tg-tab-bar" />,
|
||||
}));
|
||||
vi.mock("@/components/tg/tg-approvals-tab", () => ({
|
||||
TgApprovalsTab: () => <div data-testid="tg-approvals-tab" />,
|
||||
}));
|
||||
vi.mock("@/components/tg/tg-inbox-tab", () => ({
|
||||
TgInboxTab: () => <div data-testid="tg-inbox-tab" />,
|
||||
}));
|
||||
vi.mock("@/components/tg/tg-board-tab", () => ({
|
||||
TgBoardTab: () => <div data-testid="tg-board-tab" />,
|
||||
}));
|
||||
vi.mock("@/components/tg/tg-chat-tab", () => ({
|
||||
TgChatTab: () => <div data-testid="tg-chat-tab" />,
|
||||
}));
|
||||
|
||||
import TelegramMiniAppPage from "../page";
|
||||
|
||||
function mockWebApp(initData = "abc123") {
|
||||
return { ready: vi.fn(), expand: vi.fn(), initData };
|
||||
}
|
||||
|
||||
describe("TelegramMiniAppPage — auth bootstrap", () => {
|
||||
beforeEach(() => {
|
||||
waitForTelegramWebApp.mockReset();
|
||||
post.mockReset();
|
||||
});
|
||||
|
||||
it("shows a spinner while validating", () => {
|
||||
waitForTelegramWebApp.mockReturnValue(new Promise(() => {}));
|
||||
render(<TelegramMiniAppPage />);
|
||||
expect(screen.getByText(/connecting/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the not-inside-Telegram screen when no WebApp object exists", async () => {
|
||||
waitForTelegramWebApp.mockResolvedValue(null);
|
||||
render(<TelegramMiniAppPage />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(/open from telegram/i)).toBeInTheDocument(),
|
||||
);
|
||||
expect(post).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls ready/expand, posts initData, and renders the cockpit on success", async () => {
|
||||
const webApp = mockWebApp("real-init-data");
|
||||
waitForTelegramWebApp.mockResolvedValue(webApp);
|
||||
post.mockResolvedValue({ data: { ok: true } });
|
||||
|
||||
render(<TelegramMiniAppPage />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId("tg-tab-bar")).toBeInTheDocument(),
|
||||
);
|
||||
expect(webApp.ready).toHaveBeenCalledTimes(1);
|
||||
expect(webApp.expand).toHaveBeenCalledTimes(1);
|
||||
expect(post).toHaveBeenCalledWith("/telegram/webapp-auth", {
|
||||
init_data: "real-init-data",
|
||||
});
|
||||
// Default tab is Approvals.
|
||||
expect(screen.getByTestId("tg-approvals-tab")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an error screen with the server's message when auth is refused", async () => {
|
||||
waitForTelegramWebApp.mockResolvedValue(mockWebApp());
|
||||
post.mockRejectedValue({ message: "Mini App disabled" });
|
||||
|
||||
render(<TelegramMiniAppPage />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(/couldn.t sign in/i)).toBeInTheDocument(),
|
||||
);
|
||||
expect(screen.getByText("Mini App disabled")).toBeInTheDocument();
|
||||
expect(screen.queryByTestId("tg-tab-bar")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user