diff --git a/panel/src/app/(tg)/tg/__tests__/page.test.tsx b/panel/src/app/(tg)/tg/__tests__/page.test.tsx index e23ac7cf..dce29ba8 100644 --- a/panel/src/app/(tg)/tg/__tests__/page.test.tsx +++ b/panel/src/app/(tg)/tg/__tests__/page.test.tsx @@ -83,6 +83,20 @@ describe("TelegramMiniAppPage — auth bootstrap", () => { expect(post).not.toHaveBeenCalled(); }); + it("shows the Open-from-Telegram wall in production when the CDN bridge has empty initData", async () => { + // A plain browser at /tg: the telegram.org script defines WebApp but + // with no initData. Production must not post the empty payload (422) — + // it shows the wall, same as no bridge at all. + waitForTelegramWebApp.mockResolvedValue(mockWebApp("")); + + render(); + + 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); diff --git a/panel/src/app/(tg)/tg/page.tsx b/panel/src/app/(tg)/tg/page.tsx index 4149e054..208ab1a6 100644 --- a/panel/src/app/(tg)/tg/page.tsx +++ b/panel/src/app/(tg)/tg/page.tsx @@ -61,7 +61,11 @@ export default function TelegramMiniAppPage() { if (!webApp?.initData && process.env.NODE_ENV === "development") { webApp = createDevMockWebApp(); } - if (!webApp) { + // No bridge, or a bridge with empty initData that didn't become the + // dev mock (a plain browser at this URL in production) — show the + // "Open from Telegram" wall instead of posting an empty payload that + // 422s into a "Couldn't sign in" error. + if (!webApp || (!webApp.initData && !isDevMockWebApp(webApp))) { setState({ kind: "not_in_telegram" }); return; }