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 2.8 kB View raw
1import { describe, it, expect, vi } from 'vitest'; 2import { act, createElement } from 'react'; 3import { createRoot } from 'react-dom/client'; 4 5( globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; 6 7// Stub the heavy/irrelevant children with markers. 8vi.mock( './SkyEditor', () => ( { 9 default: () => createElement( 'div', { 'data-testid': 'sky-editor' } ), 10} ) ); 11vi.mock( './CoverImagePicker', () => ( { 12 default: () => createElement( 'div', { 'data-testid': 'cover-picker' } ), 13} ) ); 14 15import EditorCanvas from './EditorCanvas'; 16 17const base = { 18 title: '', 19 onTitleChange: vi.fn(), 20 lede: '', 21 onLedeChange: vi.fn(), 22 onBlocksChange: vi.fn(), 23 cover: null, 24 onCoverChange: vi.fn(), 25}; 26 27function mount( props: Record< string, unknown > ) { 28 const container = document.createElement( 'div' ); 29 document.body.appendChild( container ); 30 act( () => createRoot( container ).render( createElement( EditorCanvas, { ...base, ...props } as never ) ) ); 31 return container; 32} 33 34// React 18 tracks a controlled value via a prototype setter; a direct `el.value = …` 35// is invisible to it, so set through the native setter to make onChange fire. 36function setValue( el: HTMLTextAreaElement, val: string ) { 37 const proto = Object.getPrototypeOf( el ); 38 Object.getOwnPropertyDescriptor( proto, 'value' )!.set!.call( el, val ); 39 el.dispatchEvent( new Event( 'input', { bubbles: true } ) ); 40} 41 42describe( 'EditorCanvas', () => { 43 it( 'renders the title + lede fields and the block editor', () => { 44 const c = mount( {} ); 45 expect( c.querySelector( 'textarea.studio__title' ) ).not.toBe( null ); 46 expect( c.querySelector( 'textarea.studio__lede' ) ).not.toBe( null ); 47 expect( c.querySelector( '[data-testid="sky-editor"]' ) ).not.toBe( null ); 48 } ); 49 50 it( 'reports title and lede edits to the parent', () => { 51 const onTitleChange = vi.fn(); 52 const onLedeChange = vi.fn(); 53 const c = mount( { onTitleChange, onLedeChange } ); 54 setValue( c.querySelector( 'textarea.studio__title' )!, 'My title' ); 55 setValue( c.querySelector( 'textarea.studio__lede' )!, 'My lede' ); 56 expect( onTitleChange ).toHaveBeenCalledWith( 'My title' ); 57 expect( onLedeChange ).toHaveBeenCalledWith( 'My lede' ); 58 } ); 59 60 it( 'shows the Bluesky-truncation hint only for a long lede', () => { 61 expect( mount( { lede: 'short' } ).querySelector( '.studio__lede-hint' ) ).toBe( null ); 62 expect( mount( { lede: 'x'.repeat( 201 ) } ).querySelector( '.studio__lede-hint' ) ).not.toBe( null ); 63 } ); 64 65 it( 'shows the cover picker only when an upload handler is provided', () => { 66 expect( mount( {} ).querySelector( '[data-testid="cover-picker"]' ) ).toBe( null ); 67 expect( 68 mount( { onCoverUpload: vi.fn() } ).querySelector( '[data-testid="cover-picker"]' ) 69 ).not.toBe( null ); 70 } ); 71} );