Skip to content

Commit

Permalink
Blocks: Add enum validation to browser block parser (WordPress#14810)
Browse files Browse the repository at this point in the history
* Blocks: Add enum validation to browser block parser

* Block API: Remove array type in isValidByEnum JSDoc

* Block API: Fix typo in test description expectation
  • Loading branch information
aduth authored and mchowning committed Apr 15, 2019
1 parent d09229b commit 36c9def
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 4 deletions.
38 changes: 34 additions & 4 deletions packages/blocks/src/api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,36 @@ export function isOfTypes( value, types ) {
return types.some( ( type ) => isOfType( value, type ) );
}

/**
* Returns true if value is valid per the given block attribute schema type
* definition, or false otherwise.
*
* @link https://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1.1
*
* @param {*} value Value to test.
* @param {?(Array<string>|string)} type Block attribute schema type.
*
* @return {boolean} Whether value is valid.
*/
export function isValidByType( value, type ) {
return type === undefined || isOfTypes( value, castArray( type ) );
}

/**
* Returns true if value is valid per the given block attribute schema enum
* definition, or false otherwise.
*
* @link https://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1.2
*
* @param {*} value Value to test.
* @param {?Array} enumSet Block attribute schema enum.
*
* @return {boolean} Whether value is valid.
*/
export function isValidByEnum( value, enumSet ) {
return ! Array.isArray( enumSet ) || enumSet.includes( value );
}

/**
* Returns true if the given attribute schema describes a value which may be
* an ambiguous string.
Expand Down Expand Up @@ -242,7 +272,7 @@ export function parseWithAttributeSchema( innerHTML, attributeSchema ) {
* @return {*} Attribute value.
*/
export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, commentAttributes ) {
const { type } = attributeSchema;
const { type, enum: enumSet } = attributeSchema;
let value;

switch ( attributeSchema.source ) {
Expand All @@ -262,9 +292,9 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com
break;
}

if ( type !== undefined && ! isOfTypes( value, castArray( type ) ) ) {
// Reject the value if it is not valid of type. Reverting to the
// undefined value ensures the default is restored, if applicable.
if ( ! isValidByType( value, type ) || ! isValidByEnum( value, enumSet ) ) {
// Reject the value if it is not valid. Reverting to the undefined
// value ensures the default is respected, if applicable.
value = undefined;
}

Expand Down
94 changes: 94 additions & 0 deletions packages/blocks/src/api/test/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
toBooleanAttributeMatcher,
isOfType,
isOfTypes,
isValidByType,
isValidByEnum,
} from '../parser';
import {
registerBlockType,
Expand Down Expand Up @@ -180,6 +182,42 @@ describe( 'block parser', () => {
} );
} );

describe( 'isValidByType', () => {
it( 'returns true if type undefined', () => {
expect( isValidByType( null ) ).toBe( true );
} );

it( 'returns false if value is not one of types array', () => {
expect( isValidByType( null, [ 'string' ] ) ).toBe( false );
} );

it( 'returns true if value is one of types array', () => {
expect( isValidByType( null, [ 'string', 'null' ] ) ).toBe( true );
} );

it( 'returns false if value is not of type string', () => {
expect( isValidByType( null, 'string' ) ).toBe( false );
} );

it( 'returns true if value is type string', () => {
expect( isValidByType( null, 'null' ) ).toBe( true );
} );
} );

describe( 'isValidByEnum', () => {
it( 'returns true if enum set undefined', () => {
expect( isValidByEnum( 2 ) ).toBe( true );
} );

it( 'returns false if value is not of enum set', () => {
expect( isValidByEnum( 2, [ 1, 3 ] ) ).toBe( false );
} );

it( 'returns true if value is of enum set', () => {
expect( isValidByEnum( 2, [ 1, 2, 3 ] ) ).toBe( true );
} );
} );

describe( 'parseWithAttributeSchema', () => {
it( 'should return the matcher’s attribute value', () => {
const value = parseWithAttributeSchema(
Expand Down Expand Up @@ -260,6 +298,62 @@ describe( 'block parser', () => {
expect( value ).toBe( 10 );
} );

it( 'should reject type-invalid value, with default', () => {
const value = getBlockAttribute(
'number',
{
type: 'string',
default: 5,
},
'',
{ number: 10 }
);

expect( value ).toBe( 5 );
} );

it( 'should reject type-invalid value, without default', () => {
const value = getBlockAttribute(
'number',
{
type: 'string',
},
'',
{ number: 10 }
);

expect( value ).toBe( undefined );
} );

it( 'should reject enum-invalid value, with default', () => {
const value = getBlockAttribute(
'number',
{
type: 'number',
enum: [ 4, 5, 6 ],
default: 5,
},
'',
{ number: 10 }
);

expect( value ).toBe( 5 );
} );

it( 'should reject enum-invalid value, without default', () => {
const value = getBlockAttribute(
'number',
{
type: 'number',
enum: [ 4, 5, 6 ],
},
'',
{ number: 10 }
);

expect( value ).toBe( undefined );
} );

it( "should return the matcher's attribute value", () => {
const value = getBlockAttribute(
'content',
Expand Down

0 comments on commit 36c9def

Please sign in to comment.