Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275import { describe, expect, it } from "vitest";import { groupTimeline } from "$lib/components/timeline/group";import type * as ShTangledFeedGetTimeline from "$lib/api/lexicons/types/sh/tangled/feed/getTimeline";
type TimelineItem = ShTangledFeedGetTimeline.$output["feed"][number];
const alice = { did: "did:plc:alice", handle: "alice.test" };const bob = { did: "did:plc:bob", handle: "bob.test" };const carol = { did: "did:plc:carol", handle: "carol.test" };const dan = { did: "did:plc:dan", handle: "dan.test" };
let rkey = 0;
const repo = (owner: typeof alice, slug: string) => ({ did: owner.did, slug, owner });
const follow = (actor: typeof alice, subject: string, eventAt: string) => ({ event: { $type: "sh.tangled.feed.getTimeline#followEvent", uri: `at://${actor.did}/sh.tangled.graph.follow/${rkey++}`, actor, subject: { did: `did:plc:${subject}`, handle: subject } }, eventAt }) as unknown as TimelineItem;
const star = (actor: typeof alice, slug: string, eventAt: string) => ({ event: { $type: "sh.tangled.feed.getTimeline#starEvent", uri: `at://${actor.did}/sh.tangled.feed.star/${rkey++}`, actor, repo: repo(bob, slug) }, eventAt }) as unknown as TimelineItem;
const create = (owner: typeof alice, slug: string, eventAt: string) => ({ event: { $type: "sh.tangled.feed.getTimeline#repoEvent", uri: `at://${owner.did}/sh.tangled.repo/${rkey++}`, repo: repo(owner, slug) }, eventAt }) as unknown as TimelineItem;
const fork = (owner: typeof alice, slug: string, eventAt: string) => ({ event: { $type: "sh.tangled.feed.getTimeline#repoEvent", uri: `at://${owner.did}/sh.tangled.repo/${rkey++}`, repo: repo(owner, slug), source: repo(bob, slug) }, eventAt }) as unknown as TimelineItem;
const label = (item: TimelineItem) => { const event = item.event as { actor?: { handle: string }; subject?: { handle: string }; repo: { slug: string; owner: { handle: string } }; }; return event.subject?.handle ?? event.repo.slug;};
const actorLabel = (item: TimelineItem) => { const event = item.event as { actor?: { handle: string }; repo: { owner: { handle: string } } }; return event.actor?.handle ?? event.repo.owner.handle;};
const shape = (feed: TimelineItem[]) => groupTimeline(feed).map((entry) => "actor" in entry ? [entry.kind, entry.actor.handle, ...entry.items.map((i) => label(i as TimelineItem))] : [ entry.kind, label(entry.items[0] as TimelineItem), ...entry.items.map((i) => actorLabel(i as TimelineItem)) ] );
describe("groupTimeline", () => { it("merges consecutive follows by the same actor", () => { expect( shape([ follow(alice, "a", "2026-01-01T12:00:00Z"), follow(alice, "b", "2026-01-01T11:30:00Z"), follow(alice, "c", "2026-01-01T11:00:01Z") ]) ).toEqual([["follow", "alice.test", "a", "b", "c"]]); });
it("merges stars, creations and forks the same way", () => { expect( shape([ star(alice, "a", "2026-01-01T12:00:00Z"), star(alice, "b", "2026-01-01T11:59:00Z"), create(alice, "c", "2026-01-01T11:58:00Z"), create(alice, "d", "2026-01-01T11:57:00Z"), fork(alice, "e", "2026-01-01T11:56:00Z"), fork(alice, "f", "2026-01-01T11:55:00Z") ]) ).toEqual([ ["star", "alice.test", "a", "b"], ["create", "alice.test", "c", "d"], ["fork", "alice.test", "e", "f"] ]); });
it("attributes repo events to the repo owner", () => { expect( shape([ create(alice, "a", "2026-01-01T12:00:00Z"), create(bob, "b", "2026-01-01T11:59:00Z") ]) ).toEqual([ ["create", "alice.test", "a"], ["create", "bob.test", "b"] ]); });
it("merges an uninterrupted run by one actor regardless of time", () => { expect( shape([ star(alice, "a", "2026-01-01T12:00:00Z"), star(alice, "b", "2026-01-01T08:00:00Z") ]) ).toEqual([["star", "alice.test", "a", "b"]]); });
it("merges across another actor's event within an hour", () => { expect( shape([ star(alice, "a", "2026-01-01T12:00:00Z"), star(bob, "b", "2026-01-01T11:50:00Z"), star(alice, "c", "2026-01-01T11:30:00Z") ]) ).toEqual([ ["star", "alice.test", "a", "c"], ["star", "bob.test", "b"] ]); });
it("splits across another actor's event after an hour or more", () => { expect( shape([ star(alice, "a", "2026-01-01T12:00:00Z"), star(bob, "b", "2026-01-01T11:30:00Z"), star(alice, "c", "2026-01-01T11:00:00Z") ]) ).toEqual([ ["star", "alice.test", "a"], ["star", "bob.test", "b"], ["star", "alice.test", "c"] ]); });
it("merges across the same actor's events of another kind", () => { expect( shape([ follow(alice, "a", "2026-01-01T12:00:00Z"), star(alice, "b", "2026-01-01T09:00:00Z"), follow(alice, "c", "2026-01-01T08:00:00Z"), star(alice, "d", "2026-01-01T07:00:00Z"), star(alice, "e", "2026-01-01T06:00:00Z") ]) ).toEqual([ ["follow", "alice.test", "a", "c"], ["star", "alice.test", "b", "d", "e"] ]); });
it("measures the window between neighbours, not from the first event", () => { expect( shape([ follow(alice, "a", "2026-01-01T12:00:00Z"), follow(alice, "b", "2026-01-01T11:20:00Z"), follow(alice, "c", "2026-01-01T10:40:00Z") ]) ).toEqual([["follow", "alice.test", "a", "b", "c"]]); });
it("splits on a different actor", () => { expect( shape([ follow(alice, "a", "2026-01-01T12:00:00Z"), follow(bob, "b", "2026-01-01T11:59:00Z") ]) ).toEqual([ ["follow", "alice.test", "a"], ["follow", "bob.test", "b"] ]); });
it("keys each entry by its first event uri", () => { const feed = [ follow(alice, "a", "2026-01-01T12:00:00Z"), follow(alice, "b", "2026-01-01T11:59:00Z") ]; expect(groupTimeline(feed).map((e) => e.key)).toEqual([feed[0].event.uri]); });
it("groups three or more actors on the same repo into one stargazers card", () => { expect( shape([ star(alice, "core", "2026-01-01T12:00:00Z"), create(dan, "x", "2026-01-01T11:00:00Z"), star(bob, "core", "2026-01-01T10:00:00Z"), star(carol, "core", "2026-01-01T09:00:00Z") ]) ).toEqual([ ["stargazers", "core", "alice.test", "bob.test", "carol.test"], ["create", "dan.test", "x"] ]); });
it("leaves a repo starred by fewer than three actors to actor grouping", () => { expect( shape([ star(alice, "core", "2026-01-01T12:00:00Z"), star(bob, "core", "2026-01-01T11:59:00Z") ]) ).toEqual([ ["star", "alice.test", "core"], ["star", "bob.test", "core"] ]); });
it("counts distinct actors towards the threshold", () => { expect( shape([ star(alice, "core", "2026-01-01T12:00:00Z"), star(alice, "core", "2026-01-01T11:59:00Z"), star(bob, "core", "2026-01-01T11:58:00Z") ]) ).toEqual([ ["star", "alice.test", "core", "core"], ["star", "bob.test", "core"] ]); });
it("takes a popular target out of an actor burst without breaking it", () => { expect( shape([ star(alice, "a", "2026-01-01T12:00:00Z"), star(alice, "core", "2026-01-01T11:59:00Z"), star(bob, "core", "2026-01-01T11:00:00Z"), star(alice, "b", "2026-01-01T09:00:00Z"), star(carol, "core", "2026-01-01T08:00:00Z") ]) ).toEqual([ ["star", "alice.test", "a", "b"], ["stargazers", "core", "alice.test", "bob.test", "carol.test"] ]); });
it("groups popular follow and fork targets", () => { expect( shape([ follow(alice, "hub", "2026-01-01T12:00:00Z"), fork(alice, "core", "2026-01-01T11:59:00Z"), follow(bob, "hub", "2026-01-01T11:58:00Z"), fork(bob, "core", "2026-01-01T11:57:00Z"), follow(carol, "hub", "2026-01-01T11:56:00Z"), fork(carol, "core", "2026-01-01T11:55:00Z") ]) ).toEqual([ ["followers", "hub", "alice.test", "bob.test", "carol.test"], ["forkers", "core", "alice.test", "bob.test", "carol.test"] ]); });});