@wordpress/blocks
Version:
Block API for WordPress.
525 lines (471 loc) • 13.5 kB
JavaScript
/**
* Internal dependencies
*/
import { createBlock } from '../factory';
import {
getBlockTypes,
unregisterBlockType,
registerBlockType,
setDefaultBlockName,
} from '../registration';
import {
isUnmodifiedBlock,
isUnmodifiedDefaultBlock,
getAccessibleBlockLabel,
getBlockLabel,
isBlockRegistered,
__experimentalSanitizeBlockAttributes,
getBlockAttributesNamesByRole,
isContentBlock,
} from '../utils';
const noop = () => {};
describe( 'block helpers', () => {
beforeAll( () => {
// Initialize the block store.
require( '../../store' );
} );
afterEach( () => {
setDefaultBlockName( undefined );
getBlockTypes().forEach( ( block ) => {
unregisterBlockType( block.name );
} );
} );
describe( 'isUnmodifiedDefaultBlock()', () => {
it( 'should return true if the default block is unmodified', () => {
registerBlockType( 'core/test-block', {
attributes: {
align: {
type: 'string',
},
includesDefault: {
type: 'boolean',
default: true,
},
},
save: noop,
category: 'text',
title: 'test block',
} );
setDefaultBlockName( 'core/test-block' );
const unmodifiedBlock = createBlock( 'core/test-block' );
expect( isUnmodifiedDefaultBlock( unmodifiedBlock ) ).toBe( true );
} );
it( 'should return false if the default block is updated', () => {
registerBlockType( 'core/test-block', {
attributes: {
align: {
type: 'string',
},
includesDefault: {
type: 'boolean',
default: true,
},
},
save: noop,
category: 'text',
title: 'test block',
} );
setDefaultBlockName( 'core/test-block' );
const block = createBlock( 'core/test-block' );
block.attributes.align = 'left';
expect( isUnmodifiedDefaultBlock( block ) ).toBe( false );
} );
it( 'should invalidate cache if the default block name changed', () => {
registerBlockType( 'core/test-block1', {
attributes: {
includesDefault1: {
type: 'boolean',
default: true,
},
},
save: noop,
category: 'text',
title: 'test block',
} );
registerBlockType( 'core/test-block2', {
attributes: {
includesDefault2: {
type: 'boolean',
default: true,
},
},
save: noop,
category: 'text',
title: 'test block',
} );
setDefaultBlockName( 'core/test-block1' );
isUnmodifiedDefaultBlock( createBlock( 'core/test-block1' ) );
setDefaultBlockName( 'core/test-block2' );
expect(
isUnmodifiedDefaultBlock( createBlock( 'core/test-block2' ) )
).toBe( true );
} );
} );
} );
describe( 'getBlockLabel', () => {
it( 'returns only the block title when the block has no `getLabel` function', () => {
const blockType = { title: 'Recipe' };
const attributes = {};
expect( getBlockLabel( blockType, attributes ) ).toBe( 'Recipe' );
} );
it( 'returns only the block title when the block has a `getLabel` function, but it returns a falsey value', () => {
const blockType = { title: 'Recipe', __experimentalLabel: () => '' };
const attributes = {};
expect( getBlockLabel( blockType, attributes ) ).toBe( 'Recipe' );
} );
it( 'returns the block title with the label when the `getLabel` function returns a value', () => {
const blockType = {
title: 'Recipe',
__experimentalLabel: ( { heading } ) => heading,
};
const attributes = { heading: 'Cupcakes!' };
expect( getBlockLabel( blockType, attributes ) ).toBe( 'Cupcakes!' );
} );
it( 'removes any html elements from the output of the `getLabel` function', () => {
const blockType = {
title: 'Recipe',
__experimentalLabel: ( { heading } ) => heading,
};
const attributes = {
heading: '<b><span class="my-class">Cupcakes!</span></b>',
};
expect( getBlockLabel( blockType, attributes ) ).toBe( 'Cupcakes!' );
} );
} );
describe( 'getAccessibleBlockLabel', () => {
it( 'returns only the block title when the block has no `getLabel` function', () => {
const blockType = { title: 'Recipe' };
const attributes = {};
expect( getAccessibleBlockLabel( blockType, attributes ) ).toBe(
'Recipe Block'
);
} );
it( 'returns only the block title when the block has a `getLabel` function, but it returns a falsey value', () => {
const blockType = { title: 'Recipe', __experimentalLabel: () => '' };
const attributes = {};
expect( getAccessibleBlockLabel( blockType, attributes ) ).toBe(
'Recipe Block'
);
} );
it( 'returns the block title with the label when the `getLabel` function returns a value', () => {
const blockType = {
title: 'Recipe',
__experimentalLabel: ( { heading } ) => heading,
};
const attributes = { heading: 'Cupcakes!' };
expect( getAccessibleBlockLabel( blockType, attributes ) ).toBe(
'Recipe Block. Cupcakes!'
);
} );
it( 'removes any html elements from the output of the `getLabel` function', () => {
const blockType = {
title: 'Recipe',
__experimentalLabel: ( { heading } ) => heading,
};
const attributes = {
heading: '<b><span class="my-class">Cupcakes!</span></b>',
};
expect( getAccessibleBlockLabel( blockType, attributes ) ).toBe(
'Recipe Block. Cupcakes!'
);
} );
it( 'outputs the block title and label with a row number indicating the position of the block, when the optional third parameter is provided', () => {
const blockType = {
title: 'Recipe',
__experimentalLabel: ( { heading } ) => heading,
};
const attributes = { heading: 'Cupcakes!' };
expect( getAccessibleBlockLabel( blockType, attributes, 3 ) ).toBe(
'Recipe Block. Row 3. Cupcakes!'
);
} );
it( 'outputs just the block title and row number when there no label is available for the block', () => {
const blockType = { title: 'Recipe' };
const attributes = {};
expect( getAccessibleBlockLabel( blockType, attributes, 3 ) ).toBe(
'Recipe Block. Row 3'
);
} );
} );
describe( 'isBlockRegistered', () => {
it( 'returns true if the block is registered', () => {
registerBlockType( 'core/test-block', { title: 'Test block' } );
expect( isBlockRegistered( 'core/test-block' ) ).toBe( true );
unregisterBlockType( 'core/test-block' );
} );
it( 'returns false if the block is not registered', () => {
expect( isBlockRegistered( 'core/not-registered-test-block' ) ).toBe(
false
);
} );
} );
describe( 'sanitizeBlockAttributes', () => {
afterEach( () => {
getBlockTypes().forEach( ( block ) => {
unregisterBlockType( block.name );
} );
} );
it( 'sanitize block attributes not defined in the block type', () => {
registerBlockType( 'core/test-block', {
attributes: {
defined: {
type: 'string',
},
},
title: 'Test block',
} );
const attributes = __experimentalSanitizeBlockAttributes(
'core/test-block',
{
defined: 'defined-attribute',
notDefined: 'not-defined-attribute',
}
);
expect( attributes ).toEqual( {
defined: 'defined-attribute',
} );
} );
it( 'throws error if the block is not registered', () => {
expect( () => {
__experimentalSanitizeBlockAttributes(
'core/not-registered-test-block',
{}
);
} ).toThrowErrorMatchingInlineSnapshot(
`"Block type 'core/not-registered-test-block' is not registered."`
);
} );
it( 'handles undefined values and default values', () => {
registerBlockType( 'core/test-block', {
attributes: {
hasDefaultValue: {
type: 'string',
default: 'default-value',
},
noDefaultValue: {
type: 'string',
},
},
title: 'Test block',
} );
const attributes = __experimentalSanitizeBlockAttributes(
'core/test-block',
{}
);
expect( attributes ).toEqual( {
hasDefaultValue: 'default-value',
} );
} );
it( 'handles node and children sources as arrays', () => {
registerBlockType( 'core/test-block', {
attributes: {
nodeContent: {
source: 'node',
},
childrenContent: {
source: 'children',
},
withDefault: {
source: 'children',
default: 'test',
},
},
title: 'Test block',
} );
const attributes = __experimentalSanitizeBlockAttributes(
'core/test-block',
{
nodeContent: [ 'test-1', 'test-2' ],
}
);
expect( attributes ).toEqual( {
nodeContent: [ 'test-1', 'test-2' ],
childrenContent: [],
withDefault: [ 'test' ],
} );
} );
} );
describe( 'getBlockAttributesNamesByRole', () => {
beforeAll( () => {
registerBlockType( 'core/test-block-1', {
attributes: {
align: {
type: 'string',
},
content: {
type: 'boolean',
role: 'content',
},
level: {
type: 'number',
role: 'content',
},
color: {
type: 'string',
role: 'other',
},
},
save: noop,
category: 'text',
title: 'test block 1',
} );
registerBlockType( 'core/test-block-2', {
attributes: {
align: { type: 'string' },
content: { type: 'boolean' },
color: { type: 'string' },
},
save: noop,
category: 'text',
title: 'test block 2',
} );
registerBlockType( 'core/test-block-3', {
save: noop,
category: 'text',
title: 'test block 3',
} );
} );
afterAll( () => {
[
'core/test-block-1',
'core/test-block-2',
'core/test-block-3',
].forEach( unregisterBlockType );
} );
it( 'should return empty array if block has no attributes', () => {
expect( getBlockAttributesNamesByRole( 'core/test-block-3' ) ).toEqual(
[]
);
} );
it( 'should return all attribute names if no role is provided', () => {
expect( getBlockAttributesNamesByRole( 'core/test-block-1' ) ).toEqual(
expect.arrayContaining( [ 'align', 'content', 'level', 'color' ] )
);
} );
it( 'should return proper results with existing attributes and provided role', () => {
expect(
getBlockAttributesNamesByRole( 'core/test-block-1', 'content' )
).toEqual( expect.arrayContaining( [ 'content', 'level' ] ) );
expect(
getBlockAttributesNamesByRole( 'core/test-block-1', 'other' )
).toEqual( [ 'color' ] );
expect(
getBlockAttributesNamesByRole( 'core/test-block-1', 'not-exists' )
).toEqual( [] );
// A block with no `role` in any attributes.
expect(
getBlockAttributesNamesByRole( 'core/test-block-2', 'content' )
).toEqual( [] );
} );
} );
describe( 'isContentBlock', () => {
it( 'returns true if the block has a content role attribute', () => {
registerBlockType( 'core/test-content-block', {
attributes: {
content: {
type: 'string',
role: 'content',
},
align: {
type: 'string',
},
},
save: noop,
category: 'text',
title: 'test content block',
} );
expect( isContentBlock( 'core/test-content-block' ) ).toBe( true );
} );
it( 'returns false if the block does not have a content role attribute', () => {
registerBlockType( 'core/test-non-content-block', {
attributes: {
content: {
type: 'string',
},
align: {
type: 'string',
},
},
save: noop,
category: 'text',
title: 'test non-content block',
} );
expect( isContentBlock( 'core/test-non-content-block' ) ).toBe( false );
} );
} );
describe( 'isUnmodifiedBlock', () => {
beforeAll( () => {
registerBlockType( 'core/test-block', {
attributes: {
align: {
type: 'string',
},
includesDefault: {
type: 'boolean',
default: true,
},
content: {
type: 'string',
role: 'content',
},
metadata: {
type: 'object',
},
},
save: noop,
category: 'text',
title: 'test block',
} );
} );
afterAll( () => {
unregisterBlockType( 'core/test-block' );
} );
it( 'should return true if all attributes match their defaults', () => {
const block = createBlock( 'core/test-block' );
expect( isUnmodifiedBlock( block ) ).toBe( true );
} );
it( 'should return false if any attribute does not match its default', () => {
const block = createBlock( 'core/test-block', {
includesDefault: false,
} );
expect( isUnmodifiedBlock( block ) ).toBe( false );
} );
it( 'should return true if attributes with a specific role match their defaults', () => {
const block = createBlock( 'core/test-block' );
expect( isUnmodifiedBlock( block, 'content' ) ).toBe( true );
} );
it( 'should return false if attributes with a specific role do not match their defaults', () => {
const block = createBlock( 'core/test-block', {
content: 'Updated content',
} );
expect( isUnmodifiedBlock( block, 'content' ) ).toBe( false );
} );
it( 'should return true if no attributes exist for the specified role', () => {
const block = createBlock( 'core/test-block' );
expect( isUnmodifiedBlock( block, 'non-existent-role' ) ).toBe( true );
} );
it( 'should return false if no attributes exist for the role and some are modified', () => {
const block = createBlock( 'core/test-block', {
align: 'center',
content: 'Updated content',
} );
expect( isUnmodifiedBlock( block, 'non-existent-role' ) ).toBe( false );
} );
it( 'should return true if metadata attributes is not modified for role content', () => {
const block = createBlock( 'core/test-block' );
expect( isUnmodifiedBlock( block, 'content' ) ).toBe( true );
} );
it( 'should return false if metadata attributes is modified for role content', () => {
const block = createBlock( 'core/test-block', {
metadata: {
bindings: {
content: {
source: 'core/post-meta',
args: { key: 'genre' },
},
},
},
} );
expect( isUnmodifiedBlock( block, 'content' ) ).toBe( false );
} );
} );