WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 124 lines 3.9 kB view raw
1import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; 2 3const mockFetch = vi.fn(); 4 5describe("loginRoutes", () => { 6 beforeEach(() => { 7 vi.stubGlobal("fetch", mockFetch); 8 vi.stubEnv("APPVIEW_URL", "http://localhost:3000"); 9 vi.resetModules(); 10 11 // Default: user is not authenticated 12 mockFetch.mockResolvedValue({ ok: false, status: 401 }); 13 }); 14 15 afterEach(() => { 16 vi.unstubAllGlobals(); 17 vi.unstubAllEnvs(); 18 mockFetch.mockReset(); 19 }); 20 21 async function loadLoginRoutes() { 22 const mod = await import("../login.js"); 23 return mod.createLoginRoutes("http://localhost:3000"); 24 } 25 26 it("renders handle input form for unauthenticated users", async () => { 27 const routes = await loadLoginRoutes(); 28 const res = await routes.request("/login"); 29 30 expect(res.status).toBe(200); 31 const html = await res.text(); 32 expect(html).toContain('name="handle"'); 33 expect(html).toContain('type="text"'); 34 expect(html).toContain('placeholder="alice.bsky.social"'); 35 }); 36 37 it("renders login submit button", async () => { 38 const routes = await loadLoginRoutes(); 39 const res = await routes.request("/login"); 40 41 const html = await res.text(); 42 expect(html).toContain("Log in"); 43 expect(html).toContain('type="submit"'); 44 }); 45 46 it("form action points to /api/auth/login (the auth proxy)", async () => { 47 const routes = await loadLoginRoutes(); 48 const res = await routes.request("/login"); 49 50 const html = await res.text(); 51 expect(html).toContain('action="/api/auth/login"'); 52 expect(html).toContain('method="get"'); 53 }); 54 55 it("renders Internet Handle explanation text", async () => { 56 const routes = await loadLoginRoutes(); 57 const res = await routes.request("/login"); 58 59 const html = await res.text(); 60 // Should explain what Internet Handle login means 61 expect(html).toContain("Internet Handle"); 62 }); 63 64 it("displays decoded error message from query param", async () => { 65 const routes = await loadLoginRoutes(); 66 const res = await routes.request( 67 "/login?error=Invalid%20handle%20or%20unable%20to%20find%20your%20PDS." 68 ); 69 70 expect(res.status).toBe(200); 71 const html = await res.text(); 72 expect(html).toContain("Invalid handle or unable to find your PDS."); 73 }); 74 75 it("shows Log in link in header when unauthenticated", async () => { 76 const routes = await loadLoginRoutes(); 77 const res = await routes.request("/login"); 78 79 const html = await res.text(); 80 expect(html).toContain('href="/login"'); 81 expect(html).toContain("Log in"); 82 expect(html).not.toContain("Log out"); 83 }); 84 85 it("redirects to / when already authenticated", async () => { 86 mockFetch.mockResolvedValueOnce({ 87 ok: true, 88 json: () => 89 Promise.resolve({ 90 authenticated: true, 91 did: "did:plc:xyz", 92 handle: "bob.bsky.social", 93 }), 94 }); 95 96 const routes = await loadLoginRoutes(); 97 const res = await routes.request("/login", { 98 headers: { cookie: "atbb_session=valid-token" }, 99 }); 100 101 expect(res.status).toBe(302); 102 expect(res.headers.get("location")).toBe("/"); 103 }); 104 105 it("displays raw error string when error param has malformed percent-encoding", async () => { 106 const routes = await loadLoginRoutes(); 107 // %ZZ is invalid percent-encoding — decodeURIComponent would throw URIError 108 const res = await routes.request("/login?error=%ZZ"); 109 110 expect(res.status).toBe(200); 111 const html = await res.text(); 112 // Falls back to raw string instead of crashing with 500 113 expect(html).toContain("%ZZ"); 114 expect(html).toContain("login-form__error"); 115 }); 116 117 it("renders no error banner when no error query param present", async () => { 118 const routes = await loadLoginRoutes(); 119 const res = await routes.request("/login"); 120 121 const html = await res.text(); 122 expect(html).not.toContain("login-form__error"); 123 }); 124});