A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

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

at trunk 3.3 kB View raw
1import { describe, expect, it } from 'vitest'; 2import { assemblePostText } from './post-text'; 3 4const URL = 'https://skypress.blog/@me/pub/3kabcde12345'; 5 6function bytes( s: string ): number { 7 return new TextEncoder().encode( s ).length; 8} 9 10describe( 'assemblePostText', () => { 11 it( 'with no lede or mentions, is title + URL (back-compat)', () => { 12 const { text, facets } = assemblePostText( { title: 'Hello', articleUrl: URL } ); 13 expect( text ).toBe( `Hello\n\n${ URL }` ); 14 expect( facets ).toHaveLength( 1 ); 15 const link = facets[ 0 ]; 16 expect( link.features[ 0 ] ).toEqual( { 17 $type: 'app.bsky.richtext.facet#link', 18 uri: URL, 19 } ); 20 expect( link.index.byteStart ).toBe( bytes( 'Hello\n\n' ) ); 21 expect( link.index.byteEnd ).toBe( bytes( 'Hello\n\n' ) + bytes( URL ) ); 22 } ); 23 24 it( 'includes the lede between title and URL when provided', () => { 25 const { text } = assemblePostText( { 26 title: 'Hello', 27 articleUrl: URL, 28 bodyLede: 'A short lede.', 29 } ); 30 expect( text ).toBe( `Hello\n\nA short lede.\n\n${ URL }` ); 31 } ); 32 33 it( 'adds a cc line with a mention facet per handle', () => { 34 const { text, facets } = assemblePostText( { 35 title: 'Hi', 36 articleUrl: URL, 37 mentions: [ 38 { did: 'did:plc:a', handle: 'alice.bsky.social', displayText: '@alice.bsky.social' }, 39 { did: 'did:plc:b', handle: 'bob.bsky.social', displayText: '@bob.bsky.social' }, 40 ], 41 } ); 42 expect( text ).toBe( `Hi\n\ncc @alice.bsky.social @bob.bsky.social\n\n${ URL }` ); 43 44 const mentions = facets.filter( 45 ( f ) => f.features[ 0 ].$type === 'app.bsky.richtext.facet#mention' 46 ); 47 expect( mentions ).toHaveLength( 2 ); 48 49 const ccPrefix = 'Hi\n\ncc '; 50 const aliceStart = bytes( ccPrefix ); 51 expect( mentions[ 0 ].index ).toEqual( { 52 byteStart: aliceStart, 53 byteEnd: aliceStart + bytes( '@alice.bsky.social' ), 54 } ); 55 expect( mentions[ 0 ].features[ 0 ] ).toEqual( { 56 $type: 'app.bsky.richtext.facet#mention', 57 did: 'did:plc:a', 58 } ); 59 } ); 60 61 it( 'computes correct byte offsets with a multibyte title (emoji)', () => { 62 const title = '🚀 Launch'; 63 const { text, facets } = assemblePostText( { 64 title, 65 articleUrl: URL, 66 mentions: [ 67 { did: 'did:plc:a', handle: 'alice.bsky.social', displayText: '@alice.bsky.social' }, 68 ], 69 } ); 70 const mention = facets.find( 71 ( f ) => f.features[ 0 ].$type === 'app.bsky.richtext.facet#mention' 72 )!; 73 const prefix = `${ title }\n\ncc `; 74 expect( mention.index.byteStart ).toBe( bytes( prefix ) ); 75 const buf = new TextEncoder().encode( text ); 76 const slice = new TextDecoder().decode( 77 buf.slice( mention.index.byteStart, mention.index.byteEnd ) 78 ); 79 expect( slice ).toBe( '@alice.bsky.social' ); 80 } ); 81 82 it( 'computes correct byte offsets for a multibyte handle', () => { 83 const handle = 'señor.bsky.social'; 84 const { text, facets } = assemblePostText( { 85 title: 'Hi', 86 articleUrl: URL, 87 mentions: [ { did: 'did:plc:s', handle, displayText: `@${ handle }` } ], 88 } ); 89 const mention = facets.find( 90 ( f ) => f.features[ 0 ].$type === 'app.bsky.richtext.facet#mention' 91 )!; 92 const buf = new TextEncoder().encode( text ); 93 const slice = new TextDecoder().decode( 94 buf.slice( mention.index.byteStart, mention.index.byteEnd ) 95 ); 96 expect( slice ).toBe( `@${ handle }` ); 97 } ); 98} );