import { expect } from "@std/expect"; import { describe, it } from "@std/testing/bdd"; import type { PresenceRecord } from "@commonfabric/runtime-client"; import { CARET_FACET, caretFacetOf, decodeCaretFacet, participantFromRecord, } from "./presence-facets.ts"; const caret = { focused: true, cursor: { epoch: 1, version: 3 }, selection: { ranges: [{ anchor: 0, head: 2, assoc: -1 as const }], main: 0 }, basis: "provisional" as const, }; describe("presence-facets", () => { describe("decodeCaretFacet()", () => { it("returns what `caretFacetOf()` wrote, and refuses an extra key", () => { expect(decodeCaretFacet(caretFacetOf(caret))).toEqual(caret); expect(decodeCaretFacet({ ...caret, focused: false, selection: null })) .toEqual({ ...caret, focused: false, selection: null }); expect(() => decodeCaretFacet({ ...caret, document: "no" })).toThrow( "caret facet", ); }); it("refuses a focused caret without a selection", () => { expect(() => decodeCaretFacet({ ...caret, selection: null })).toThrow( "caret facet", ); }); it("refuses an invalid range, association, position, or cursor", () => { const withRanges = (ranges: unknown[]) => ({ ...caret, selection: { ranges, main: 0 }, }); expect(() => decodeCaretFacet(withRanges([{ anchor: -1, head: 0, assoc: 0 }])) ) .toThrow("range"); expect(() => decodeCaretFacet(withRanges([{ anchor: 0, head: 0 }]))) .toThrow("range"); expect(() => decodeCaretFacet(withRanges([{ anchor: 0, head: 0, assoc: 2 }])) ) .toThrow("range"); expect(() => decodeCaretFacet( withRanges([{ anchor: 2_147_483_648, head: 0, assoc: 0 }]), ) ).toThrow("range"); expect(() => decodeCaretFacet(withRanges([]))).toThrow("selection"); expect(() => decodeCaretFacet({ ...caret, cursor: { epoch: 1, version: 1.5 } }) ).toThrow("cursor"); }); }); describe("caretFacetOf()", () => { it("publishes only the main range of a selection wider than the facet carries", () => { const ranges = Array.from({ length: 20 }, (_, index) => ({ anchor: index, head: index, assoc: 0 as const, })); expect( caretFacetOf({ ...caret, selection: { ranges, main: 7 } }).selection, ).toEqual({ ranges: [{ anchor: 7, head: 7, assoc: 0 }], main: 0 }); }); it("refuses a caret its decoder would drop", () => { expect(() => caretFacetOf({ ...caret, selection: null })).toThrow( "caret facet", ); expect(() => caretFacetOf({ ...caret, selection: { ranges: [{ anchor: -1, head: 0, assoc: 0 }], main: 0, }, }) ).toThrow("range"); }); }); describe("participantFromRecord()", () => { const record: PresenceRecord = { participantId: "participant:1", principal: "did:key:z6Mk-peer", revision: 4, name: "Ada", facets: { [CARET_FACET]: caretFacetOf(caret), pointer: { x: 1 } }, }; it("returns the participant the caret facet describes", () => { expect(participantFromRecord(record)).toEqual({ participantId: "participant:1", revision: 4, name: "Ada", ...caret, }); }); it("returns `null` for a record without a readable caret facet", () => { expect( participantFromRecord({ ...record, facets: { pointer: { x: 1 } } }), ) .toBeNull(); expect( participantFromRecord({ ...record, facets: { [CARET_FACET]: { focused: "yes" } }, }), ).toBeNull(); }); }); });