This repository has no description
0

Configure Feed

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

core / web / src / lib / components / profile / pages.test.ts
6.6 kB 220 lines
1import { describe, expect, it, vi } from "vitest"; 2import { createBobbinClient } from "$lib/api/client"; 3import { 4 fetchPeoplePage, 5 fetchPinned, 6 fetchReposPage, 7 fetchStarredPage, 8 fetchVouchesPage 9} from "./pages"; 10 11const enriched = () => Response.json({ output: { items: [], hits: [] }, data: {} }); 12 13const requestBody = (fetchMock: ReturnType<typeof vi.fn>, index = 0) => { 14 const init = fetchMock.mock.calls[index][1] as RequestInit; 15 return JSON.parse(String(init.body)) as { 16 xrpc: string; 17 enrich: { source: string; type: string; targets?: string[] }[]; 18 }; 19}; 20 21describe("fetchStarredPage", () => { 22 it("only requests minidocs for string URI subjects", async () => { 23 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( 24 Response.json({ 25 output: { items: [] }, 26 data: {} 27 }) 28 ); 29 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 30 31 await fetchStarredPage(ctx, { did: "did:plc:alice" }); 32 33 const body = requestBody(fetchMock); 34 expect(body.enrich[0].targets).toEqual(["items[].value.subject.uri"]); 35 }); 36 37 it("targets repo stats and owner minidocs independently", async () => { 38 const fetchMock = vi 39 .fn<typeof globalThis.fetch>() 40 .mockResolvedValueOnce( 41 Response.json({ 42 output: { 43 items: [ 44 { 45 uri: "at://did:plc:alice/sh.tangled.feed.star/one", 46 value: { subject: { did: "did:plc:repo" }, createdAt: "2026-08-01T00:00:00Z" } 47 } 48 ] 49 }, 50 data: {} 51 }) 52 ) 53 .mockResolvedValueOnce(Response.json({ output: { items: [] }, data: {} })); 54 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 55 56 await fetchStarredPage(ctx, { did: "did:plc:alice" }); 57 58 const body = requestBody(fetchMock, 1); 59 expect(body.enrich).toEqual([ 60 { 61 source: "sh.tangled.feed.star:subject", 62 type: "sh.tangled.query.enrichResponse#count", 63 targets: ["items[].value.repoDid"] 64 }, 65 { 66 source: "sh.tangled.repo:.repo", 67 type: "com.bad-example.identity.miniDoc", 68 targets: ["items[].uri"] 69 } 70 ]); 71 }); 72 73 it.each([ 74 [undefined, "items[].value.repoDid"], 75 ["needle", "hits[].value.repoDid"] 76 ])("targets repo stats for list/search results", async (q, expectedTarget) => { 77 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue(enriched()); 78 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 79 80 await fetchReposPage(ctx, { 81 did: "did:plc:alice", 82 handle: "alice.test", 83 viewerDid: "did:plc:viewer", 84 q 85 }); 86 87 const body = requestBody(fetchMock); 88 expect(body.enrich).toHaveLength(2); 89 expect(body.enrich.every((descriptor) => descriptor.targets?.[0] === expectedTarget)).toBe( 90 true 91 ); 92 }); 93 94 it.each([ 95 ["followers" as const, "items[].uri"], 96 ["following" as const, "items[].value.subject"] 97 ])("targets %s identities and stats", async (direction, expectedTarget) => { 98 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue(enriched()); 99 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 100 101 await fetchPeoplePage(ctx, { 102 did: "did:plc:alice", 103 viewerDid: "did:plc:viewer", 104 direction 105 }); 106 107 const body = requestBody(fetchMock); 108 expect(body.enrich).toHaveLength(4); 109 expect(body.enrich.every((descriptor) => descriptor.targets?.[0] === expectedTarget)).toBe( 110 true 111 ); 112 }); 113 114 it("does not retry missing enriched identities", async () => { 115 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( 116 Response.json({ 117 output: { 118 items: [ 119 { 120 uri: "at://did:plc:bob/sh.tangled.graph.follow/one", 121 value: { subject: "did:plc:alice", createdAt: "2026-08-01T00:00:00Z" } 122 } 123 ] 124 }, 125 data: {} 126 }) 127 ); 128 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 129 130 const page = await fetchPeoplePage(ctx, { 131 did: "did:plc:alice", 132 direction: "followers" 133 }); 134 135 expect(fetchMock).toHaveBeenCalledOnce(); 136 expect(page.items[0].handle).toBe("handle.invalid"); 137 }); 138 139 it("targets incoming vouch authors", async () => { 140 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue(enriched()); 141 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 142 143 await fetchVouchesPage(ctx, { 144 did: "did:plc:alice", 145 cursors: { outgoing: null } 146 }); 147 148 expect(requestBody(fetchMock).enrich[0].targets).toEqual(["items[].uri"]); 149 }); 150 151 it("targets repo dids for both pinned repo queries", async () => { 152 const fetchMock = vi.fn<typeof globalThis.fetch>().mockImplementation(async () => enriched()); 153 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 154 155 await fetchPinned(ctx, { 156 keys: ["did:plc:repo", "at://did:plc:alice/sh.tangled.repo/example"], 157 handle: "alice.test", 158 viewerDid: "did:plc:viewer" 159 }); 160 161 const bodies = fetchMock.mock.calls.map((_, index) => requestBody(fetchMock, index)); 162 expect(bodies.map((body) => body.xrpc).sort()).toEqual([ 163 "sh.tangled.repo.getRepos", 164 "sh.tangled.repo.getReposByRepoDids" 165 ]); 166 expect( 167 bodies.every((body) => 168 body.enrich.every((descriptor) => descriptor.targets?.[0] === "items[].value.repoDid") 169 ) 170 ).toBe(true); 171 }); 172 173 it("deduplicates repos returned by fetchReposPage by repoKey", async () => { 174 const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( 175 Response.json({ 176 output: { 177 items: [ 178 { 179 uri: "at://did:plc:alice/sh.tangled.repo/repo-one", 180 value: { 181 repoDid: "did:plc:repo-one", 182 name: "repo-one", 183 createdAt: "2026-08-01T00:00:00Z" 184 } 185 }, 186 { 187 uri: "at://did:plc:alice/sh.tangled.repo/repo-one-dup", 188 value: { 189 repoDid: "did:plc:repo-one", 190 name: "repo-one-dup", 191 createdAt: "2026-08-02T00:00:00Z" 192 } 193 }, 194 { 195 uri: "at://did:plc:alice/sh.tangled.repo/repo-two", 196 value: { 197 repoDid: "did:plc:repo-two", 198 name: "repo-two", 199 createdAt: "2026-08-03T00:00:00Z" 200 } 201 } 202 ] 203 }, 204 data: {} 205 }) 206 ); 207 const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 208 209 const result = await fetchReposPage(ctx, { 210 did: "did:plc:alice", 211 handle: "alice.test", 212 viewerDid: "did:plc:viewer" 213 }); 214 215 expect(result.items).toHaveLength(2); 216 expect(result.items[0].repoDid).toBe("did:plc:repo-one"); 217 expect(result.items[0].name).toBe("repo-one-dup"); 218 expect(result.items[1].repoDid).toBe("did:plc:repo-two"); 219 }); 220});