diff --git a/apps/cli/ai/block-settings.ts b/apps/cli/ai/block-settings.ts new file mode 100644 index 0000000000..759df20079 --- /dev/null +++ b/apps/cli/ai/block-settings.ts @@ -0,0 +1,73 @@ +import { readFileSync } from 'fs'; +import path from 'path'; + +// Matches the opening comment of a block in serialized markup, e.g. +// `` or ``. Closing +// comments (``) do not match. +const BLOCK_COMMENT = / +
+ + + +
+`; + +describe( 'createBlockSettingsNotes', () => { + let blocksDir: string; + + function declare( slug: string, attributes: Record< string, unknown > ) { + mkdirSync( path.join( blocksDir, slug ), { recursive: true } ); + writeFileSync( + path.join( blocksDir, slug, 'block.json' ), + JSON.stringify( { name: `core/${ slug }`, attributes } ) + ); + } + + beforeEach( () => { + blocksDir = path.join( mkdtempSync( path.join( tmpdir(), 'block-settings-' ) ), 'blocks' ); + declare( 'navigation', { + ref: { type: 'number' }, + submenuVisibility: { type: 'string', enum: [ 'hover', 'click', 'always' ], default: 'hover' }, + showSubmenuIcon: { type: 'boolean', default: true }, + __unstableLocation: { type: 'string' }, + } ); + declare( 'site-title', { level: { type: 'number', default: 1 } } ); + declare( 'group', { tagName: { type: 'string', default: 'div' } } ); + declare( 'paragraph', { content: { type: 'rich-text', source: 'rich-text', selector: 'p' } } ); + } ); + + afterEach( () => { + rmSync( path.dirname( blocksDir ), { recursive: true, force: true } ); + } ); + + it( 'describes each core block type in the markup from its block.json', () => { + const notes = createBlockSettingsNotes( blocksDir )( HEADER ).split( '\n' ); + + const where = blocksDir.split( path.sep ).join( '/' ); + expect( notes ).toEqual( [ + `core/group settings, declared in ${ where }/group/block.json: tagName (string, default "div").`, + `core/site-title settings, declared in ${ where }/site-title/block.json: level (number, default 1).`, + `core/navigation settings, declared in ${ where }/navigation/block.json: ref (number), submenuVisibility ("hover"|"click"|"always", default "hover"), showSubmenuIcon (boolean, default true).`, + ] ); + } ); + + it( 'describes a block type once', () => { + const notes = createBlockSettingsNotes( blocksDir ); + + expect( notes( HEADER ) ).toContain( 'core/navigation' ); + expect( notes( '' ) ).toBe( '' ); + } ); + + it( 'leaves out content attributes, closing comments and undeclared blocks', () => { + const notes = createBlockSettingsNotes( blocksDir ); + + expect( notes( '

Hi

' ) ).toBe( '' ); + expect( notes( '' ) ).toBe( '' ); + expect( notes( 'No blocks here.' ) ).toBe( '' ); + } ); +} );