Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try adding more permissive block validation modes #19188

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
358 changes: 317 additions & 41 deletions packages/blocks/src/api/test/validation.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* WordPress dependencies
*/
import { addFilter, removeFilter } from '@wordpress/hooks';

/**
* Internal dependencies
*/
Expand All @@ -24,6 +29,12 @@ import {
getBlockTypes,
getBlockType,
} from '../registration';
import {
getSaveContent,
} from '../serializer';
import {
normalizeBlockType,
} from '../utils';

describe( 'validation', () => {
const defaultBlockSettings = {
Expand Down Expand Up @@ -606,60 +617,325 @@ describe( 'validation', () => {
} );

describe( 'isValidBlockContent()', () => {
it( 'returns false if block is not valid', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );
describe( 'no-save-error', () => {
beforeAll( () => {
addFilter( 'editor.blockValidationMode', 'testNoSaveError', () => ( 'no-save-error' ) );
} );

const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Apples'
);
afterAll( () => {
removeFilter( 'editor.blockValidationMode', 'testNoSaveError' );
} );

expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
} );
it( 'returns true if block serializes but does not match original content', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas' };
registerBlockType( blockName, defaultBlockSettings );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'Apples',
);

it( 'returns false if error occurs while generating block save', () => {
registerBlockType( 'core/test-block', {
...defaultBlockSettings,
save() {
throw new Error();
},
const blockType = normalizeBlockType( blockName );

expect( console ).not.toHaveWarned();
expect( console ).not.toHaveErrored();
expect( isValid ).toBe( true );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( 'Bananas' );
} );

const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Bananas'
);
it( 'returns false if error occurs while generating block save', () => {
registerBlockType( 'core/test-block', {
...defaultBlockSettings,
save() {
throw new Error();
},
} );

expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
} );
const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Bananas',
);

it( 'returns true is block is valid', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
} );

const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Bananas'
);
it( 'returns true if original had an extra class', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', classes: [ 'foo', 'bar' ] };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
const sortedClasses = [ ...attributes.classes ].sort().join( ' ' );
return (
<div className={ sortedClasses }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div class="foo bar baz">Bananas</div>',
);

const blockType = normalizeBlockType( blockName );

expect( isValid ).toBe( true );
expect( console ).not.toHaveWarned();
expect( console ).not.toHaveErrored();
expect( isValid ).toBe( true );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div class="bar foo">Bananas</div>' );
} );

it( 'really does not care if serialized content does not match original content', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', classes: [ 'foo', 'bar' ] };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
const sortedClasses = [ ...attributes.classes ].sort().join( ' ' );
return (
<div className={ sortedClasses }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div class="sugar">Blueberries</div>',
);

const blockType = normalizeBlockType( blockName );

expect( console ).not.toHaveWarned();
expect( console ).not.toHaveErrored();
expect( isValid ).toBe( true );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div class="bar foo">Bananas</div>' );
} );
} );
describe( 'strict', () => {
it( 'returns false if block is not valid', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );

it( 'works also when block type object is passed as object', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );
const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Apples'
);

const isValid = isValidBlockContent(
getBlockType( 'core/test-block' ),
{ fruit: 'Bananas' },
'Bananas'
);
expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
} );

it( 'returns false if error occurs while generating block save', () => {
registerBlockType( 'core/test-block', {
...defaultBlockSettings,
save() {
throw new Error();
},
} );

const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Bananas'
);

expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
} );

it( 'returns true if class order does not match', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', classes: [ 'foo', 'bar' ] };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
const sortedClasses = [ ...attributes.classes ].sort().join( ' ' );
return (
<div className={ sortedClasses }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div class="foo bar">Bananas</div>',
);

expect( isValid ).toBe( true );
const blockType = normalizeBlockType( blockName );

expect( console ).not.toHaveWarned();
expect( console ).not.toHaveErrored();
expect( isValid ).toBe( true );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div class="bar foo">Bananas</div>' );
} );

it( 'returns false if has extra classes', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', classes: [ 'foo', 'bar' ] };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
const sortedClasses = [ ...attributes.classes ].sort().join( ' ' );
return (
<div className={ sortedClasses }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div class="foo bar baz">Bananas</div>',
);

const blockType = normalizeBlockType( blockName );

expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div class="bar foo">Bananas</div>' );
} );

it( 'returns false if has extra data attributes', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', classes: [ 'foo', 'bar' ] };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
const sortedClasses = [ ...attributes.classes ].sort().join( ' ' );
return (
<div className={ sortedClasses }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div class="foo bar" data-foo="baz">Bananas</div>',
);

const blockType = normalizeBlockType( blockName );

expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div class="bar foo">Bananas</div>' );
} );

it( 'returns false if tags do not match', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', level: 2 };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
if ( attributes.level === 1 ) {
return (
<h1>
{ attributes.fruit }
</h1>
);
}

if ( attributes.level === 2 ) {
return (
<h2>
{ attributes.fruit }
</h2>
);
}

return (
<h3>
{ attributes.fruit }
</h3>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<h3 class="foo bar" data-foo="baz">Bananas</h3>',
);

const blockType = normalizeBlockType( blockName );

expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<h2>Bananas</h2>' );
} );

it( 'returns false if there are floating point rounding errors', () => {
const blockName = 'core/test-block';
const blockAttributes = { fruit: 'Bananas', x: 0.07, y: 0.07 };
registerBlockType( blockName, {
...defaultBlockSettings,
save: ( { attributes } ) => {
return (
<div style={ `background-position:${ attributes.x * 100 }% ${ attributes.y * 100 }%` }>
{ attributes.fruit }
</div>
);
},
} );

const isValid = isValidBlockContent(
blockName,
blockAttributes,
'<div style="background-position:7% 7%">Bananas</div>',
);

const blockType = normalizeBlockType( blockName );

expect( console ).toHaveWarned();
expect( console ).toHaveErrored();
expect( isValid ).toBe( false );
expect( getSaveContent( blockType, blockAttributes ) ).toBe( '<div style="background-position:7.000000000000001% 7.000000000000001%">Bananas</div>' );
} );

it( 'returns true is block is valid', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );

const isValid = isValidBlockContent(
'core/test-block',
{ fruit: 'Bananas' },
'Bananas'
);

expect( isValid ).toBe( true );
} );

it( 'works also when block type object is passed as object', () => {
registerBlockType( 'core/test-block', defaultBlockSettings );

const isValid = isValidBlockContent(
getBlockType( 'core/test-block' ),
{ fruit: 'Bananas' },
'Bananas'
);

expect( isValid ).toBe( true );
} );
} );
} );
} );
Loading