diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md
index bcacdd68de76e..6bf2454fd03ac 100644
--- a/docs/designers-developers/developers/data/data-core-block-editor.md
+++ b/docs/designers-developers/developers/data/data-core-block-editor.md
@@ -776,6 +776,17 @@ Whether redo history exists.
## Actions
+### initBlocks
+
+Returns an action object used in signalling that blocks state should be
+intialized using a specified array of blocks,
+
+This action reset the undo/redo history
+
+*Parameters*
+
+ * blocks: Array of blocks.
+
### resetBlocks
Returns an action object used in signalling that blocks state should be
@@ -1019,4 +1030,14 @@ Returns an action object used in signalling that undo history should pop.
### createUndoLevel
Returns an action object used in signalling that undo history record should
-be created.
\ No newline at end of file
+be created.
+
+### __unstableSaveResuableBlock
+
+Returns an action object used in signalling that a temporary reusable blocks have been saved
+in order to switch its temporary id with the real id.
+
+*Parameters*
+
+ * id: Reusable block's id.
+ * updatedId: Updated block's id.
\ No newline at end of file
diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md
index e8759c34d1790..7de4e4f80ad1a 100644
--- a/docs/designers-developers/developers/data/data-core-editor.md
+++ b/docs/designers-developers/developers/data/data-core-editor.md
@@ -584,19 +584,6 @@ before state satisfies the given predicate function.
Whether predicate matches for some history.
-### getBlockListSettings
-
-Returns the Block List settings of a block, if any exist.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Block settings of the block if set.
-
### isPostLocked
Returns whether the post is locked.
@@ -694,6 +681,18 @@ Return the current block list.
Block list.
+### isEditorReady
+
+Is the editor ready
+
+*Parameters*
+
+ * state: null
+
+*Returns*
+
+is Ready.
+
## Actions
### setupEditor
@@ -741,7 +740,6 @@ Returns an action object used to setup the editor state when first opening an ed
*Parameters*
* post: Post object.
- * blocks: Array of blocks.
### editPost
diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php
index f3e38e0c7b53a..c25a394f01936 100644
--- a/lib/packages-dependencies.php
+++ b/lib/packages-dependencies.php
@@ -53,6 +53,7 @@
'wp-data',
'wp-element',
'wp-i18n',
+ 'wp-notices',
),
'wp-blocks' => array(
'lodash',
diff --git a/package-lock.json b/package-lock.json
index 96bef502043dc..fa2faf11ea78c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2350,8 +2350,9 @@
"@wordpress/data": "file:packages/data",
"@wordpress/element": "file:packages/element",
"@wordpress/i18n": "file:packages/i18n",
+ "@wordpress/notices": "file:packages/notices",
"lodash": "^4.17.10",
- "redux-optimist": "^1.0.0",
+ "refx": "^3.0.0",
"rememo": "^3.0.0"
}
},
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index d4f3cf7f757c7..c115eb96e7d31 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -28,6 +28,7 @@
"@wordpress/data": "file:../data",
"@wordpress/element": "file:../element",
"@wordpress/i18n": "file:../i18n",
+ "@wordpress/notices": "file:../notices",
"lodash": "^4.17.10",
"refx": "^3.0.0",
"rememo": "^3.0.0"
diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js
index 024b0225b3c3e..03a215da73d80 100644
--- a/packages/block-editor/src/index.js
+++ b/packages/block-editor/src/index.js
@@ -3,6 +3,7 @@
*/
import '@wordpress/blocks';
+import '@wordpress/notices';
/**
* Internal dependencies
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index b8f7179f21b73..1b85ba8cdd41d 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -12,7 +12,6 @@ import {
keys,
isEqual,
isEmpty,
- overSome,
get,
} from 'lodash';
@@ -32,22 +31,6 @@ import {
} from './defaults';
import { insertAt, moveTo } from './array';
-/**
- * Returns a post attribute value, flattening nested rendered content using its
- * raw value in place of its original object form.
- *
- * @param {*} value Original value.
- *
- * @return {*} Raw value.
- */
-export function getPostRawValue( value ) {
- if ( value && 'object' === typeof value && 'raw' in value ) {
- return value.raw;
- }
-
- return value;
-}
-
/**
* Given an array of blocks, returns an object where each key is a nesting
* context, the value of which is an array of block client IDs existing within
@@ -191,23 +174,6 @@ export function isUpdatingSameBlockAttribute( action, previousAction ) {
);
}
-/**
- * Returns true if, given the currently dispatching action and the previously
- * dispatched action, the two actions are editing the same post property, or
- * false otherwise.
- *
- * @param {Object} action Currently dispatching action.
- * @param {Object} previousAction Previously dispatched action.
- *
- * @return {boolean} Whether actions are updating the same post property.
- */
-export function isUpdatingSamePostProperty( action, previousAction ) {
- return (
- action.type === 'EDIT_POST' &&
- hasSameKeys( action.edits, previousAction.edits )
- );
-}
-
/**
* Returns true if, given the currently dispatching action and the previously
* dispatched action, the two actions are modifying the same property such that
@@ -223,10 +189,7 @@ export function shouldOverwriteState( action, previousAction ) {
return false;
}
- return overSome( [
- isUpdatingSameBlockAttribute,
- isUpdatingSamePostProperty,
- ] )( action, previousAction );
+ return isUpdatingSameBlockAttribute( action, previousAction );
}
/**
diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js
new file mode 100644
index 0000000000000..c1d08c75dda50
--- /dev/null
+++ b/packages/block-editor/src/store/test/actions.js
@@ -0,0 +1,338 @@
+/**
+ * Internal dependencies
+ */
+import {
+ replaceBlocks,
+ startTyping,
+ stopTyping,
+ enterFormattedText,
+ exitFormattedText,
+ toggleSelection,
+ resetBlocks,
+ updateBlockAttributes,
+ updateBlock,
+ selectBlock,
+ startMultiSelect,
+ stopMultiSelect,
+ multiSelect,
+ clearSelectedBlock,
+ replaceBlock,
+ insertBlock,
+ insertBlocks,
+ showInsertionPoint,
+ hideInsertionPoint,
+ mergeBlocks,
+ redo,
+ undo,
+ removeBlocks,
+ removeBlock,
+ toggleBlockMode,
+ updateBlockListSettings,
+} from '../actions';
+
+describe( 'actions', () => {
+ describe( 'resetBlocks', () => {
+ it( 'should return the RESET_BLOCKS actions', () => {
+ const blocks = [];
+ const result = resetBlocks( blocks );
+ expect( result ).toEqual( {
+ type: 'RESET_BLOCKS',
+ blocks,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlockAttributes', () => {
+ it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => {
+ const clientId = 'myclientid';
+ const attributes = {};
+ const result = updateBlockAttributes( clientId, attributes );
+ expect( result ).toEqual( {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId,
+ attributes,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlock', () => {
+ it( 'should return the UPDATE_BLOCK action', () => {
+ const clientId = 'myclientid';
+ const updates = {};
+ const result = updateBlock( clientId, updates );
+ expect( result ).toEqual( {
+ type: 'UPDATE_BLOCK',
+ clientId,
+ updates,
+ } );
+ } );
+ } );
+
+ describe( 'selectBlock', () => {
+ it( 'should return the SELECT_BLOCK action', () => {
+ const clientId = 'myclientid';
+ const result = selectBlock( clientId, -1 );
+ expect( result ).toEqual( {
+ type: 'SELECT_BLOCK',
+ initialPosition: -1,
+ clientId,
+ } );
+ } );
+ } );
+
+ describe( 'startMultiSelect', () => {
+ it( 'should return the START_MULTI_SELECT', () => {
+ expect( startMultiSelect() ).toEqual( {
+ type: 'START_MULTI_SELECT',
+ } );
+ } );
+ } );
+
+ describe( 'stopMultiSelect', () => {
+ it( 'should return the Stop_MULTI_SELECT', () => {
+ expect( stopMultiSelect() ).toEqual( {
+ type: 'STOP_MULTI_SELECT',
+ } );
+ } );
+ } );
+ describe( 'multiSelect', () => {
+ it( 'should return MULTI_SELECT action', () => {
+ const start = 'start';
+ const end = 'end';
+ expect( multiSelect( start, end ) ).toEqual( {
+ type: 'MULTI_SELECT',
+ start,
+ end,
+ } );
+ } );
+ } );
+
+ describe( 'clearSelectedBlock', () => {
+ it( 'should return CLEAR_SELECTED_BLOCK action', () => {
+ expect( clearSelectedBlock() ).toEqual( {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+ } );
+ } );
+
+ describe( 'replaceBlock', () => {
+ it( 'should return the REPLACE_BLOCKS action', () => {
+ const block = {
+ clientId: 'ribs',
+ };
+
+ expect( replaceBlock( [ 'chicken' ], block ) ).toEqual( {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ block ],
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'replaceBlocks', () => {
+ it( 'should return the REPLACE_BLOCKS action', () => {
+ const blocks = [ {
+ clientId: 'ribs',
+ } ];
+
+ expect( replaceBlocks( [ 'chicken' ], blocks ) ).toEqual( {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks,
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'insertBlock', () => {
+ it( 'should return the INSERT_BLOCKS action', () => {
+ const block = {
+ clientId: 'ribs',
+ };
+ const index = 5;
+ expect( insertBlock( block, index, 'testclientid' ) ).toEqual( {
+ type: 'INSERT_BLOCKS',
+ blocks: [ block ],
+ index,
+ rootClientId: 'testclientid',
+ time: expect.any( Number ),
+ updateSelection: true,
+ } );
+ } );
+ } );
+
+ describe( 'insertBlocks', () => {
+ it( 'should return the INSERT_BLOCKS action', () => {
+ const blocks = [ {
+ clientId: 'ribs',
+ } ];
+ const index = 3;
+ expect( insertBlocks( blocks, index, 'testclientid' ) ).toEqual( {
+ type: 'INSERT_BLOCKS',
+ blocks,
+ index,
+ rootClientId: 'testclientid',
+ time: expect.any( Number ),
+ updateSelection: true,
+ } );
+ } );
+ } );
+
+ describe( 'showInsertionPoint', () => {
+ it( 'should return the SHOW_INSERTION_POINT action', () => {
+ expect( showInsertionPoint() ).toEqual( {
+ type: 'SHOW_INSERTION_POINT',
+ } );
+ } );
+ } );
+
+ describe( 'hideInsertionPoint', () => {
+ it( 'should return the HIDE_INSERTION_POINT action', () => {
+ expect( hideInsertionPoint() ).toEqual( {
+ type: 'HIDE_INSERTION_POINT',
+ } );
+ } );
+ } );
+
+ describe( 'mergeBlocks', () => {
+ it( 'should return MERGE_BLOCKS action', () => {
+ const firstBlockClientId = 'blockA';
+ const secondBlockClientId = 'blockB';
+ expect( mergeBlocks( firstBlockClientId, secondBlockClientId ) ).toEqual( {
+ type: 'MERGE_BLOCKS',
+ blocks: [ firstBlockClientId, secondBlockClientId ],
+ } );
+ } );
+ } );
+
+ describe( 'redo', () => {
+ it( 'should return REDO action', () => {
+ expect( redo() ).toEqual( {
+ type: 'REDO',
+ } );
+ } );
+ } );
+
+ describe( 'undo', () => {
+ it( 'should return UNDO action', () => {
+ expect( undo() ).toEqual( {
+ type: 'UNDO',
+ } );
+ } );
+ } );
+
+ describe( 'removeBlocks', () => {
+ it( 'should return REMOVE_BLOCKS action', () => {
+ const clientIds = [ 'clientId' ];
+ expect( removeBlocks( clientIds ) ).toEqual( {
+ type: 'REMOVE_BLOCKS',
+ clientIds,
+ selectPrevious: true,
+ } );
+ } );
+ } );
+
+ describe( 'removeBlock', () => {
+ it( 'should return REMOVE_BLOCKS action', () => {
+ const clientId = 'myclientid';
+ expect( removeBlock( clientId ) ).toEqual( {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [
+ clientId,
+ ],
+ selectPrevious: true,
+ } );
+ expect( removeBlock( clientId, false ) ).toEqual( {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [
+ clientId,
+ ],
+ selectPrevious: false,
+ } );
+ } );
+ } );
+
+ describe( 'toggleBlockMode', () => {
+ it( 'should return TOGGLE_BLOCK_MODE action', () => {
+ const clientId = 'myclientid';
+ expect( toggleBlockMode( clientId ) ).toEqual( {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId,
+ } );
+ } );
+ } );
+
+ describe( 'startTyping', () => {
+ it( 'should return the START_TYPING action', () => {
+ expect( startTyping() ).toEqual( {
+ type: 'START_TYPING',
+ } );
+ } );
+ } );
+
+ describe( 'stopTyping', () => {
+ it( 'should return the STOP_TYPING action', () => {
+ expect( stopTyping() ).toEqual( {
+ type: 'STOP_TYPING',
+ } );
+ } );
+ } );
+
+ describe( 'enterFormattedText', () => {
+ it( 'should return the ENTER_FORMATTED_TEXT action', () => {
+ expect( enterFormattedText() ).toEqual( {
+ type: 'ENTER_FORMATTED_TEXT',
+ } );
+ } );
+ } );
+
+ describe( 'exitFormattedText', () => {
+ it( 'should return the EXIT_FORMATTED_TEXT action', () => {
+ expect( exitFormattedText() ).toEqual( {
+ type: 'EXIT_FORMATTED_TEXT',
+ } );
+ } );
+ } );
+
+ describe( 'toggleSelection', () => {
+ it( 'should return the TOGGLE_SELECTION action with default value for isSelectionEnabled = true', () => {
+ expect( toggleSelection() ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: true,
+ } );
+ } );
+
+ it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = true as passed in the argument', () => {
+ expect( toggleSelection( true ) ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: true,
+ } );
+ } );
+
+ it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = false as passed in the argument', () => {
+ expect( toggleSelection( false ) ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: false,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlockListSettings', () => {
+ it( 'should return the UPDATE_BLOCK_LIST_SETTINGS with undefined settings', () => {
+ expect( updateBlockListSettings( 'chicken' ) ).toEqual( {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: 'chicken',
+ settings: undefined,
+ } );
+ } );
+
+ it( 'should return the UPDATE_BLOCK_LIST_SETTINGS action with the passed settings', () => {
+ expect( updateBlockListSettings( 'chicken', { chicken: 'ribs' } ) ).toEqual( {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: 'chicken',
+ settings: { chicken: 'ribs' },
+ } );
+ } );
+ } );
+} );
diff --git a/packages/editor/src/store/test/array.js b/packages/block-editor/src/store/test/array.js
similarity index 100%
rename from packages/editor/src/store/test/array.js
rename to packages/block-editor/src/store/test/array.js
diff --git a/packages/block-editor/src/store/test/effects.js b/packages/block-editor/src/store/test/effects.js
new file mode 100644
index 0000000000000..6b81becf4e79d
--- /dev/null
+++ b/packages/block-editor/src/store/test/effects.js
@@ -0,0 +1,290 @@
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ getBlockTypes,
+ unregisterBlockType,
+ registerBlockType,
+ createBlock,
+} from '@wordpress/blocks';
+import { dispatch as dataDispatch, createRegistry } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import actions, {
+ updateEditorSettings,
+ mergeBlocks,
+ replaceBlocks,
+ resetBlocks,
+ selectBlock,
+ setTemplateValidity,
+} from '../actions';
+import effects, { validateBlocksToTemplate } from '../effects';
+import * as selectors from '../selectors';
+import reducer from '../reducer';
+import applyMiddlewares from '../middlewares';
+import '../../';
+
+describe( 'effects', () => {
+ beforeAll( () => {
+ jest.spyOn( dataDispatch( 'core/notices' ), 'createErrorNotice' );
+ jest.spyOn( dataDispatch( 'core/notices' ), 'createSuccessNotice' );
+ } );
+
+ beforeEach( () => {
+ dataDispatch( 'core/notices' ).createErrorNotice.mockReset();
+ dataDispatch( 'core/notices' ).createSuccessNotice.mockReset();
+ } );
+
+ const defaultBlockSettings = { save: () => 'Saved', category: 'common', title: 'block title' };
+
+ describe( '.MERGE_BLOCKS', () => {
+ const handler = effects.MERGE_BLOCKS;
+ const defaultGetBlock = selectors.getBlock;
+
+ afterEach( () => {
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
+ selectors.getBlock = defaultGetBlock;
+ } );
+
+ it( 'should only focus the blockA if the blockA has no merge function', () => {
+ registerBlockType( 'core/test-block', defaultBlockSettings );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 1 );
+ expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken' ) );
+ } );
+
+ it( 'should merge the blocks if blocks of the same type', () => {
+ registerBlockType( 'core/test-block', {
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: { content: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 2 );
+ expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken', -1 ) );
+ expect( dispatch ).toHaveBeenCalledWith( {
+ ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken ribs' },
+ } ] ),
+ time: expect.any( Number ),
+ } );
+ } );
+
+ it( 'should not merge the blocks have different types without transformation', () => {
+ registerBlockType( 'core/test-block', {
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ registerBlockType( 'core/test-block-2', defaultBlockSettings );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block2',
+ attributes: { content: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).not.toHaveBeenCalled();
+ } );
+
+ it( 'should transform and merge the blocks', () => {
+ registerBlockType( 'core/test-block', {
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ registerBlockType( 'core/test-block-2', {
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ transforms: {
+ to: [ {
+ type: 'block',
+ blocks: [ 'core/test-block' ],
+ transform: ( { content2 } ) => {
+ return createBlock( 'core/test-block', {
+ content: content2,
+ } );
+ },
+ } ],
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block 2',
+ } );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block-2',
+ attributes: { content2: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 2 );
+ // expect( dispatch ).toHaveBeenCalledWith( focusBlock( 'chicken', { offset: -1 } ) );
+ expect( dispatch ).toHaveBeenCalledWith( {
+ ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken ribs' },
+ } ] ),
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'validateBlocksToTemplate', () => {
+ let store;
+ beforeEach( () => {
+ store = createRegistry().registerStore( 'test', {
+ actions,
+ selectors,
+ reducer,
+ } );
+ applyMiddlewares( store );
+
+ registerBlockType( 'core/test-block', defaultBlockSettings );
+ } );
+
+ afterEach( () => {
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
+ } );
+
+ it( 'should return undefined if no template assigned', () => {
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return undefined if invalid but unlocked', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/foo', {} ],
+ ],
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return undefined if locked and valid', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/test-block' ],
+ ],
+ templateLock: 'all',
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return validity set action if invalid on default state', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/foo' ],
+ ],
+ templateLock: 'all',
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toEqual( setTemplateValidity( false ) );
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js
new file mode 100644
index 0000000000000..f6a75d2a0b37c
--- /dev/null
+++ b/packages/block-editor/src/store/test/reducer.js
@@ -0,0 +1,1733 @@
+/**
+ * External dependencies
+ */
+import { values, noop } from 'lodash';
+import deepFreeze from 'deep-freeze';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ registerBlockType,
+ unregisterBlockType,
+ createBlock,
+} from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import {
+ hasSameKeys,
+ isUpdatingSameBlockAttribute,
+ shouldOverwriteState,
+ editor,
+ isTyping,
+ isCaretWithinFormattedText,
+ blockSelection,
+ preferences,
+ blocksMode,
+ insertionPoint,
+ template,
+ blockListSettings,
+} from '../reducer';
+
+describe( 'state', () => {
+ describe( 'hasSameKeys()', () => {
+ it( 'returns false if two objects do not have the same keys', () => {
+ const a = { foo: 10 };
+ const b = { bar: 10 };
+
+ expect( hasSameKeys( a, b ) ).toBe( false );
+ } );
+
+ it( 'returns false if two objects have the same keys', () => {
+ const a = { foo: 10 };
+ const b = { foo: 20 };
+
+ expect( hasSameKeys( a, b ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'isUpdatingSameBlockAttribute()', () => {
+ it( 'should return false if not updating block attributes', () => {
+ const action = {
+ type: 'EDIT_POST',
+ edits: {},
+ };
+ const previousAction = {
+ type: 'EDIT_POST',
+ edits: {},
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if not updating the same block', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ attributes: {
+ foo: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if not updating the same block attributes', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ bar: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return true if updating the same block attributes', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'shouldOverwriteState()', () => {
+ it( 'should return false if no previous action', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = undefined;
+
+ expect( shouldOverwriteState( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if the action types are different', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'REPLACE_BLOCKS',
+ };
+
+ expect( shouldOverwriteState( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return true if updating same block attribute', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 20,
+ },
+ };
+
+ expect( shouldOverwriteState( action, previousAction ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'editor()', () => {
+ beforeAll( () => {
+ registerBlockType( 'core/test-block', {
+ save: noop,
+ edit: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ } );
+
+ afterAll( () => {
+ unregisterBlockType( 'core/test-block' );
+ } );
+
+ it( 'should return history (empty edits, blocks) by default', () => {
+ const state = editor( undefined, {} );
+
+ expect( state.past ).toEqual( [] );
+ expect( state.future ).toEqual( [] );
+ expect( state.present.blocks.byClientId ).toEqual( {} );
+ expect( state.present.blocks.order ).toEqual( {} );
+ } );
+
+ it( 'should key by reset blocks clientId', () => {
+ const original = editor( undefined, {} );
+ const state = editor( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [ { clientId: 'bananas', innerBlocks: [] } ],
+ } );
+
+ expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
+ expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'bananas' );
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ 'bananas' ],
+ bananas: [],
+ } );
+ } );
+
+ it( 'should key by reset blocks clientId, including inner blocks', () => {
+ const original = editor( undefined, {} );
+ const state = editor( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'bananas',
+ innerBlocks: [ { clientId: 'apples', innerBlocks: [] } ],
+ } ],
+ } );
+
+ expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ 'bananas' ],
+ apples: [],
+ bananas: [ 'apples' ],
+ } );
+ } );
+
+ it( 'should insert block', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
+ expect( values( state.present.blocks.byClientId )[ 1 ].clientId ).toBe( 'ribs' );
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ 'chicken', 'ribs' ],
+ chicken: [],
+ ribs: [],
+ } );
+ } );
+
+ it( 'should replace the block', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
+ expect( values( state.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
+ expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'wings' );
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ 'wings' ],
+ wings: [],
+ } );
+ } );
+
+ it( 'should replace the nested block', () => {
+ const nestedBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
+ const replacementBlock = createBlock( 'core/test-block' );
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+
+ const state = editor( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ nestedBlock.clientId ],
+ blocks: [ replacementBlock ],
+ } );
+
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ replacementBlock.clientId ],
+ [ replacementBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should replace the block even if the new block clientId is the same', () => {
+ const originalState = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const replacedState = editor( originalState, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( replacedState.present.blocks.byClientId ) ).toHaveLength( 1 );
+ expect( values( originalState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/test-block' );
+ expect( values( replacedState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
+ expect( values( replacedState.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'chicken' );
+ expect( replacedState.present.blocks.order ).toEqual( {
+ '': [ 'chicken' ],
+ chicken: [],
+ } );
+
+ const nestedBlock = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ };
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
+ const replacementNestedBlock = {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ attributes: {},
+ innerBlocks: [],
+ };
+
+ const originalNestedState = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+
+ const replacedNestedState = editor( originalNestedState, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ nestedBlock.clientId ],
+ blocks: [ replacementNestedBlock ],
+ } );
+
+ expect( replacedNestedState.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ replacementNestedBlock.clientId ],
+ [ replacementNestedBlock.clientId ]: [],
+ } );
+
+ expect( originalNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/test-block' );
+ expect( replacedNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/freeform' );
+ } );
+
+ it( 'should update the block', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ isValid: false,
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( deepFreeze( original ), {
+ type: 'UPDATE_BLOCK',
+ clientId: 'chicken',
+ updates: {
+ attributes: { content: 'ribs' },
+ isValid: true,
+ },
+ } );
+
+ expect( state.present.blocks.byClientId.chicken ).toEqual( {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ isValid: true,
+ } );
+
+ expect( state.present.blocks.attributes.chicken ).toEqual( {
+ content: 'ribs',
+ } );
+ } );
+
+ it( 'should update the reusable block reference if the temporary id is swapped', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/block',
+ attributes: {
+ ref: 'random-clientId',
+ },
+ isValid: false,
+ innerBlocks: [],
+ } ],
+ } );
+
+ const state = editor( deepFreeze( original ), {
+ type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
+ id: 'random-clientId',
+ updatedId: 3,
+ } );
+
+ expect( state.present.blocks.byClientId.chicken ).toEqual( {
+ clientId: 'chicken',
+ name: 'core/block',
+ isValid: false,
+ } );
+
+ expect( state.present.blocks.attributes.chicken ).toEqual( {
+ ref: 3,
+ } );
+ } );
+
+ it( 'should move the block up', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
+ } );
+
+ it( 'should move the nested block up', () => {
+ const movedBlock = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlock ] );
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ movedBlock.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ movedBlock.clientId, siblingBlock.clientId ],
+ [ movedBlock.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should move multiple blocks up', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs', 'veggies' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'veggies', 'chicken' ] );
+ } );
+
+ it( 'should move multiple nested blocks up', () => {
+ const movedBlockA = createBlock( 'core/test-block' );
+ const movedBlockB = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlockA, movedBlockB ] );
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ movedBlockA.clientId, movedBlockB.clientId, siblingBlock.clientId ],
+ [ movedBlockA.clientId ]: [],
+ [ movedBlockB.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should not move the first block up', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.present.blocks.order ).toBe( original.present.blocks.order );
+ } );
+
+ it( 'should move the block down', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
+ } );
+
+ it( 'should move the nested block down', () => {
+ const movedBlock = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlock, siblingBlock ] );
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ movedBlock.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlock.clientId ],
+ [ movedBlock.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should move multiple blocks down', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'chicken', 'ribs' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'veggies', 'chicken', 'ribs' ] );
+ } );
+
+ it( 'should move multiple nested blocks down', () => {
+ const movedBlockA = createBlock( 'core/test-block' );
+ const movedBlockB = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlockA, movedBlockB, siblingBlock ] );
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.present.blocks.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlockA.clientId, movedBlockB.clientId ],
+ [ movedBlockA.clientId ]: [],
+ [ movedBlockB.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should not move the last block down', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state.present.blocks.order ).toBe( original.present.blocks.order );
+ } );
+
+ it( 'should remove the block', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
+ expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
+ expect( state.present.blocks.byClientId ).toEqual( {
+ ribs: {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ },
+ } );
+ expect( state.present.blocks.attributes ).toEqual( {
+ ribs: {},
+ } );
+ } );
+
+ it( 'should remove multiple blocks', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken', 'veggies' ],
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
+ expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
+ expect( state.present.blocks.order ).not.toHaveProperty( 'veggies' );
+ expect( state.present.blocks.byClientId ).toEqual( {
+ ribs: {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ },
+ } );
+ expect( state.present.blocks.attributes ).toEqual( {
+ ribs: {},
+ } );
+ } );
+
+ it( 'should cascade remove to include inner blocks', () => {
+ const block = createBlock( 'core/test-block', {}, [
+ createBlock( 'core/test-block', {}, [
+ createBlock( 'core/test-block' ),
+ ] ),
+ ] );
+
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ block ],
+ } );
+
+ const state = editor( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ block.clientId ],
+ } );
+
+ expect( state.present.blocks.byClientId ).toEqual( {} );
+ expect( state.present.blocks.order ).toEqual( {
+ '': [],
+ } );
+ } );
+
+ it( 'should insert at the specified index', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'loquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+
+ const state = editor( original, {
+ type: 'INSERT_BLOCKS',
+ index: 1,
+ blocks: [ {
+ clientId: 'persimmon',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 3 );
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'kumquat', 'persimmon', 'loquat' ] );
+ } );
+
+ it( 'should move block to lower index', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 0,
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken', 'veggies' ] );
+ } );
+
+ it( 'should move block to higher index', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 2,
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'veggies', 'ribs' ] );
+ } );
+
+ it( 'should not move block if passed same index', () => {
+ const original = editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = editor( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 1,
+ } );
+
+ expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'ribs', 'veggies' ] );
+ } );
+
+ describe( 'blocks', () => {
+ it( 'should not reset any blocks that are not in the post', () => {
+ const actions = [
+ {
+ type: 'RESET_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block1',
+ innerBlocks: [
+ { clientId: 'block11', innerBlocks: [] },
+ { clientId: 'block12', innerBlocks: [] },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'RECEIVE_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block2',
+ innerBlocks: [
+ { clientId: 'block21', innerBlocks: [] },
+ { clientId: 'block22', innerBlocks: [] },
+ ],
+ },
+ ],
+ },
+ ];
+ const original = deepFreeze( actions.reduce( editor, undefined ) );
+
+ const state = editor( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block3',
+ innerBlocks: [
+ { clientId: 'block31', innerBlocks: [] },
+ { clientId: 'block32', innerBlocks: [] },
+ ],
+ },
+ ],
+ } );
+
+ expect( state.present.blocks.byClientId ).toEqual( {
+ block2: { clientId: 'block2' },
+ block21: { clientId: 'block21' },
+ block22: { clientId: 'block22' },
+ block3: { clientId: 'block3' },
+ block31: { clientId: 'block31' },
+ block32: { clientId: 'block32' },
+ } );
+ } );
+
+ describe( 'byClientId', () => {
+ it( 'should ignore updates to non-existent block', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.present.blocks.byClientId ).toBe( original.present.blocks.byClientId );
+ } );
+
+ it( 'should return with same reference if no changes in updates', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.present.blocks.byClientId ).toBe( state.present.blocks.byClientId );
+ } );
+ } );
+
+ describe( 'attributes', () => {
+ it( 'should return with attribute block updates', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.present.blocks.attributes.kumquat.updated ).toBe( true );
+ } );
+
+ it( 'should accumulate attribute block updates', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ moreUpdated: true,
+ },
+ } );
+
+ expect( state.present.blocks.attributes.kumquat ).toEqual( {
+ updated: true,
+ moreUpdated: true,
+ } );
+ } );
+
+ it( 'should ignore updates to non-existent block', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.present.blocks.attributes ).toBe( original.present.blocks.attributes );
+ } );
+
+ it( 'should return with same reference if no changes in updates', () => {
+ const original = deepFreeze( editor( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = editor( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.present.blocks.attributes ).toBe( state.present.blocks.attributes );
+ } );
+ } );
+ } );
+
+ describe( 'withHistory', () => {
+ it( 'should overwrite present history if updating same attributes', () => {
+ let state;
+
+ state = editor( state, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( state.past ).toHaveLength( 1 );
+
+ state = editor( state, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ test: 1,
+ },
+ } );
+
+ state = editor( state, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ test: 2,
+ },
+ } );
+
+ expect( state.past ).toHaveLength( 2 );
+ } );
+
+ it( 'should not overwrite present history if updating different attributes', () => {
+ let state;
+
+ state = editor( state, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( state.past ).toHaveLength( 1 );
+
+ state = editor( state, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ test: 1,
+ },
+ } );
+
+ state = editor( state, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ other: 1,
+ },
+ } );
+
+ expect( state.past ).toHaveLength( 3 );
+ } );
+ } );
+ } );
+
+ describe( 'insertionPoint', () => {
+ it( 'should default to null', () => {
+ const state = insertionPoint( undefined, {} );
+
+ expect( state ).toBe( null );
+ } );
+
+ it( 'should set insertion point', () => {
+ const state = insertionPoint( null, {
+ type: 'SHOW_INSERTION_POINT',
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+
+ expect( state ).toEqual( {
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+ } );
+
+ it( 'should clear the insertion point', () => {
+ const original = deepFreeze( {
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+ const state = insertionPoint( original, {
+ type: 'HIDE_INSERTION_POINT',
+ } );
+
+ expect( state ).toBe( null );
+ } );
+ } );
+
+ describe( 'isTyping()', () => {
+ it( 'should set the typing flag to true', () => {
+ const state = isTyping( false, {
+ type: 'START_TYPING',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should set the typing flag to false', () => {
+ const state = isTyping( false, {
+ type: 'STOP_TYPING',
+ } );
+
+ expect( state ).toBe( false );
+ } );
+ } );
+
+ describe( 'isCaretWithinFormattedText()', () => {
+ it( 'should set the flag to true', () => {
+ const state = isCaretWithinFormattedText( false, {
+ type: 'ENTER_FORMATTED_TEXT',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should set the flag to false', () => {
+ const state = isCaretWithinFormattedText( true, {
+ type: 'EXIT_FORMATTED_TEXT',
+ } );
+
+ expect( state ).toBe( false );
+ } );
+ } );
+
+ describe( 'blockSelection()', () => {
+ it( 'should return with block clientId as selected', () => {
+ const state = blockSelection( undefined, {
+ type: 'SELECT_BLOCK',
+ clientId: 'kumquat',
+ initialPosition: -1,
+ } );
+
+ expect( state ).toEqual( {
+ start: 'kumquat',
+ end: 'kumquat',
+ initialPosition: -1,
+ isMultiSelecting: false,
+ isEnabled: true,
+ } );
+ } );
+
+ it( 'should set multi selection', () => {
+ const original = deepFreeze( { isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'MULTI_SELECT',
+ start: 'ribs',
+ end: 'chicken',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should set continuous multi selection', () => {
+ const original = deepFreeze( { isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'MULTI_SELECT',
+ start: 'ribs',
+ end: 'chicken',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: true,
+ } );
+ } );
+
+ it( 'should start multi selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'START_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: true,
+ } );
+ } );
+
+ it( 'should return same reference if already multi-selecting', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'START_MULTI_SELECT',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should end multi selection with selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should return same reference if already ended multi-selecting', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should end multi selection without selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not update the state if the block is already selected', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
+
+ const state1 = blockSelection( original, {
+ type: 'SELECT_BLOCK',
+ clientId: 'ribs',
+ } );
+
+ expect( state1 ).toBe( original );
+ } );
+
+ it( 'should unset multi selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
+
+ const state1 = blockSelection( original, {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+
+ expect( state1 ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should return same reference if clearing selection but no selection', () => {
+ const original = deepFreeze( { start: null, end: null, isMultiSelecting: false } );
+
+ const state1 = blockSelection( original, {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+
+ expect( state1 ).toBe( original );
+ } );
+
+ it( 'should select inserted block', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
+
+ const state3 = blockSelection( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ } ],
+ updateSelection: true,
+ } );
+
+ expect( state3 ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not select inserted block if updateSelection flag is false', () => {
+ const original = deepFreeze( { start: 'a', end: 'b' } );
+
+ const state3 = blockSelection( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ } ],
+ updateSelection: false,
+ } );
+
+ expect( state3 ).toEqual( {
+ start: 'a',
+ end: 'b',
+ } );
+ } );
+
+ it( 'should not update the state if the block moved is already selected', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
+ const state = blockSelection( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should replace the selected block', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toEqual( {
+ start: 'wings',
+ end: 'wings',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not replace the selected block if we keep it when replacing blocks', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [
+ {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ },
+ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should reset if replacing with empty set', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [],
+ } );
+
+ expect( state ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should keep the selected block', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'ribs' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should remove the selection if we are removing the selected block', () => {
+ const original = deepFreeze( {
+ start: 'chicken',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ const state = blockSelection( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should keep the selection if we are not removing the selected block', () => {
+ const original = deepFreeze( {
+ start: 'chicken',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ const state = blockSelection( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+ } );
+
+ describe( 'preferences()', () => {
+ it( 'should apply all defaults', () => {
+ const state = preferences( undefined, {} );
+
+ expect( state ).toEqual( {
+ insertUsage: {},
+ } );
+ } );
+ it( 'should record recently used blocks', () => {
+ const state = preferences( deepFreeze( { insertUsage: {} } ), {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'bacon',
+ name: 'core-embed/twitter',
+ } ],
+ time: 123456,
+ } );
+
+ expect( state ).toEqual( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123456,
+ count: 1,
+ insert: { name: 'core-embed/twitter' },
+ },
+ },
+ } );
+
+ const twoRecentBlocks = preferences( deepFreeze( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123456,
+ count: 1,
+ insert: { name: 'core-embed/twitter' },
+ },
+ },
+ } ), {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'eggs',
+ name: 'core-embed/twitter',
+ }, {
+ clientId: 'bacon',
+ name: 'core/block',
+ attributes: { ref: 123 },
+ } ],
+ time: 123457,
+ } );
+
+ expect( twoRecentBlocks ).toEqual( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123457,
+ count: 2,
+ insert: { name: 'core-embed/twitter' },
+ },
+ 'core/block/123': {
+ time: 123457,
+ count: 1,
+ insert: { name: 'core/block', ref: 123 },
+ },
+ },
+ } );
+ } );
+ } );
+
+ describe( 'blocksMode', () => {
+ it( 'should set mode to html if not set', () => {
+ const action = {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId: 'chicken',
+ };
+ const value = blocksMode( deepFreeze( {} ), action );
+
+ expect( value ).toEqual( { chicken: 'html' } );
+ } );
+
+ it( 'should toggle mode to visual if set as html', () => {
+ const action = {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId: 'chicken',
+ };
+ const value = blocksMode( deepFreeze( { chicken: 'html' } ), action );
+
+ expect( value ).toEqual( { chicken: 'visual' } );
+ } );
+ } );
+
+ describe( 'template', () => {
+ it( 'should default to visible', () => {
+ const state = template( undefined, {} );
+
+ expect( state ).toEqual( { isValid: true } );
+ } );
+
+ it( 'should reset the validity flag', () => {
+ const original = deepFreeze( { isValid: false, template: [] } );
+ const state = template( original, {
+ type: 'SET_TEMPLATE_VALIDITY',
+ isValid: true,
+ } );
+
+ expect( state ).toEqual( { isValid: true, template: [] } );
+ } );
+ } );
+
+ describe( 'blockListSettings', () => {
+ it( 'should add new settings', () => {
+ const original = deepFreeze( {} );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+ } );
+
+ it( 'should return same reference if updated as the same', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should return same reference if updated settings not assigned and id not exists', () => {
+ const original = deepFreeze( {} );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should update the settings of a block', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/list' ],
+ },
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/list' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+ } );
+
+ it( 'should remove existing settings if updated settings not assigned', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ } );
+
+ expect( state ).toEqual( {} );
+ } );
+
+ it( 'should remove the settings of a block when it is replaced', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+ } );
+
+ it( 'should remove the settings of a block when it is removed', () => {
+ const original = deepFreeze( {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ } );
+
+ expect( state ).toEqual( {} );
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js
new file mode 100644
index 0000000000000..b4baa2a69d4dd
--- /dev/null
+++ b/packages/block-editor/src/store/test/selectors.js
@@ -0,0 +1,2815 @@
+/**
+ * External dependencies
+ */
+import { filter } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ registerBlockType,
+ unregisterBlockType,
+ setFreeformContentHandlerName,
+} from '@wordpress/blocks';
+import { RawHTML } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import * as selectors from '../selectors';
+
+const {
+ hasEditorUndo,
+ hasEditorRedo,
+ getBlockDependantsCacheBust,
+ getBlockName,
+ getBlock,
+ getBlocks,
+ getBlockCount,
+ getClientIdsWithDescendants,
+ getClientIdsOfDescendants,
+ hasSelectedBlock,
+ getSelectedBlock,
+ getSelectedBlockClientId,
+ getBlockRootClientId,
+ getBlockHierarchyRootClientId,
+ getGlobalBlockCount,
+ getMultiSelectedBlockClientIds,
+ getMultiSelectedBlocks,
+ getMultiSelectedBlocksStartClientId,
+ getMultiSelectedBlocksEndClientId,
+ getBlockOrder,
+ getBlockIndex,
+ getPreviousBlockClientId,
+ getNextBlockClientId,
+ isBlockSelected,
+ hasSelectedInnerBlock,
+ isBlockWithinSelection,
+ hasMultiSelection,
+ isBlockMultiSelected,
+ isFirstMultiSelectedBlock,
+ getBlockMode,
+ isTyping,
+ isCaretWithinFormattedText,
+ getBlockInsertionPoint,
+ isBlockInsertionPointVisible,
+ isSelectionEnabled,
+ canInsertBlockType,
+ getInserterItems,
+ isValidTemplate,
+ getTemplate,
+ getTemplateLock,
+ getBlockListSettings,
+ INSERTER_UTILITY_HIGH,
+ INSERTER_UTILITY_MEDIUM,
+ INSERTER_UTILITY_LOW,
+} = selectors;
+
+describe( 'selectors', () => {
+ let cachedSelectors;
+
+ beforeAll( () => {
+ cachedSelectors = filter( selectors, ( selector ) => selector.clear );
+ } );
+
+ beforeEach( () => {
+ registerBlockType( 'core/block', {
+ save: () => null,
+ category: 'reusable',
+ title: 'Reusable Block Stub',
+ supports: {
+ inserter: false,
+ },
+ } );
+
+ registerBlockType( 'core/test-block-a', {
+ save: ( props ) => props.attributes.text,
+ category: 'formatting',
+ title: 'Test Block A',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ } );
+
+ registerBlockType( 'core/test-block-b', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'Test Block B',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ supports: {
+ multiple: false,
+ },
+ } );
+
+ registerBlockType( 'core/test-block-c', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'Test Block C',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ parent: [ 'core/test-block-b' ],
+ } );
+
+ registerBlockType( 'core/test-freeform', {
+ save: ( props ) => { props.attributes.content },
+ category: 'common',
+ title: 'Test Freeform Content Handler',
+ icon: 'test',
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ } );
+
+ setFreeformContentHandlerName( 'core/test-freeform' );
+
+ cachedSelectors.forEach( ( { clear } ) => clear() );
+ } );
+
+ afterEach( () => {
+ unregisterBlockType( 'core/block' );
+ unregisterBlockType( 'core/test-block-a' );
+ unregisterBlockType( 'core/test-block-b' );
+ unregisterBlockType( 'core/test-block-c' );
+ unregisterBlockType( 'core/test-freeform' );
+
+ setFreeformContentHandlerName( undefined );
+ } );
+
+ describe( 'hasEditorUndo', () => {
+ it( 'should return true when the past history is not empty', () => {
+ const state = {
+ editor: {
+ past: [
+ {},
+ ],
+ },
+ };
+
+ expect( hasEditorUndo( state ) ).toBe( true );
+ } );
+
+ it( 'should return false when the past history is empty', () => {
+ const state = {
+ editor: {
+ past: [],
+ },
+ };
+
+ expect( hasEditorUndo( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'hasEditorRedo', () => {
+ it( 'should return true when the future history is not empty', () => {
+ const state = {
+ editor: {
+ future: [
+ {},
+ ],
+ },
+ };
+
+ expect( hasEditorRedo( state ) ).toBe( true );
+ } );
+
+ it( 'should return false when the future history is empty', () => {
+ const state = {
+ editor: {
+ future: [],
+ },
+ };
+
+ expect( hasEditorRedo( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getBlockDependantsCacheBust', () => {
+ const rootBlock = { clientId: 123, name: 'core/paragraph' };
+ const rootBlockAttributes = {};
+ const rootOrder = [ 123 ];
+
+ it( 'returns an unchanging reference', () => {
+ const rootBlockOrder = [];
+
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const nextState = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on added inner block', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const nextState = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: [ 456 ],
+ 456: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns an unchanging reference on unchanging inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlock = { clientId: 456, name: 'core/paragraph' };
+ const childBlockAttributes = {};
+ const childBlockOrder = [];
+
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const nextState = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on updated inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlockOrder = [];
+
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const nextState = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: { content: [ 'foo' ] },
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on updated grandchild inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlock = { clientId: 456, name: 'core/paragraph' };
+ const childBlockAttributes = {};
+ const childBlockOrder = [ 789 ];
+ const grandChildBlockOrder = [];
+
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ 789: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ 789: grandChildBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const nextState = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ 789: { content: [ 'foo' ] },
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ 789: grandChildBlockOrder,
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+ } );
+
+ describe( 'getBlockName', () => {
+ it( 'returns null if no block by clientId', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
+
+ expect( name ).toBe( null );
+ } );
+
+ it( 'returns block name', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ name: 'core/paragraph',
+ },
+ },
+ attributes: {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {},
+ },
+ order: {
+ '': [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
+
+ expect( name ).toBe( 'core/paragraph' );
+ } );
+ } );
+
+ describe( 'getBlock', () => {
+ it( 'should return the block', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [],
+ } );
+ } );
+
+ it( 'should return null if the block is not present in state', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect( getBlock( state, 123 ) ).toBe( null );
+ } );
+
+ it( 'should include inner blocks', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/paragraph' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [ 456 ],
+ 456: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [ {
+ clientId: 456,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ } );
+
+ it( 'should merge meta attributes for the block', () => {
+ registerBlockType( 'core/meta-block', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'test block',
+ attributes: {
+ foo: {
+ type: 'string',
+ source: 'meta',
+ meta: 'foo',
+ },
+ },
+ } );
+
+ const state = {
+ settings: {
+ __experimentalMetaSource: {
+ value: {
+ foo: 'bar',
+ },
+ },
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/meta-block' },
+ },
+ attributes: {
+ 123: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/meta-block',
+ attributes: {
+ foo: 'bar',
+ },
+ innerBlocks: [],
+ } );
+
+ unregisterBlockType( 'core/meta-block' );
+ } );
+ } );
+
+ describe( 'getBlocks', () => {
+ it( 'should return the ordered blocks', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+
+ expect( getBlocks( state ) ).toEqual( [
+ { clientId: 123, name: 'core/paragraph', attributes: {}, innerBlocks: [] },
+ { clientId: 23, name: 'core/heading', attributes: {}, innerBlocks: [] },
+ ] );
+ } );
+ } );
+
+ describe( 'getClientIdsOfDescendants', () => {
+ it( 'should return the ids of any descendants, given an array of clientIds', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
+ 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
+ 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
+ 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
+ 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
+ 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
+ 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
+ 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
+ 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
+ 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
+ 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
+ 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
+ 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
+ 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
+ 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
+ },
+ attributes: {
+ 'uuid-2': {},
+ 'uuid-4': {},
+ 'uuid-6': {},
+ 'uuid-8': {},
+ 'uuid-10': {},
+ 'uuid-12': {},
+ 'uuid-14': {},
+ 'uuid-16': {},
+ 'uuid-18': {},
+ 'uuid-20': {},
+ 'uuid-22': {},
+ 'uuid-24': {},
+ 'uuid-26': {},
+ 'uuid-28': {},
+ 'uuid-30': {},
+ },
+ order: {
+ '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
+ 'uuid-2': [ ],
+ 'uuid-4': [ ],
+ 'uuid-6': [ ],
+ 'uuid-8': [ ],
+ 'uuid-10': [ 'uuid-12', 'uuid-14' ],
+ 'uuid-12': [ 'uuid-16' ],
+ 'uuid-14': [ 'uuid-18' ],
+ 'uuid-16': [ ],
+ 'uuid-18': [ 'uuid-24' ],
+ 'uuid-20': [ ],
+ 'uuid-22': [ ],
+ 'uuid-24': [ 'uuid-26', 'uuid-28' ],
+ 'uuid-26': [ ],
+ 'uuid-28': [ 'uuid-30' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+ expect( getClientIdsOfDescendants( state, [ 'uuid-10' ] ) ).toEqual( [
+ 'uuid-12',
+ 'uuid-14',
+ 'uuid-16',
+ 'uuid-18',
+ 'uuid-24',
+ 'uuid-26',
+ 'uuid-28',
+ 'uuid-30',
+ ] );
+ } );
+ } );
+
+ describe( 'getClientIdsWithDescendants', () => {
+ it( 'should return the ids for top-level blocks and their descendants of any depth (for nested blocks).', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
+ 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
+ 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
+ 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
+ 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
+ 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
+ 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
+ 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
+ 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
+ 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
+ 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
+ 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
+ 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
+ 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
+ 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
+ },
+ attributes: {
+ 'uuid-2': {},
+ 'uuid-4': {},
+ 'uuid-6': {},
+ 'uuid-8': {},
+ 'uuid-10': {},
+ 'uuid-12': {},
+ 'uuid-14': {},
+ 'uuid-16': {},
+ 'uuid-18': {},
+ 'uuid-20': {},
+ 'uuid-22': {},
+ 'uuid-24': {},
+ 'uuid-26': {},
+ 'uuid-28': {},
+ 'uuid-30': {},
+ },
+ order: {
+ '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
+ 'uuid-2': [ ],
+ 'uuid-4': [ ],
+ 'uuid-6': [ ],
+ 'uuid-8': [ ],
+ 'uuid-10': [ 'uuid-12', 'uuid-14' ],
+ 'uuid-12': [ 'uuid-16' ],
+ 'uuid-14': [ 'uuid-18' ],
+ 'uuid-16': [ ],
+ 'uuid-18': [ 'uuid-24' ],
+ 'uuid-20': [ ],
+ 'uuid-22': [ ],
+ 'uuid-24': [ 'uuid-26', 'uuid-28' ],
+ 'uuid-26': [ ],
+ 'uuid-28': [ 'uuid-30' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ };
+ expect( getClientIdsWithDescendants( state ) ).toEqual( [
+ 'uuid-6',
+ 'uuid-8',
+ 'uuid-10',
+ 'uuid-22',
+ 'uuid-12',
+ 'uuid-14',
+ 'uuid-16',
+ 'uuid-18',
+ 'uuid-24',
+ 'uuid-26',
+ 'uuid-28',
+ 'uuid-30',
+ ] );
+ } );
+ } );
+
+ describe( 'getBlockCount', () => {
+ it( 'should return the number of top-level blocks in the post', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockCount( state ) ).toBe( 2 );
+ } );
+
+ it( 'should return the number of blocks in a nested context', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/columns' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ 789: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [ 456, 789 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockCount( state, '123' ) ).toBe( 2 );
+ } );
+ } );
+
+ describe( 'hasSelectedBlock', () => {
+ it( 'should return false if no selection', () => {
+ const state = {
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( false );
+ } );
+
+ it( 'should return false if multi-selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if singular selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'getGlobalBlockCount', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/heading' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ 789: {},
+ },
+ order: {
+ '': [ 123, 456 ],
+ },
+ },
+ },
+ },
+ };
+
+ it( 'should return the global number of blocks in the post', () => {
+ expect( getGlobalBlockCount( state ) ).toBe( 2 );
+ } );
+
+ it( 'should return the global number of blocks in the post of a given type', () => {
+ expect( getGlobalBlockCount( state, 'core/paragraph' ) ).toBe( 1 );
+ } );
+
+ it( 'should return 0 if no blocks exist', () => {
+ const emptyState = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ },
+ },
+ };
+ expect( getGlobalBlockCount( emptyState ) ).toBe( 0 );
+ expect( getGlobalBlockCount( emptyState, 'core/heading' ) ).toBe( 0 );
+ } );
+ } );
+
+ describe( 'getSelectedBlockClientId', () => {
+ it( 'should return null if no block is selected', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toBe( null );
+ } );
+
+ it( 'should return null if there is multi selection', () => {
+ const state = {
+ blockSelection: { start: 23, end: 123 },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toBe( null );
+ } );
+
+ it( 'should return the selected block ClientId', () => {
+ const state = {
+ editor: { present: { blocks: { byClientId: { 23: { name: 'fake block' } } } } },
+ blockSelection: { start: 23, end: 23 },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toEqual( 23 );
+ } );
+ } );
+
+ describe( 'getSelectedBlock', () => {
+ it( 'should return null if no block is selected', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getSelectedBlock( state ) ).toBe( null );
+ } );
+
+ it( 'should return null if there is multi selection', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ blockSelection: { start: 23, end: 123 },
+ };
+
+ expect( getSelectedBlock( state ) ).toBe( null );
+ } );
+
+ it( 'should return the selected block', () => {
+ const state = {
+ currentPost: {},
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ blockSelection: { start: 23, end: 23 },
+ };
+
+ expect( getSelectedBlock( state ) ).toEqual( {
+ clientId: 23,
+ name: 'core/heading',
+ attributes: {},
+ innerBlocks: [],
+ } );
+ } );
+ } );
+
+ describe( 'getBlockRootClientId', () => {
+ it( 'should return null if the block does not exist', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {},
+ },
+ },
+ },
+ };
+
+ expect( getBlockRootClientId( state, 56 ) ).toBeNull();
+ } );
+
+ it( 'should return root ClientId relative the block ClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockRootClientId( state, 56 ) ).toBe( '123' );
+ } );
+ } );
+
+ describe( 'getBlockHierarchyRootClientId', () => {
+ it( 'should return the given block if the block has no parents', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {},
+ },
+ },
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( 56 );
+ } );
+
+ it( 'should return root ClientId relative the block ClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( '123' );
+ } );
+
+ it( 'should return the top level root ClientId relative the block ClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ '123', '23' ],
+ 123: [ '456', '56' ],
+ 56: [ '12' ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, '12' ) ).toBe( '123' );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlockClientIds', () => {
+ it( 'should return empty if there is no multi selection', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [] );
+ } );
+
+ it( 'should return selected block clientIds if there is multi selection', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 4, 3, 2 ] );
+ } );
+
+ it( 'should return selected block clientIds if there is multi selection (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ 4: [ 9, 8, 7, 6 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 7, end: 9 },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 9, 8, 7 ] );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocks', () => {
+ it( 'should return the same reference on subsequent invocations of empty selection', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ blockSelection: { start: null, end: null },
+ currentPost: {},
+ };
+
+ expect(
+ getMultiSelectedBlocks( state )
+ ).toBe( getMultiSelectedBlocks( state ) );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocksStartClientId', () => {
+ it( 'returns null if there is no multi selection', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlocksStartClientId( state ) ).toBeNull();
+ } );
+
+ it( 'returns multi selection start', () => {
+ const state = {
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlocksStartClientId( state ) ).toBe( 2 );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocksEndClientId', () => {
+ it( 'returns null if there is no multi selection', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlocksEndClientId( state ) ).toBeNull();
+ } );
+
+ it( 'returns multi selection end', () => {
+ const state = {
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlocksEndClientId( state ) ).toBe( 4 );
+ } );
+ } );
+
+ describe( 'getBlockOrder', () => {
+ it( 'should return the ordered block ClientIds of top-level blocks by default', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockOrder( state ) ).toEqual( [ 123, 23 ] );
+ } );
+
+ it( 'should return the ordered block ClientIds at a specified rootClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockOrder( state, '123' ) ).toEqual( [ 456 ] );
+ } );
+ } );
+
+ describe( 'getBlockIndex', () => {
+ it( 'should return the block order', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockIndex( state, 23 ) ).toBe( 1 );
+ } );
+
+ it( 'should return the block order (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getBlockIndex( state, 56, '123' ) ).toBe( 1 );
+ } );
+ } );
+
+ describe( 'getPreviousBlockClientId', () => {
+ it( 'should return the previous block', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 23 ) ).toEqual( 123 );
+ } );
+
+ it( 'should return the previous block (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 56, '123' ) ).toEqual( 456 );
+ } );
+
+ it( 'should return null for the first block', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 123 ) ).toBeNull();
+ } );
+
+ it( 'should return null for the first block (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 456, '123' ) ).toBeNull();
+ } );
+ } );
+
+ describe( 'getNextBlockClientId', () => {
+ it( 'should return the following block', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 123 ) ).toEqual( 23 );
+ } );
+
+ it( 'should return the following block (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 456, '123' ) ).toEqual( 56 );
+ } );
+
+ it( 'should return null for the last block', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 23 ) ).toBeNull();
+ } );
+
+ it( 'should return null for the last block (nested context)', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 56, '123' ) ).toBeNull();
+ } );
+ } );
+
+ describe( 'isBlockSelected', () => {
+ it( 'should return true if the block is selected', () => {
+ const state = {
+ blockSelection: { start: 123, end: 123 },
+ };
+
+ expect( isBlockSelected( state, 123 ) ).toBe( true );
+ } );
+
+ it( 'should return false if a multi-selection range exists', () => {
+ const state = {
+ blockSelection: { start: 123, end: 124 },
+ };
+
+ expect( isBlockSelected( state, 123 ) ).toBe( false );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( isBlockSelected( state, 23 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'hasSelectedInnerBlock', () => {
+ it( 'should return false if the selected block is a child of the given ClientId', () => {
+ const state = {
+ blockSelection: { start: 5, end: 5 },
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ 4: [ 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( hasSelectedInnerBlock( state, 4 ) ).toBe( false );
+ } );
+
+ it( 'should return true if the selected block is a child of the given ClientId', () => {
+ const state = {
+ blockSelection: { start: 3, end: 3 },
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ 4: [ 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( hasSelectedInnerBlock( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return true if a multi selection exists that contains children of the block with the given ClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ 6: [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+ expect( hasSelectedInnerBlock( state, 6 ) ).toBe( true );
+ } );
+
+ it( 'should return false if a multi selection exists bot does not contains children of the block with the given ClientId', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ 3: [ 2, 1 ],
+ 6: [ 5, 4 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 5, end: 4 },
+ };
+ expect( hasSelectedInnerBlock( state, 3 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isBlockWithinSelection', () => {
+ it( 'should return true if the block is selected but not the last', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is the last selected', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 3 ) ).toBe( false );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 2 ) ).toBe( false );
+ } );
+
+ it( 'should return false if there is no selection', () => {
+ const state = {
+ blockSelection: {},
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 4 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'hasMultiSelection', () => {
+ it( 'should return false if no selection', () => {
+ const state = {
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( false );
+ } );
+
+ it( 'should return false if singular selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if multi-selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'isBlockMultiSelected', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ it( 'should return true if the block is multi selected', () => {
+ expect( isBlockMultiSelected( state, 3 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not multi selected', () => {
+ expect( isBlockMultiSelected( state, 5 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isFirstMultiSelectedBlock', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ it( 'should return true if the block is first in multi selection', () => {
+ expect( isFirstMultiSelectedBlock( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not first in multi selection', () => {
+ expect( isFirstMultiSelectedBlock( state, 3 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getBlockMode', () => {
+ it( 'should return "visual" if unset', () => {
+ const state = {
+ blocksMode: {},
+ };
+
+ expect( getBlockMode( state, 123 ) ).toEqual( 'visual' );
+ } );
+
+ it( 'should return the block mode', () => {
+ const state = {
+ blocksMode: {
+ 123: 'html',
+ },
+ };
+
+ expect( getBlockMode( state, 123 ) ).toEqual( 'html' );
+ } );
+ } );
+
+ describe( 'isTyping', () => {
+ it( 'should return the isTyping flag if the block is selected', () => {
+ const state = {
+ isTyping: true,
+ };
+
+ expect( isTyping( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ isTyping: false,
+ };
+
+ expect( isTyping( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isCaretWithinFormattedText', () => {
+ it( 'returns true if the isCaretWithinFormattedText state is also true', () => {
+ const state = {
+ isCaretWithinFormattedText: true,
+ };
+
+ expect( isCaretWithinFormattedText( state ) ).toBe( true );
+ } );
+
+ it( 'returns false if the isCaretWithinFormattedText state is also false', () => {
+ const state = {
+ isCaretWithinFormattedText: false,
+ };
+
+ expect( isCaretWithinFormattedText( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isSelectionEnabled', () => {
+ it( 'should return true if selection is enable', () => {
+ const state = {
+ blockSelection: {
+ isEnabled: true,
+ },
+ };
+
+ expect( isSelectionEnabled( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if selection is disabled', () => {
+ const state = {
+ blockSelection: {
+ isEnabled: false,
+ },
+ };
+
+ expect( isSelectionEnabled( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getBlockInsertionPoint', () => {
+ it( 'should return the explicitly assigned insertion point', () => {
+ const state = {
+ currentPost: {},
+ preferences: { mode: 'visual' },
+ blockSelection: {
+ start: 'clientId2',
+ end: 'clientId2',
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [ 'clientId2' ],
+ clientId2: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ insertionPoint: {
+ rootClientId: undefined,
+ index: 0,
+ },
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 0,
+ } );
+ } );
+
+ it( 'should return an object for the selected block', () => {
+ const state = {
+ currentPost: {},
+ preferences: { mode: 'visual' },
+ blockSelection: {
+ start: 'clientId1',
+ end: 'clientId1',
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ },
+ attributes: {
+ clientId1: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 1,
+ } );
+ } );
+
+ it( 'should return an object for the nested selected block', () => {
+ const state = {
+ currentPost: {},
+ preferences: { mode: 'visual' },
+ blockSelection: {
+ start: 'clientId2',
+ end: 'clientId2',
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [ 'clientId2' ],
+ clientId2: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: 'clientId1',
+ index: 1,
+ } );
+ } );
+
+ it( 'should return an object for the last multi selected clientId', () => {
+ const state = {
+ currentPost: {},
+ preferences: { mode: 'visual' },
+ blockSelection: {
+ start: 'clientId1',
+ end: 'clientId2',
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1', 'clientId2' ],
+ clientId1: [],
+ clientId2: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 2,
+ } );
+ } );
+
+ it( 'should return an object for the last block if no selection', () => {
+ const state = {
+ currentPost: {},
+ preferences: { mode: 'visual' },
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1', 'clientId2' ],
+ clientId1: [],
+ clientId2: [],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 2,
+ } );
+ } );
+ } );
+
+ describe( 'isBlockInsertionPointVisible', () => {
+ it( 'should return false if no assigned insertion point', () => {
+ const state = {
+ insertionPoint: null,
+ };
+
+ expect( isBlockInsertionPointVisible( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if assigned insertion point', () => {
+ const state = {
+ insertionPoint: {
+ rootClientId: undefined,
+ index: 5,
+ },
+ };
+
+ expect( isBlockInsertionPointVisible( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'canInsertBlockType', () => {
+ it( 'should deny blocks that are not registered', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/invalid' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that are not allowed by the editor', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {
+ allowedBlockTypes: [],
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
+ } );
+
+ it( 'should allow blocks that are allowed by the editor', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {
+ allowedBlockTypes: [ 'core/test-block-a' ],
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( true );
+ } );
+
+ it( 'should deny blocks when the editor has a template lock', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {
+ templateLock: 'all',
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that restrict parent from being inserted into the root', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that restrict parent from being inserted into a restricted parent', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( false );
+ } );
+
+ it( 'should allow blocks that restrict parent to be inserted into an allowed parent', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
+ } );
+
+ it( 'should deny restricted blocks from being inserted into a block that restricts allowedBlocks', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [ 'core/test-block-c' ],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( false );
+ } );
+
+ it( 'should allow allowed blocks to be inserted into a block that restricts allowedBlocks', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [ 'core/test-block-b' ],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( true );
+ } );
+
+ it( 'should prioritise parent over allowedBlocks', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'getInserterItems', () => {
+ it( 'should properly list block type and reusable block items', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ ],
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockAItem = items.find( ( item ) => item.id === 'core/test-block-a' );
+ expect( testBlockAItem ).toEqual( {
+ id: 'core/test-block-a',
+ name: 'core/test-block-a',
+ initialAttributes: {},
+ title: 'Test Block A',
+ icon: {
+ src: 'test',
+ },
+ category: 'formatting',
+ keywords: [ 'testing' ],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ hasChildBlocksWithInserterSupport: false,
+ } );
+ const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' );
+ expect( reusableBlockItem ).toEqual( {
+ id: 'core/block/1',
+ name: 'core/block',
+ initialAttributes: { ref: 1 },
+ title: 'Reusable Block 1',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+
+ it( 'should not list a reusable block item if it is being inserted inside it self', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1ref: {
+ name: 'core/block',
+ clientId: 'block1ref',
+ },
+ itselfBlock1: { name: 'core/test-block-a' },
+ itselfBlock2: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1ref: {
+ attributes: {
+ ref: 1,
+ },
+ },
+ itselfBlock1: {},
+ itselfBlock2: {},
+ },
+ order: {
+ '': [ 'block1ref' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'itselfBlock1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'itselfBlock2', title: 'Reusable Block 2' },
+ ],
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state, 'itselfBlock1' );
+ const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
+ expect( reusableBlockItems ).toHaveLength( 1 );
+ expect( reusableBlockItems[ 0 ] ).toEqual( {
+ id: 'core/block/2',
+ name: 'core/block',
+ initialAttributes: { ref: 2 },
+ title: 'Reusable Block 2',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+
+ it( 'should not list a reusable block item if it is being inserted inside a descendent', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block2ref: {
+ name: 'core/block',
+ clientId: 'block1ref',
+ },
+ referredBlock1: { name: 'core/test-block-a' },
+ referredBlock2: { name: 'core/test-block-b' },
+ childReferredBlock2: { name: 'core/test-block-a' },
+ grandchildReferredBlock2: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block2ref: {
+ attributes: {
+ ref: 2,
+ },
+ },
+ referredBlock1: {},
+ referredBlock2: {},
+ childReferredBlock2: {},
+ grandchildReferredBlock2: {},
+ },
+ order: {
+ '': [ 'block2ref' ],
+ referredBlock2: [ 'childReferredBlock2' ],
+ childReferredBlock2: [ 'grandchildReferredBlock2' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'referredBlock1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'referredBlock2', title: 'Reusable Block 2' },
+ ],
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state, 'grandchildReferredBlock2' );
+ const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
+ expect( reusableBlockItems ).toHaveLength( 1 );
+ expect( reusableBlockItems[ 0 ] ).toEqual( {
+ id: 'core/block/1',
+ name: 'core/block',
+ initialAttributes: { ref: 1 },
+ title: 'Reusable Block 1',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+ it( 'should order items by descending utility and frecency', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ block2: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ block2: {},
+ },
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'block2', title: 'Reusable Block 2' },
+ ],
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {
+ 'core/block/1': { count: 10, time: 1000 },
+ 'core/block/2': { count: 20, time: 1000 },
+ },
+ },
+ blockListSettings: {},
+ };
+ const itemIDs = getInserterItems( state ).map( ( item ) => item.id );
+ expect( itemIDs ).toEqual( [
+ 'core/block/2',
+ 'core/block/1',
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ ] );
+ } );
+
+ it( 'should correctly cache the return values', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ block2: { name: 'core/test-block-a' },
+ block3: { name: 'core/test-block-a' },
+ block4: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ block2: {},
+ block3: {},
+ block4: {},
+ },
+ order: {
+ '': [ 'block3', 'block4' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'block2', title: 'Reusable Block 2' },
+ ],
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+
+ const stateSecondBlockRestricted = {
+ ...state,
+ blockListSettings: {
+ block4: {
+ allowedBlocks: [ 'core/test-block-b' ],
+ },
+ },
+ };
+
+ const firstBlockFirstCall = getInserterItems( state, 'block3' );
+ const firstBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block3' );
+ expect( firstBlockFirstCall ).toBe( firstBlockSecondCall );
+ expect( firstBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ 'core/block/1',
+ 'core/block/2',
+ ] );
+
+ const secondBlockFirstCall = getInserterItems( state, 'block4' );
+ const secondBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block4' );
+ expect( secondBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ 'core/block/1',
+ 'core/block/2',
+ ] );
+ expect( secondBlockSecondCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ ] );
+ } );
+
+ it( 'should set isDisabled when a block with `multiple: false` has been used', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { clientId: 'block1', name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: { attribute: {} },
+ },
+ order: {
+ '': [ 'block1' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ reusableBlocks: {
+ data: {},
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( testBlockBItem.isDisabled ).toBe( true );
+ } );
+
+ it( 'should give common blocks a low utility', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ reusableBlocks: {
+ data: {},
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( testBlockBItem.utility ).toBe( INSERTER_UTILITY_LOW );
+ } );
+
+ it( 'should give used blocks a medium utility and set a frecency', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ reusableBlocks: {
+ data: {},
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {
+ 'core/test-block-b': { count: 10, time: 1000 },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const reusableBlock2Item = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( reusableBlock2Item.utility ).toBe( INSERTER_UTILITY_MEDIUM );
+ expect( reusableBlock2Item.frecency ).toBe( 2.5 );
+ } );
+
+ it( 'should give contextual blocks a high utility', () => {
+ const state = {
+ editor: {
+ present: {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: { attribute: {} },
+ },
+ order: {
+ '': [ 'block1' ],
+ },
+ },
+ edits: {},
+ },
+ },
+ initialEdits: {},
+ reusableBlocks: {
+ data: {},
+ },
+ currentPost: {},
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state, 'block1' );
+ const testBlockCItem = items.find( ( item ) => item.id === 'core/test-block-c' );
+ expect( testBlockCItem.utility ).toBe( INSERTER_UTILITY_HIGH );
+ } );
+ } );
+
+ describe( 'isValidTemplate', () => {
+ it( 'should return true if template is valid', () => {
+ const state = {
+ template: { isValid: true },
+ };
+
+ expect( isValidTemplate( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if template is not valid', () => {
+ const state = {
+ template: { isValid: false },
+ };
+
+ expect( isValidTemplate( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getTemplate', () => {
+ it( 'should return the template object', () => {
+ const template = [];
+ const state = {
+ settings: { template },
+ };
+
+ expect( getTemplate( state ) ).toBe( template );
+ } );
+ } );
+
+ describe( 'getTemplateLock', () => {
+ it( 'should return the general template lock if no clientId was set', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ };
+
+ expect( getTemplateLock( state ) ).toBe( 'all' );
+ } );
+
+ it( 'should return null if the specified clientId was not found ', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ templateLock: 'insert',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
+ } );
+
+ it( 'should return null if template lock was not set on the specified block', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ test: 'tes1t',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
+ } );
+
+ it( 'should return the template lock for the specified clientId', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ templateLock: 'insert',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'chicken' ) ).toBe( 'insert' );
+ } );
+ } );
+
+ describe( 'getBlockListSettings', () => {
+ it( 'should return the settings of a block', () => {
+ const state = {
+ blockListSettings: {
+ chicken: {
+ setting1: false,
+ },
+ ribs: {
+ setting2: true,
+ },
+ },
+ };
+
+ expect( getBlockListSettings( state, 'chicken' ) ).toEqual( {
+ setting1: false,
+ } );
+ } );
+
+ it( 'should return undefined if settings for the block don’t exist', () => {
+ const state = {
+ blockListSettings: {},
+ };
+
+ expect( getBlockListSettings( state, 'chicken' ) ).toBe( undefined );
+ } );
+ } );
+} );
diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js
index b4dacc0f41d5d..47d1c4d03c6fd 100644
--- a/packages/editor/src/store/actions.js
+++ b/packages/editor/src/store/actions.js
@@ -3,6 +3,11 @@
*/
import { castArray } from 'lodash';
+/**
+ * WordPress dependencies
+ */
+import { dispatch } from '@wordpress/data';
+
/**
* Returns an action object used in signalling that editor has initialized with
* the specified post object and editor settings.
@@ -326,7 +331,7 @@ export function updateEditorBlocks( blocks ) {
*/
const getBlockEditorAction = ( name ) => ( ...args ) => {
- window.wp.data.dispatch( 'core/block-editor' )[ name ]( ...args );
+ dispatch( 'core/block-editor' )[ name ]( ...args );
return { type: 'DO_NOTHING' };
};
diff --git a/packages/editor/src/store/array.js b/packages/editor/src/store/array.js
deleted file mode 100644
index 176d8936450af..0000000000000
--- a/packages/editor/src/store/array.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * External dependencies
- */
-import { castArray } from 'lodash';
-
-/**
- * Insert one or multiple elements into a given position of an array.
- *
- * @param {Array} array Source array.
- * @param {*} elements Elements to insert.
- * @param {number} index Insert Position.
- *
- * @return {Array} Result.
- */
-export function insertAt( array, elements, index ) {
- return [
- ...array.slice( 0, index ),
- ...castArray( elements ),
- ...array.slice( index ),
- ];
-}
-
-/**
- * Moves an element in an array.
- *
- * @param {Array} array Source array.
- * @param {number} from Source index.
- * @param {number} to Destination index.
- * @param {number} count Number of elements to move.
- *
- * @return {Array} Result.
- */
-export function moveTo( array, from, to, count = 1 ) {
- const withoutMovedElements = [ ...array ];
- withoutMovedElements.splice( from, count );
- return insertAt(
- withoutMovedElements,
- array.slice( from, from + count ),
- to,
- );
-}
diff --git a/packages/editor/src/store/effects/test/reusable-blocks.js b/packages/editor/src/store/effects/test/reusable-blocks.js
index 071e4f07f4f6f..8e2d2b263e2fa 100644
--- a/packages/editor/src/store/effects/test/reusable-blocks.js
+++ b/packages/editor/src/store/effects/test/reusable-blocks.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { noop, reduce } from 'lodash';
+import { noop } from 'lodash';
/**
* WordPress dependencies
@@ -12,6 +12,7 @@ import {
unregisterBlockType,
createBlock,
} from '@wordpress/blocks';
+import { dispatch as dataDispatch, select as dataSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -25,11 +26,8 @@ import {
convertBlockToReusable,
} from '../reusable-blocks';
import {
- resetBlocks,
- receiveBlocks,
__experimentalSaveReusableBlock as saveReusableBlock,
__experimentalDeleteReusableBlock as deleteReusableBlock,
- removeBlocks,
__experimentalConvertBlockToReusable as convertBlockToReusableAction,
__experimentalConvertBlockToStatic as convertBlockToStaticAction,
__experimentalReceiveReusableBlocks as receiveReusableBlocksAction,
@@ -50,6 +48,7 @@ describe( 'reusable blocks effects', () => {
name: { type: 'string' },
},
} );
+
registerBlockType( 'core/block', {
title: 'Reusable Block',
category: 'common',
@@ -58,11 +57,15 @@ describe( 'reusable blocks effects', () => {
ref: { type: 'string' },
},
} );
+
+ // jest.spyOn( dataDispatch( 'core/block-editor' ), 'createErrorNotice' );
} );
afterAll( () => {
unregisterBlockType( 'core/test-block' );
unregisterBlockType( 'core/block' );
+
+ // dataDispatch( 'core/block-editor' ).createErrorNotice.mockReset();
} );
describe( 'fetchReusableBlocks', () => {
@@ -252,10 +255,8 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( () => parsedBlock );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -267,6 +268,8 @@ describe( 'reusable blocks effects', () => {
id: 123,
updatedId: 456,
} );
+
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
it( 'should handle an API error', async () => {
@@ -286,10 +289,8 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( () => parsedBlock );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -299,6 +300,8 @@ describe( 'reusable blocks effects', () => {
type: 'SAVE_REUSABLE_BLOCK_FAILURE',
id: 123,
} );
+
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
@@ -310,9 +313,13 @@ describe( 'reusable blocks effects', () => {
},
] );
- expect( receiveReusableBlocks( action ) ).toEqual( receiveBlocks( [
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'receiveBlocks' ).mockImplementation( () => {} );
+ receiveReusableBlocks( action );
+ expect( dataDispatch( 'core/block-editor' ).receiveBlocks ).toHaveBeenCalledWith( [
{ clientId: 'broccoli' },
- ] ) );
+ ] );
+
+ dataDispatch( 'core/block-editor' ).receiveBlocks.mockReset();
} );
} );
@@ -335,11 +342,12 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ associatedBlock,
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -352,8 +360,8 @@ describe( 'reusable blocks effects', () => {
optimist: expect.any( Object ),
} );
- expect( dispatch ).toHaveBeenCalledWith(
- removeBlocks( [ associatedBlock.clientId, parsedBlock.clientId ] )
+ expect( dataDispatch( 'core/block-editor' ).removeBlocks ).toHaveBeenCalledWith(
+ [ associatedBlock.clientId, parsedBlock.clientId ]
);
expect( dispatch ).toHaveBeenCalledWith( {
@@ -361,6 +369,9 @@ describe( 'reusable blocks effects', () => {
id: 123,
optimist: expect.any( Object ),
} );
+
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
it( 'should handle an API error', async () => {
@@ -379,11 +390,11 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
-
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -395,16 +406,19 @@ describe( 'reusable blocks effects', () => {
id: 123,
optimist: expect.any( Object ),
} );
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
it( 'should not save reusable blocks with temporary IDs', async () => {
const reusableBlock = { id: 'reusable1', title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -412,6 +426,8 @@ describe( 'reusable blocks effects', () => {
await deleteReusableBlocks( deleteReusableBlock( 'reusable1' ), store );
expect( dispatch ).not.toHaveBeenCalled();
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
} );
@@ -421,28 +437,29 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( id ) =>
+ associatedBlock.clientId === id ? associatedBlock : parsedBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
convertBlockToStatic( convertBlockToStaticAction( associatedBlock.clientId ), store );
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ associatedBlock.clientId ],
- blocks: [
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ associatedBlock.clientId,
+ [
expect.objectContaining( {
name: 'core/test-block',
attributes: { name: 'Big Bird' },
} ),
- ],
- time: expect.any( Number ),
- } );
+ ]
+ );
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
it( 'should convert a reusable block with nested blocks into a static block', () => {
@@ -452,22 +469,20 @@ describe( 'reusable blocks effects', () => {
createBlock( 'core/test-block', { name: 'Oscar the Grouch' } ),
createBlock( 'core/test-block', { name: 'Cookie Monster' } ),
] );
-
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( id ) =>
+ associatedBlock.clientId === id ? associatedBlock : parsedBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
convertBlockToStatic( convertBlockToStaticAction( associatedBlock.clientId ), store );
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ associatedBlock.clientId ],
- blocks: [
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ associatedBlock.clientId,
+ [
expect.objectContaining( {
name: 'core/test-block',
attributes: { name: 'Big Bird' },
@@ -480,19 +495,25 @@ describe( 'reusable blocks effects', () => {
} ),
],
} ),
- ],
- time: expect.any( Number ),
- } );
+ ]
+ );
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
describe( 'convertBlockToReusable', () => {
it( 'should convert a static block into a reusable block', () => {
const staticBlock = createBlock( 'core/block', { ref: 123 } );
- const state = reducer( undefined, resetBlocks( [ staticBlock ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( ) =>
+ staticBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'receiveBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
- const store = { getState: () => state, dispatch };
+ const store = { getState: () => {}, dispatch };
convertBlockToReusable( convertBlockToReusableAction( staticBlock.clientId ), store );
@@ -511,21 +532,21 @@ describe( 'reusable blocks effects', () => {
saveReusableBlock( expect.stringMatching( /^reusable/ ) ),
);
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ staticBlock.clientId ],
- blocks: [
- expect.objectContaining( {
- name: 'core/block',
- attributes: { ref: expect.stringMatching( /^reusable/ ) },
- } ),
- ],
- time: expect.any( Number ),
- } );
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ [ staticBlock.clientId ],
+ expect.objectContaining( {
+ name: 'core/block',
+ attributes: { ref: expect.stringMatching( /^reusable/ ) },
+ } ),
+ );
- expect( dispatch ).toHaveBeenCalledWith(
- receiveBlocks( [ staticBlock ] ),
+ expect( dataDispatch( 'core/block-editor' ).receiveBlocks ).toHaveBeenCalledWith(
+ [ staticBlock ]
);
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataDispatch( 'core/block-editor' ).receiveBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
} );
diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js
index 384b824e7ecd8..8de29078fd312 100644
--- a/packages/editor/src/store/reducer.js
+++ b/packages/editor/src/store/reducer.js
@@ -7,9 +7,7 @@ import {
reduce,
omit,
mapValues,
- keys,
isEqual,
- overSome,
get,
} from 'lodash';
@@ -62,75 +60,6 @@ function getMutateSafeObject( original, working ) {
return working;
}
-/**
- * Returns true if the two object arguments have the same keys, or false
- * otherwise.
- *
- * @param {Object} a First object.
- * @param {Object} b Second object.
- *
- * @return {boolean} Whether the two objects have the same keys.
- */
-export function hasSameKeys( a, b ) {
- return isEqual( keys( a ), keys( b ) );
-}
-
-/**
- * Returns true if, given the currently dispatching action and the previously
- * dispatched action, the two actions are updating the same block attribute, or
- * false otherwise.
- *
- * @param {Object} action Currently dispatching action.
- * @param {Object} previousAction Previously dispatched action.
- *
- * @return {boolean} Whether actions are updating the same block attribute.
- */
-export function isUpdatingSameBlockAttribute( action, previousAction ) {
- return (
- action.type === 'UPDATE_BLOCK_ATTRIBUTES' &&
- action.clientId === previousAction.clientId &&
- hasSameKeys( action.attributes, previousAction.attributes )
- );
-}
-
-/**
- * Returns true if, given the currently dispatching action and the previously
- * dispatched action, the two actions are editing the same post property, or
- * false otherwise.
- *
- * @param {Object} action Currently dispatching action.
- * @param {Object} previousAction Previously dispatched action.
- *
- * @return {boolean} Whether actions are updating the same post property.
- */
-export function isUpdatingSamePostProperty( action, previousAction ) {
- return (
- action.type === 'EDIT_POST' &&
- hasSameKeys( action.edits, previousAction.edits )
- );
-}
-
-/**
- * Returns true if, given the currently dispatching action and the previously
- * dispatched action, the two actions are modifying the same property such that
- * undo history should be batched.
- *
- * @param {Object} action Currently dispatching action.
- * @param {Object} previousAction Previously dispatched action.
- *
- * @return {boolean} Whether to overwrite present state.
- */
-export function shouldOverwriteState( action, previousAction ) {
- if ( ! previousAction || action.type !== previousAction.type ) {
- return false;
- }
-
- return overSome( [
- isUpdatingSameBlockAttribute,
- isUpdatingSamePostProperty,
- ] )( action, previousAction );
-}
-
/**
* Undoable reducer returning the editor post state, including blocks parsed
* from current HTML markup.
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index 34195de66ed38..39b5c5f5bdec3 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -21,6 +21,7 @@ import {
import { isInTheFuture, getDate } from '@wordpress/date';
import { removep } from '@wordpress/autop';
import { addQueryArgs } from '@wordpress/url';
+import { select } from '@wordpress/data';
/**
* Internal dependencies
@@ -1063,7 +1064,7 @@ export function isEditorReady( state ) {
*/
const getBlockEditorSelector = ( name ) => ( state, ...args ) => {
- return window.wp.data.select( 'core/block-editor' )[ name ]( ...args );
+ return select( 'core/block-editor' )[ name ]( ...args );
};
export const getBlockDependantsCacheBust = getBlockEditorSelector( 'getBlockDependantsCacheBust' );
diff --git a/packages/editor/src/store/test/actions.js b/packages/editor/src/store/test/actions.js
index b071dc78204fb..4875576181b10 100644
--- a/packages/editor/src/store/test/actions.js
+++ b/packages/editor/src/store/test/actions.js
@@ -2,42 +2,16 @@
* Internal dependencies
*/
import {
- replaceBlocks,
- startTyping,
- stopTyping,
- enterFormattedText,
- exitFormattedText,
__experimentalFetchReusableBlocks as fetchReusableBlocks,
__experimentalSaveReusableBlock as saveReusableBlock,
__experimentalDeleteReusableBlock as deleteReusableBlock,
__experimentalConvertBlockToStatic as convertBlockToStatic,
__experimentalConvertBlockToReusable as convertBlockToReusable,
- toggleSelection,
setupEditor,
resetPost,
- resetBlocks,
- updateBlockAttributes,
- updateBlock,
- selectBlock,
- startMultiSelect,
- stopMultiSelect,
- multiSelect,
- clearSelectedBlock,
- replaceBlock,
- insertBlock,
- insertBlocks,
- showInsertionPoint,
- hideInsertionPoint,
editPost,
savePost,
trashPost,
- mergeBlocks,
- redo,
- undo,
- removeBlocks,
- removeBlock,
- toggleBlockMode,
- updateBlockListSettings,
} from '../actions';
describe( 'actions', () => {
@@ -62,169 +36,6 @@ describe( 'actions', () => {
} );
} );
} );
- describe( 'resetBlocks', () => {
- it( 'should return the RESET_BLOCKS actions', () => {
- const blocks = [];
- const result = resetBlocks( blocks );
- expect( result ).toEqual( {
- type: 'RESET_BLOCKS',
- blocks,
- } );
- } );
- } );
-
- describe( 'updateBlockAttributes', () => {
- it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => {
- const clientId = 'myclientid';
- const attributes = {};
- const result = updateBlockAttributes( clientId, attributes );
- expect( result ).toEqual( {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId,
- attributes,
- } );
- } );
- } );
-
- describe( 'updateBlock', () => {
- it( 'should return the UPDATE_BLOCK action', () => {
- const clientId = 'myclientid';
- const updates = {};
- const result = updateBlock( clientId, updates );
- expect( result ).toEqual( {
- type: 'UPDATE_BLOCK',
- clientId,
- updates,
- } );
- } );
- } );
-
- describe( 'selectBlock', () => {
- it( 'should return the SELECT_BLOCK action', () => {
- const clientId = 'myclientid';
- const result = selectBlock( clientId, -1 );
- expect( result ).toEqual( {
- type: 'SELECT_BLOCK',
- initialPosition: -1,
- clientId,
- } );
- } );
- } );
-
- describe( 'startMultiSelect', () => {
- it( 'should return the START_MULTI_SELECT', () => {
- expect( startMultiSelect() ).toEqual( {
- type: 'START_MULTI_SELECT',
- } );
- } );
- } );
-
- describe( 'stopMultiSelect', () => {
- it( 'should return the Stop_MULTI_SELECT', () => {
- expect( stopMultiSelect() ).toEqual( {
- type: 'STOP_MULTI_SELECT',
- } );
- } );
- } );
- describe( 'multiSelect', () => {
- it( 'should return MULTI_SELECT action', () => {
- const start = 'start';
- const end = 'end';
- expect( multiSelect( start, end ) ).toEqual( {
- type: 'MULTI_SELECT',
- start,
- end,
- } );
- } );
- } );
-
- describe( 'clearSelectedBlock', () => {
- it( 'should return CLEAR_SELECTED_BLOCK action', () => {
- expect( clearSelectedBlock() ).toEqual( {
- type: 'CLEAR_SELECTED_BLOCK',
- } );
- } );
- } );
-
- describe( 'replaceBlock', () => {
- it( 'should return the REPLACE_BLOCKS action', () => {
- const block = {
- clientId: 'ribs',
- };
-
- expect( replaceBlock( [ 'chicken' ], block ) ).toEqual( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ block ],
- time: expect.any( Number ),
- } );
- } );
- } );
-
- describe( 'replaceBlocks', () => {
- it( 'should return the REPLACE_BLOCKS action', () => {
- const blocks = [ {
- clientId: 'ribs',
- } ];
-
- expect( replaceBlocks( [ 'chicken' ], blocks ) ).toEqual( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks,
- time: expect.any( Number ),
- } );
- } );
- } );
-
- describe( 'insertBlock', () => {
- it( 'should return the INSERT_BLOCKS action', () => {
- const block = {
- clientId: 'ribs',
- };
- const index = 5;
- expect( insertBlock( block, index, 'testclientid' ) ).toEqual( {
- type: 'INSERT_BLOCKS',
- blocks: [ block ],
- index,
- rootClientId: 'testclientid',
- time: expect.any( Number ),
- updateSelection: true,
- } );
- } );
- } );
-
- describe( 'insertBlocks', () => {
- it( 'should return the INSERT_BLOCKS action', () => {
- const blocks = [ {
- clientId: 'ribs',
- } ];
- const index = 3;
- expect( insertBlocks( blocks, index, 'testclientid' ) ).toEqual( {
- type: 'INSERT_BLOCKS',
- blocks,
- index,
- rootClientId: 'testclientid',
- time: expect.any( Number ),
- updateSelection: true,
- } );
- } );
- } );
-
- describe( 'showInsertionPoint', () => {
- it( 'should return the SHOW_INSERTION_POINT action', () => {
- expect( showInsertionPoint() ).toEqual( {
- type: 'SHOW_INSERTION_POINT',
- } );
- } );
- } );
-
- describe( 'hideInsertionPoint', () => {
- it( 'should return the HIDE_INSERTION_POINT action', () => {
- expect( hideInsertionPoint() ).toEqual( {
- type: 'HIDE_INSERTION_POINT',
- } );
- } );
- } );
describe( 'editPost', () => {
it( 'should return EDIT_POST action', () => {
@@ -264,106 +75,6 @@ describe( 'actions', () => {
} );
} );
- describe( 'mergeBlocks', () => {
- it( 'should return MERGE_BLOCKS action', () => {
- const firstBlockClientId = 'blockA';
- const secondBlockClientId = 'blockB';
- expect( mergeBlocks( firstBlockClientId, secondBlockClientId ) ).toEqual( {
- type: 'MERGE_BLOCKS',
- blocks: [ firstBlockClientId, secondBlockClientId ],
- } );
- } );
- } );
-
- describe( 'redo', () => {
- it( 'should return REDO action', () => {
- expect( redo() ).toEqual( {
- type: 'REDO',
- } );
- } );
- } );
-
- describe( 'undo', () => {
- it( 'should return UNDO action', () => {
- expect( undo() ).toEqual( {
- type: 'UNDO',
- } );
- } );
- } );
-
- describe( 'removeBlocks', () => {
- it( 'should return REMOVE_BLOCKS action', () => {
- const clientIds = [ 'clientId' ];
- expect( removeBlocks( clientIds ) ).toEqual( {
- type: 'REMOVE_BLOCKS',
- clientIds,
- selectPrevious: true,
- } );
- } );
- } );
-
- describe( 'removeBlock', () => {
- it( 'should return REMOVE_BLOCKS action', () => {
- const clientId = 'myclientid';
- expect( removeBlock( clientId ) ).toEqual( {
- type: 'REMOVE_BLOCKS',
- clientIds: [
- clientId,
- ],
- selectPrevious: true,
- } );
- expect( removeBlock( clientId, false ) ).toEqual( {
- type: 'REMOVE_BLOCKS',
- clientIds: [
- clientId,
- ],
- selectPrevious: false,
- } );
- } );
- } );
-
- describe( 'toggleBlockMode', () => {
- it( 'should return TOGGLE_BLOCK_MODE action', () => {
- const clientId = 'myclientid';
- expect( toggleBlockMode( clientId ) ).toEqual( {
- type: 'TOGGLE_BLOCK_MODE',
- clientId,
- } );
- } );
- } );
-
- describe( 'startTyping', () => {
- it( 'should return the START_TYPING action', () => {
- expect( startTyping() ).toEqual( {
- type: 'START_TYPING',
- } );
- } );
- } );
-
- describe( 'stopTyping', () => {
- it( 'should return the STOP_TYPING action', () => {
- expect( stopTyping() ).toEqual( {
- type: 'STOP_TYPING',
- } );
- } );
- } );
-
- describe( 'enterFormattedText', () => {
- it( 'should return the ENTER_FORMATTED_TEXT action', () => {
- expect( enterFormattedText() ).toEqual( {
- type: 'ENTER_FORMATTED_TEXT',
- } );
- } );
- } );
-
- describe( 'exitFormattedText', () => {
- it( 'should return the EXIT_FORMATTED_TEXT action', () => {
- expect( exitFormattedText() ).toEqual( {
- type: 'EXIT_FORMATTED_TEXT',
- } );
- } );
- } );
-
describe( 'fetchReusableBlocks', () => {
it( 'should return the FETCH_REUSABLE_BLOCKS action', () => {
expect( fetchReusableBlocks() ).toEqual( {
@@ -416,45 +127,4 @@ describe( 'actions', () => {
} );
} );
} );
-
- describe( 'toggleSelection', () => {
- it( 'should return the TOGGLE_SELECTION action with default value for isSelectionEnabled = true', () => {
- expect( toggleSelection() ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: true,
- } );
- } );
-
- it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = true as passed in the argument', () => {
- expect( toggleSelection( true ) ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: true,
- } );
- } );
-
- it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = false as passed in the argument', () => {
- expect( toggleSelection( false ) ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: false,
- } );
- } );
- } );
-
- describe( 'updateBlockListSettings', () => {
- it( 'should return the UPDATE_BLOCK_LIST_SETTINGS with undefined settings', () => {
- expect( updateBlockListSettings( 'chicken' ) ).toEqual( {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: 'chicken',
- settings: undefined,
- } );
- } );
-
- it( 'should return the UPDATE_BLOCK_LIST_SETTINGS action with the passed settings', () => {
- expect( updateBlockListSettings( 'chicken', { chicken: 'ribs' } ) ).toEqual( {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: 'chicken',
- settings: { chicken: 'ribs' },
- } );
- } );
- } );
} );
diff --git a/packages/editor/src/store/test/effects.js b/packages/editor/src/store/test/effects.js
index a6ce470d86e54..8ee0cde5242e4 100644
--- a/packages/editor/src/store/test/effects.js
+++ b/packages/editor/src/store/test/effects.js
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { noop } from 'lodash';
-
/**
* WordPress dependencies
*/
@@ -10,27 +5,15 @@ import {
getBlockTypes,
unregisterBlockType,
registerBlockType,
- createBlock,
} from '@wordpress/blocks';
-import { dispatch as dataDispatch, createRegistry } from '@wordpress/data';
+import { dispatch as dataDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
-import actions, {
- updateEditorSettings,
- setupEditorState,
- mergeBlocks,
- replaceBlocks,
- resetBlocks,
- selectBlock,
- setTemplateValidity,
-} from '../actions';
-import effects, { validateBlocksToTemplate } from '../effects';
+import { setupEditorState, updateEditorBlocks } from '../actions';
+import effects from '../effects';
import { SAVE_POST_NOTICE_ID } from '../effects/posts';
-import * as selectors from '../selectors';
-import reducer from '../reducer';
-import applyMiddlewares from '../middlewares';
import '../../';
describe( 'effects', () => {
@@ -46,178 +29,6 @@ describe( 'effects', () => {
const defaultBlockSettings = { save: () => 'Saved', category: 'common', title: 'block title' };
- describe( '.MERGE_BLOCKS', () => {
- const handler = effects.MERGE_BLOCKS;
- const defaultGetBlock = selectors.getBlock;
-
- afterEach( () => {
- getBlockTypes().forEach( ( block ) => {
- unregisterBlockType( block.name );
- } );
- selectors.getBlock = defaultGetBlock;
- } );
-
- it( 'should only focus the blockA if the blockA has no merge function', () => {
- registerBlockType( 'core/test-block', defaultBlockSettings );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block',
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
-
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 1 );
- expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken' ) );
- } );
-
- it( 'should merge the blocks if blocks of the same type', () => {
- registerBlockType( 'core/test-block', {
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: { content: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 2 );
- expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken', -1 ) );
- expect( dispatch ).toHaveBeenCalledWith( {
- ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken ribs' },
- } ] ),
- time: expect.any( Number ),
- } );
- } );
-
- it( 'should not merge the blocks have different types without transformation', () => {
- registerBlockType( 'core/test-block', {
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- registerBlockType( 'core/test-block-2', defaultBlockSettings );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block2',
- attributes: { content: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).not.toHaveBeenCalled();
- } );
-
- it( 'should transform and merge the blocks', () => {
- registerBlockType( 'core/test-block', {
- attributes: {
- content: {
- type: 'string',
- },
- },
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- registerBlockType( 'core/test-block-2', {
- attributes: {
- content: {
- type: 'string',
- },
- },
- transforms: {
- to: [ {
- type: 'block',
- blocks: [ 'core/test-block' ],
- transform: ( { content2 } ) => {
- return createBlock( 'core/test-block', {
- content: content2,
- } );
- },
- } ],
- },
- save: noop,
- category: 'common',
- title: 'test block 2',
- } );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block-2',
- attributes: { content2: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 2 );
- // expect( dispatch ).toHaveBeenCalledWith( focusBlock( 'chicken', { offset: -1 } ) );
- expect( dispatch ).toHaveBeenCalledWith( {
- ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken ribs' },
- } ] ),
- time: expect.any( Number ),
- } );
- } );
- } );
-
describe( '.REQUEST_POST_UPDATE_SUCCESS', () => {
const handler = effects.REQUEST_POST_UPDATE_SUCCESS;
@@ -423,19 +234,11 @@ describe( 'effects', () => {
},
status: 'draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post, settings: {} }, { getState } );
+ const result = handler( { post, settings: {} } );
expect( result ).toEqual( [
+ updateEditorBlocks( [] ),
setupEditorState( post, [], {} ),
] );
} );
@@ -452,22 +255,11 @@ describe( 'effects', () => {
},
status: 'draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post }, { getState } );
+ const result = handler( { post } );
expect( result[ 0 ].blocks ).toHaveLength( 1 );
- expect( result ).toEqual( [
- setupEditorState( post, result[ 0 ].blocks, {} ),
- ] );
+ expect( result[ 1 ] ).toEqual( setupEditorState( post, result[ 0 ].blocks, {} ) );
} );
it( 'should return post setup action only if auto-draft', () => {
@@ -481,93 +273,13 @@ describe( 'effects', () => {
},
status: 'auto-draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post }, { getState } );
+ const result = handler( { post } );
expect( result ).toEqual( [
+ updateEditorBlocks( [] ),
setupEditorState( post, [], { title: 'A History of Pork' } ),
] );
} );
} );
-
- describe( 'validateBlocksToTemplate', () => {
- let store;
- beforeEach( () => {
- store = createRegistry().registerStore( 'test', {
- actions,
- selectors,
- reducer,
- } );
- applyMiddlewares( store );
-
- registerBlockType( 'core/test-block', defaultBlockSettings );
- } );
-
- afterEach( () => {
- getBlockTypes().forEach( ( block ) => {
- unregisterBlockType( block.name );
- } );
- } );
-
- it( 'should return undefined if no template assigned', () => {
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return undefined if invalid but unlocked', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/foo', {} ],
- ],
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return undefined if locked and valid', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/test-block' ],
- ],
- templateLock: 'all',
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return validity set action if invalid on default state', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/foo' ],
- ],
- templateLock: 'all',
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toEqual( setTemplateValidity( false ) );
- } );
- } );
} );
diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js
index 2d11a0e319a2d..5f84a7af97497 100644
--- a/packages/editor/src/store/test/reducer.js
+++ b/packages/editor/src/store/test/reducer.js
@@ -1,986 +1,41 @@
/**
* External dependencies
*/
-import { values, noop } from 'lodash';
import deepFreeze from 'deep-freeze';
-/**
- * WordPress dependencies
- */
-import {
- registerBlockType,
- unregisterBlockType,
- createBlock,
-} from '@wordpress/blocks';
-
/**
* Internal dependencies
*/
import {
- hasSameKeys,
- isUpdatingSameBlockAttribute,
- isUpdatingSamePostProperty,
- shouldOverwriteState,
getPostRawValue,
- editor,
initialEdits,
+ editor,
currentPost,
- isTyping,
- isCaretWithinFormattedText,
- blockSelection,
preferences,
saving,
- blocksMode,
- insertionPoint,
reusableBlocks,
- template,
- blockListSettings,
autosave,
- postSavingLock,
- previewLink,
-} from '../reducer';
-import { INITIAL_EDITS_DEFAULTS } from '../defaults';
-
-describe( 'state', () => {
- describe( 'hasSameKeys()', () => {
- it( 'returns false if two objects do not have the same keys', () => {
- const a = { foo: 10 };
- const b = { bar: 10 };
-
- expect( hasSameKeys( a, b ) ).toBe( false );
- } );
-
- it( 'returns false if two objects have the same keys', () => {
- const a = { foo: 10 };
- const b = { foo: 20 };
-
- expect( hasSameKeys( a, b ) ).toBe( true );
- } );
- } );
-
- describe( 'isUpdatingSameBlockAttribute()', () => {
- it( 'should return false if not updating block attributes', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {},
- };
- const previousAction = {
- type: 'EDIT_POST',
- edits: {},
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if not updating the same block', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- attributes: {
- foo: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if not updating the same block attributes', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- bar: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return true if updating the same block attributes', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( true );
- } );
- } );
-
- describe( 'isUpdatingSamePostProperty()', () => {
- it( 'should return false if not editing post', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- attributes: {
- foo: 10,
- },
- };
-
- expect( isUpdatingSamePostProperty( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if not editing the same post properties', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'EDIT_POST',
- edits: {
- bar: 20,
- },
- };
-
- expect( isUpdatingSamePostProperty( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return true if updating the same post properties', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'EDIT_POST',
- edits: {
- foo: 20,
- },
- };
-
- expect( isUpdatingSamePostProperty( action, previousAction ) ).toBe( true );
- } );
- } );
-
- describe( 'shouldOverwriteState()', () => {
- it( 'should return false if no previous action', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {
- foo: 10,
- },
- };
- const previousAction = undefined;
-
- expect( shouldOverwriteState( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if the action types are different', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'EDIT_DIFFERENT_POST',
- edits: {
- foo: 20,
- },
- };
-
- expect( shouldOverwriteState( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return true if updating same block attribute', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 20,
- },
- };
-
- expect( shouldOverwriteState( action, previousAction ) ).toBe( true );
- } );
-
- it( 'should return true if updating same post property', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'EDIT_POST',
- edits: {
- foo: 20,
- },
- };
-
- expect( shouldOverwriteState( action, previousAction ) ).toBe( true );
- } );
- } );
-
- describe( 'getPostRawValue', () => {
- it( 'returns original value for non-rendered content', () => {
- const value = getPostRawValue( '' );
-
- expect( value ).toBe( '' );
- } );
-
- it( 'returns raw value for rendered content', () => {
- const value = getPostRawValue( { raw: '' } );
-
- expect( value ).toBe( '' );
- } );
- } );
-
- describe( 'editor()', () => {
- beforeAll( () => {
- registerBlockType( 'core/test-block', {
- save: noop,
- edit: noop,
- category: 'common',
- title: 'test block',
- } );
- } );
-
- afterAll( () => {
- unregisterBlockType( 'core/test-block' );
- } );
-
- it( 'should return history (empty edits, blocks) by default', () => {
- const state = editor( undefined, {} );
-
- expect( state.past ).toEqual( [] );
- expect( state.future ).toEqual( [] );
- expect( state.present.edits ).toEqual( {} );
- expect( state.present.blocks.byClientId ).toEqual( {} );
- expect( state.present.blocks.order ).toEqual( {} );
- expect( state.present.blocks.isDirty ).toBe( false );
- } );
-
- it( 'should key by reset blocks clientId', () => {
- const original = editor( undefined, {} );
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [ { clientId: 'bananas', innerBlocks: [] } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'bananas' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'bananas' ],
- bananas: [],
- } );
- } );
-
- it( 'should key by reset blocks clientId, including inner blocks', () => {
- const original = editor( undefined, {} );
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'bananas',
- innerBlocks: [ { clientId: 'apples', innerBlocks: [] } ],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'bananas' ],
- apples: [],
- bananas: [ 'apples' ],
- } );
- } );
-
- it( 'should insert block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
- expect( values( state.present.blocks.byClientId )[ 1 ].clientId ).toBe( 'ribs' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'chicken', 'ribs' ],
- chicken: [],
- ribs: [],
- } );
- } );
-
- it( 'should replace the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( state.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
- expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'wings' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'wings' ],
- wings: [],
- } );
- } );
-
- it( 'should replace the nested block', () => {
- const nestedBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
- const replacementBlock = createBlock( 'core/test-block' );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
-
- const state = editor( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ nestedBlock.clientId ],
- blocks: [ replacementBlock ],
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ replacementBlock.clientId ],
- [ replacementBlock.clientId ]: [],
- } );
- } );
-
- it( 'should replace the block even if the new block clientId is the same', () => {
- const originalState = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const replacedState = editor( originalState, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'chicken',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( replacedState.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( originalState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/test-block' );
- expect( values( replacedState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
- expect( values( replacedState.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'chicken' );
- expect( replacedState.present.blocks.order ).toEqual( {
- '': [ 'chicken' ],
- chicken: [],
- } );
-
- const nestedBlock = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- };
- const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
- const replacementNestedBlock = {
- clientId: 'chicken',
- name: 'core/freeform',
- attributes: {},
- innerBlocks: [],
- };
-
- const originalNestedState = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
-
- const replacedNestedState = editor( originalNestedState, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ nestedBlock.clientId ],
- blocks: [ replacementNestedBlock ],
- } );
-
- expect( replacedNestedState.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ replacementNestedBlock.clientId ],
- [ replacementNestedBlock.clientId ]: [],
- } );
-
- expect( originalNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/test-block' );
- expect( replacedNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/freeform' );
- } );
-
- it( 'should update the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- isValid: false,
- innerBlocks: [],
- } ],
- } );
- const state = editor( deepFreeze( original ), {
- type: 'UPDATE_BLOCK',
- clientId: 'chicken',
- updates: {
- attributes: { content: 'ribs' },
- isValid: true,
- },
- } );
-
- expect( state.present.blocks.byClientId.chicken ).toEqual( {
- clientId: 'chicken',
- name: 'core/test-block',
- isValid: true,
- } );
-
- expect( state.present.blocks.attributes.chicken ).toEqual( {
- content: 'ribs',
- } );
- } );
-
- it( 'should update the reusable block reference if the temporary id is swapped', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/block',
- attributes: {
- ref: 'random-clientId',
- },
- isValid: false,
- innerBlocks: [],
- } ],
- } );
-
- const state = editor( deepFreeze( original ), {
- type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
- id: 'random-clientId',
- updatedId: 3,
- } );
-
- expect( state.present.blocks.byClientId.chicken ).toEqual( {
- clientId: 'chicken',
- name: 'core/block',
- isValid: false,
- } );
-
- expect( state.present.blocks.attributes.chicken ).toEqual( {
- ref: 3,
- } );
- } );
-
- it( 'should move the block up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
- } );
-
- it( 'should move the nested block up', () => {
- const movedBlock = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ movedBlock.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ movedBlock.clientId, siblingBlock.clientId ],
- [ movedBlock.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should move multiple blocks up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs', 'veggies' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'veggies', 'chicken' ] );
- } );
-
- it( 'should move multiple nested blocks up', () => {
- const movedBlockA = createBlock( 'core/test-block' );
- const movedBlockB = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlockA, movedBlockB ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ movedBlockA.clientId, movedBlockB.clientId, siblingBlock.clientId ],
- [ movedBlockA.clientId ]: [],
- [ movedBlockB.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should not move the first block up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order ).toBe( original.present.blocks.order );
- } );
-
- it( 'should move the block down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
- } );
-
- it( 'should move the nested block down', () => {
- const movedBlock = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlock, siblingBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ movedBlock.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlock.clientId ],
- [ movedBlock.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should move multiple blocks down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'chicken', 'ribs' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'veggies', 'chicken', 'ribs' ] );
- } );
-
- it( 'should move multiple nested blocks down', () => {
- const movedBlockA = createBlock( 'core/test-block' );
- const movedBlockB = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlockA, movedBlockB, siblingBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlockA.clientId, movedBlockB.clientId ],
- [ movedBlockA.clientId ]: [],
- [ movedBlockB.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should not move the last block down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'ribs' ],
- } );
-
- expect( state.present.blocks.order ).toBe( original.present.blocks.order );
- } );
-
- it( 'should remove the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
- expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
- expect( state.present.blocks.byClientId ).toEqual( {
- ribs: {
- clientId: 'ribs',
- name: 'core/test-block',
- },
- } );
- expect( state.present.blocks.attributes ).toEqual( {
- ribs: {},
- } );
- } );
-
- it( 'should remove multiple blocks', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken', 'veggies' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
- expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
- expect( state.present.blocks.order ).not.toHaveProperty( 'veggies' );
- expect( state.present.blocks.byClientId ).toEqual( {
- ribs: {
- clientId: 'ribs',
- name: 'core/test-block',
- },
- } );
- expect( state.present.blocks.attributes ).toEqual( {
- ribs: {},
- } );
- } );
-
- it( 'should cascade remove to include inner blocks', () => {
- const block = createBlock( 'core/test-block', {}, [
- createBlock( 'core/test-block', {}, [
- createBlock( 'core/test-block' ),
- ] ),
- ] );
-
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ block ],
- } );
-
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ block.clientId ],
- } );
-
- expect( state.present.blocks.byClientId ).toEqual( {} );
- expect( state.present.blocks.order ).toEqual( {
- '': [],
- } );
- } );
-
- it( 'should insert at the specified index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'loquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- const state = editor( original, {
- type: 'INSERT_BLOCKS',
- index: 1,
- blocks: [ {
- clientId: 'persimmon',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 3 );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'kumquat', 'persimmon', 'loquat' ] );
- } );
-
- it( 'should move block to lower index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 0,
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken', 'veggies' ] );
- } );
+ postSavingLock,
+ previewLink,
+} from '../reducer';
+import { INITIAL_EDITS_DEFAULTS } from '../defaults';
- it( 'should move block to higher index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 2,
- } );
+describe( 'state', () => {
+ describe( 'getPostRawValue', () => {
+ it( 'returns original value for non-rendered content', () => {
+ const value = getPostRawValue( '' );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'veggies', 'ribs' ] );
+ expect( value ).toBe( '' );
} );
- it( 'should not move block if passed same index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 1,
- } );
+ it( 'returns raw value for rendered content', () => {
+ const value = getPostRawValue( { raw: '' } );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'ribs', 'veggies' ] );
+ expect( value ).toBe( '' );
} );
+ } );
+ describe( 'editor()', () => {
describe( 'edits()', () => {
it( 'should save newly edited properties', () => {
const original = editor( undefined, {
@@ -998,7 +53,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toEqual( {
+ expect( state.edits ).toEqual( {
status: 'draft',
title: 'post title',
tags: [ 1 ],
@@ -1021,7 +76,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toBe( original.present.edits );
+ expect( state.edits ).toBe( original.edits );
} );
it( 'should save modified properties', () => {
@@ -1042,7 +97,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toEqual( {
+ expect( state.edits ).toEqual( {
status: 'draft',
title: 'modified title',
tags: [ 2 ],
@@ -1068,7 +123,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toEqual( {
+ expect( state.edits ).toEqual( {
meta: {
a: 1,
b: 2,
@@ -1084,7 +139,7 @@ describe( 'state', () => {
edits: {},
} );
- expect( state.present.edits ).toBe( original.present.edits );
+ expect( state.edits ).toBe( original.edits );
} );
it( 'unset reset post values which match by canonical value', () => {
@@ -1104,7 +159,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toEqual( {} );
+ expect( state.edits ).toEqual( {} );
} );
it( 'unset reset post values by deep match', () => {
@@ -1130,7 +185,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toEqual( {} );
+ expect( state.edits ).toEqual( {} );
} );
it( 'should omit content when resetting', () => {
@@ -1145,7 +200,7 @@ describe( 'state', () => {
},
} );
- expect( state.present.edits ).toHaveProperty( 'content' );
+ expect( state.edits ).toHaveProperty( 'content' );
state = editor( original, {
type: 'RESET_BLOCKS',
@@ -1162,745 +217,130 @@ describe( 'state', () => {
} ],
} );
- expect( state.present.edits ).not.toHaveProperty( 'content' );
- } );
- } );
-
- describe( 'blocks', () => {
- it( 'should not reset any blocks that are not in the post', () => {
- const actions = [
- {
- type: 'RESET_BLOCKS',
- blocks: [
- {
- clientId: 'block1',
- innerBlocks: [
- { clientId: 'block11', innerBlocks: [] },
- { clientId: 'block12', innerBlocks: [] },
- ],
- },
- ],
- },
- {
- type: 'RECEIVE_BLOCKS',
- blocks: [
- {
- clientId: 'block2',
- innerBlocks: [
- { clientId: 'block21', innerBlocks: [] },
- { clientId: 'block22', innerBlocks: [] },
- ],
- },
- ],
- },
- ];
- const original = deepFreeze( actions.reduce( editor, undefined ) );
-
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [
- {
- clientId: 'block3',
- innerBlocks: [
- { clientId: 'block31', innerBlocks: [] },
- { clientId: 'block32', innerBlocks: [] },
- ],
- },
- ],
- } );
-
- expect( state.present.blocks.byClientId ).toEqual( {
- block2: { clientId: 'block2' },
- block21: { clientId: 'block21' },
- block22: { clientId: 'block22' },
- block3: { clientId: 'block3' },
- block31: { clientId: 'block31' },
- block32: { clientId: 'block32' },
- } );
- } );
-
- describe( 'byClientId', () => {
- it( 'should ignore updates to non-existent block', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.byClientId ).toBe( original.present.blocks.byClientId );
- } );
-
- it( 'should return with same reference if no changes in updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.byClientId ).toBe( state.present.blocks.byClientId );
- } );
- } );
-
- describe( 'attributes', () => {
- it( 'should return with attribute block updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes.kumquat.updated ).toBe( true );
- } );
-
- it( 'should accumulate attribute block updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- moreUpdated: true,
- },
- } );
-
- expect( state.present.blocks.attributes.kumquat ).toEqual( {
- updated: true,
- moreUpdated: true,
- } );
- } );
-
- it( 'should ignore updates to non-existent block', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes ).toBe( original.present.blocks.attributes );
- } );
-
- it( 'should return with same reference if no changes in updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes ).toBe( state.present.blocks.attributes );
- } );
- } );
- } );
-
- describe( 'withHistory', () => {
- it( 'should overwrite present history if updating same attributes', () => {
- let state;
-
- state = editor( state, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- expect( state.past ).toHaveLength( 1 );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 1,
- },
- } );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 2,
- },
- } );
-
- expect( state.past ).toHaveLength( 2 );
- } );
-
- it( 'should not overwrite present history if updating different attributes', () => {
- let state;
-
- state = editor( state, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- expect( state.past ).toHaveLength( 1 );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 1,
- },
- } );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- other: 1,
- },
- } );
-
- expect( state.past ).toHaveLength( 3 );
- } );
- } );
- } );
-
- describe( 'initialEdits', () => {
- it( 'should default to initial edits', () => {
- const state = initialEdits( undefined, {} );
-
- expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
- } );
-
- it( 'should return initial edits on post reset', () => {
- const state = initialEdits( undefined, {
- type: 'RESET_POST',
- } );
-
- expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
- } );
-
- it( 'should return referentially equal state if setup includes no edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return referentially equal state if reset while having made no edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'RESET_POST',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return setup edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- content: '',
- },
- } );
-
- expect( state ).toEqual( {
- title: '',
- content: '',
- } );
- } );
-
- it( 'should unset content on editor setup', () => {
- const original = initialEdits( undefined, {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- content: '',
- },
- } );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR_STATE',
- } );
-
- expect( state ).toEqual( { title: '' } );
- } );
-
- it( 'should unset values on post update', () => {
- const original = initialEdits( undefined, {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- },
- } );
- const state = initialEdits( deepFreeze( original ), {
- type: 'UPDATE_POST',
- edits: {
- title: '',
- },
- } );
-
- expect( state ).toEqual( {} );
- } );
- } );
-
- describe( 'currentPost()', () => {
- it( 'should reset a post object', () => {
- const original = deepFreeze( { title: 'unmodified' } );
-
- const state = currentPost( original, {
- type: 'RESET_POST',
- post: {
- title: 'new post',
- },
- } );
-
- expect( state ).toEqual( {
- title: 'new post',
- } );
- } );
-
- it( 'should update the post object with UPDATE_POST', () => {
- const original = deepFreeze( { title: 'unmodified', status: 'publish' } );
-
- const state = currentPost( original, {
- type: 'UPDATE_POST',
- edits: {
- title: 'updated post object from server',
- },
- } );
-
- expect( state ).toEqual( {
- title: 'updated post object from server',
- status: 'publish',
- } );
- } );
- } );
-
- describe( 'insertionPoint', () => {
- it( 'should default to null', () => {
- const state = insertionPoint( undefined, {} );
-
- expect( state ).toBe( null );
- } );
-
- it( 'should set insertion point', () => {
- const state = insertionPoint( null, {
- type: 'SHOW_INSERTION_POINT',
- rootClientId: 'clientId1',
- index: 0,
- } );
-
- expect( state ).toEqual( {
- rootClientId: 'clientId1',
- index: 0,
- } );
- } );
-
- it( 'should clear the insertion point', () => {
- const original = deepFreeze( {
- rootClientId: 'clientId1',
- index: 0,
- } );
- const state = insertionPoint( original, {
- type: 'HIDE_INSERTION_POINT',
- } );
-
- expect( state ).toBe( null );
- } );
- } );
-
- describe( 'isTyping()', () => {
- it( 'should set the typing flag to true', () => {
- const state = isTyping( false, {
- type: 'START_TYPING',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should set the typing flag to false', () => {
- const state = isTyping( false, {
- type: 'STOP_TYPING',
- } );
-
- expect( state ).toBe( false );
- } );
- } );
-
- describe( 'isCaretWithinFormattedText()', () => {
- it( 'should set the flag to true', () => {
- const state = isCaretWithinFormattedText( false, {
- type: 'ENTER_FORMATTED_TEXT',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should set the flag to false', () => {
- const state = isCaretWithinFormattedText( true, {
- type: 'EXIT_FORMATTED_TEXT',
+ expect( state.edits ).not.toHaveProperty( 'content' );
} );
-
- expect( state ).toBe( false );
} );
} );
- describe( 'blockSelection()', () => {
- it( 'should return with block clientId as selected', () => {
- const state = blockSelection( undefined, {
- type: 'SELECT_BLOCK',
- clientId: 'kumquat',
- initialPosition: -1,
- } );
-
- expect( state ).toEqual( {
- start: 'kumquat',
- end: 'kumquat',
- initialPosition: -1,
- isMultiSelecting: false,
- isEnabled: true,
- } );
- } );
-
- it( 'should set multi selection', () => {
- const original = deepFreeze( { isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'MULTI_SELECT',
- start: 'ribs',
- end: 'chicken',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should set continuous multi selection', () => {
- const original = deepFreeze( { isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'MULTI_SELECT',
- start: 'ribs',
- end: 'chicken',
- } );
+ describe( 'initialEdits', () => {
+ it( 'should default to initial edits', () => {
+ const state = initialEdits( undefined, {} );
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: true,
- } );
+ expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
} );
- it( 'should start multi selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'START_MULTI_SELECT',
+ it( 'should return initial edits on post reset', () => {
+ const state = initialEdits( undefined, {
+ type: 'RESET_POST',
} );
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: true,
- } );
+ expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
} );
- it( 'should return same reference if already multi-selecting', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'START_MULTI_SELECT',
+ it( 'should return referentially equal state if setup includes no edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR',
} );
expect( state ).toBe( original );
} );
- it( 'should end multi selection with selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should return same reference if already ended multi-selecting', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
+ it( 'should return referentially equal state if reset while having made no edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'RESET_POST',
} );
expect( state ).toBe( original );
} );
- it( 'should end multi selection without selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
+ it( 'should return setup edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ content: '',
+ },
} );
expect( state ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should not update the state if the block is already selected', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
-
- const state1 = blockSelection( original, {
- type: 'SELECT_BLOCK',
- clientId: 'ribs',
- } );
-
- expect( state1 ).toBe( original );
- } );
-
- it( 'should unset multi selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
-
- const state1 = blockSelection( original, {
- type: 'CLEAR_SELECTED_BLOCK',
- } );
-
- expect( state1 ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should return same reference if clearing selection but no selection', () => {
- const original = deepFreeze( { start: null, end: null, isMultiSelecting: false } );
-
- const state1 = blockSelection( original, {
- type: 'CLEAR_SELECTED_BLOCK',
+ title: '',
+ content: '',
} );
-
- expect( state1 ).toBe( original );
} );
- it( 'should select inserted block', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
-
- const state3 = blockSelection( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- } ],
- updateSelection: true,
- } );
-
- expect( state3 ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: false,
+ it( 'should unset content on editor setup', () => {
+ const original = initialEdits( undefined, {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ content: '',
+ },
} );
- } );
-
- it( 'should not select inserted block if updateSelection flag is false', () => {
- const original = deepFreeze( { start: 'a', end: 'b' } );
-
- const state3 = blockSelection( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- } ],
- updateSelection: false,
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR_STATE',
} );
- expect( state3 ).toEqual( {
- start: 'a',
- end: 'b',
- } );
+ expect( state ).toEqual( { title: '' } );
} );
- it( 'should not update the state if the block moved is already selected', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
- const state = blockSelection( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs' ],
+ it( 'should unset values on post update', () => {
+ const original = initialEdits( undefined, {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ },
} );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should replace the selected block', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'UPDATE_POST',
+ edits: {
+ title: '',
+ },
} );
- expect( state ).toEqual( {
- start: 'wings',
- end: 'wings',
- initialPosition: null,
- isMultiSelecting: false,
- } );
+ expect( state ).toEqual( {} );
} );
+ } );
- it( 'should not replace the selected block if we keep it when replacing blocks', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [
- {
- clientId: 'chicken',
- name: 'core/freeform',
- },
- {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
- } );
-
- expect( state ).toBe( original );
- } );
+ describe( 'currentPost()', () => {
+ it( 'should reset a post object', () => {
+ const original = deepFreeze( { title: 'unmodified' } );
- it( 'should reset if replacing with empty set', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [],
+ const state = currentPost( original, {
+ type: 'RESET_POST',
+ post: {
+ title: 'new post',
+ },
} );
expect( state ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
+ title: 'new post',
} );
} );
- it( 'should keep the selected block', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'ribs' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
- } );
-
- expect( state ).toBe( original );
- } );
+ it( 'should update the post object with UPDATE_POST', () => {
+ const original = deepFreeze( { title: 'unmodified', status: 'publish' } );
- it( 'should remove the selection if we are removing the selected block', () => {
- const original = deepFreeze( {
- start: 'chicken',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- const state = blockSelection( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken' ],
+ const state = currentPost( original, {
+ type: 'UPDATE_POST',
+ edits: {
+ title: 'updated post object from server',
+ },
} );
expect( state ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should keep the selection if we are not removing the selected block', () => {
- const original = deepFreeze( {
- start: 'chicken',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- const state = blockSelection( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'ribs' ],
+ title: 'updated post object from server',
+ status: 'publish',
} );
-
- expect( state ).toBe( original );
} );
} );
describe( 'preferences()', () => {
it( 'should apply all defaults', () => {
const state = preferences( undefined, {} );
-
expect( state ).toEqual( {
- insertUsage: {},
isPublishSidebarEnabled: true,
} );
} );
@@ -1922,84 +362,6 @@ describe( 'state', () => {
expect( state.isPublishSidebarEnabled ).toBe( true );
} );
-
- it( 'should record recently used blocks', () => {
- const state = preferences( deepFreeze( { insertUsage: {} } ), {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'bacon',
- name: 'core-embed/twitter',
- } ],
- time: 123456,
- } );
-
- expect( state ).toEqual( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123456,
- count: 1,
- insert: { name: 'core-embed/twitter' },
- },
- },
- } );
-
- const twoRecentBlocks = preferences( deepFreeze( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123456,
- count: 1,
- insert: { name: 'core-embed/twitter' },
- },
- },
- } ), {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'eggs',
- name: 'core-embed/twitter',
- }, {
- clientId: 'bacon',
- name: 'core/block',
- attributes: { ref: 123 },
- } ],
- time: 123457,
- } );
-
- expect( twoRecentBlocks ).toEqual( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123457,
- count: 2,
- insert: { name: 'core-embed/twitter' },
- },
- 'core/block/123': {
- time: 123457,
- count: 1,
- insert: { name: 'core/block', ref: 123 },
- },
- },
- } );
- } );
-
- it( 'should remove recorded reusable blocks that are deleted', () => {
- const initialState = {
- insertUsage: {
- 'core/block/123': {
- time: 1000,
- count: 1,
- insert: { name: 'core/block', ref: 123 },
- },
- },
- };
-
- const state = preferences( deepFreeze( initialState ), {
- type: 'REMOVE_REUSABLE_BLOCK',
- id: 123,
- } );
-
- expect( state ).toEqual( {
- insertUsage: {},
- } );
- } );
} );
describe( 'saving()', () => {
@@ -2047,28 +409,6 @@ describe( 'state', () => {
} );
} );
- describe( 'blocksMode', () => {
- it( 'should set mode to html if not set', () => {
- const action = {
- type: 'TOGGLE_BLOCK_MODE',
- clientId: 'chicken',
- };
- const value = blocksMode( deepFreeze( {} ), action );
-
- expect( value ).toEqual( { chicken: 'html' } );
- } );
-
- it( 'should toggle mode to visual if set as html', () => {
- const action = {
- type: 'TOGGLE_BLOCK_MODE',
- clientId: 'chicken',
- };
- const value = blocksMode( deepFreeze( { chicken: 'html' } ), action );
-
- expect( value ).toEqual( { chicken: 'visual' } );
- } );
- } );
-
describe( 'reusableBlocks()', () => {
it( 'should start out empty', () => {
const state = reusableBlocks( undefined, {} );
@@ -2322,153 +662,6 @@ describe( 'state', () => {
} );
} );
- describe( 'template', () => {
- it( 'should default to visible', () => {
- const state = template( undefined, {} );
-
- expect( state ).toEqual( { isValid: true } );
- } );
-
- it( 'should reset the validity flag', () => {
- const original = deepFreeze( { isValid: false, template: [] } );
- const state = template( original, {
- type: 'SET_TEMPLATE_VALIDITY',
- isValid: true,
- } );
-
- expect( state ).toEqual( { isValid: true, template: [] } );
- } );
- } );
-
- describe( 'blockListSettings', () => {
- it( 'should add new settings', () => {
- const original = deepFreeze( {} );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
- } );
-
- it( 'should return same reference if updated as the same', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return same reference if updated settings not assigned and id not exists', () => {
- const original = deepFreeze( {} );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should update the settings of a block', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/list' ],
- },
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/list' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
- } );
-
- it( 'should remove existing settings if updated settings not assigned', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- } );
-
- expect( state ).toEqual( {} );
- } );
-
- it( 'should remove the settings of a block when it is replaced', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
- } );
-
- it( 'should remove the settings of a block when it is removed', () => {
- const original = deepFreeze( {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- } );
-
- expect( state ).toEqual( {} );
- } );
- } );
-
describe( 'autosave', () => {
it( 'returns null by default', () => {
const state = autosave( undefined, {} );
diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js
index 6e8f963129dc7..71b905f98bec4 100644
--- a/packages/editor/src/store/test/selectors.js
+++ b/packages/editor/src/store/test/selectors.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { filter, without, omit } from 'lodash';
+import { filter, without } from 'lodash';
/**
* WordPress dependencies
@@ -13,6 +13,7 @@ import {
getDefaultBlockName,
setDefaultBlockName,
setFreeformContentHandlerName,
+ getBlockTypes,
} from '@wordpress/blocks';
import { RawHTML } from '@wordpress/element';
@@ -23,9 +24,6 @@ import * as selectors from '../selectors';
import { PREFERENCES_DEFAULTS } from '../defaults';
const {
- canUserUseUnfilteredHTML,
- hasEditorUndo,
- hasEditorRedo,
isEditedPostNew,
hasChangedContent,
isEditedPostDirty,
@@ -49,69 +47,27 @@ const {
isEditedPostEmpty,
isEditedPostBeingScheduled,
isEditedPostDateFloating,
- getBlockDependantsCacheBust,
- getBlockName,
- getBlock,
- getBlocks,
- getBlockCount,
- getClientIdsWithDescendants,
- getClientIdsOfDescendants,
- hasSelectedBlock,
- getSelectedBlock,
- getSelectedBlockClientId,
- getBlockRootClientId,
- getBlockHierarchyRootClientId,
getCurrentPostAttribute,
getEditedPostAttribute,
getAutosaveAttribute,
- getGlobalBlockCount,
- getMultiSelectedBlockClientIds,
- getMultiSelectedBlocks,
- getMultiSelectedBlocksStartClientId,
- getMultiSelectedBlocksEndClientId,
- getBlockOrder,
- getBlockIndex,
- getPreviousBlockClientId,
- getNextBlockClientId,
- isBlockSelected,
- hasSelectedInnerBlock,
- isBlockWithinSelection,
- hasMultiSelection,
- isBlockMultiSelected,
- isFirstMultiSelectedBlock,
- getBlockMode,
- isTyping,
- isCaretWithinFormattedText,
- getBlockInsertionPoint,
- isBlockInsertionPointVisible,
isSavingPost,
didPostSaveRequestSucceed,
didPostSaveRequestFail,
getSuggestedPostFormat,
- getBlocksForSerialization,
getEditedPostContent,
__experimentalGetReusableBlock: getReusableBlock,
__experimentalIsSavingReusableBlock: isSavingReusableBlock,
__experimentalIsFetchingReusableBlock: isFetchingReusableBlock,
- isSelectionEnabled,
__experimentalGetReusableBlocks: getReusableBlocks,
getStateBeforeOptimisticTransaction,
isPublishingPost,
isPublishSidebarEnabled,
- canInsertBlockType,
- getInserterItems,
- isValidTemplate,
- getTemplate,
- getTemplateLock,
- getBlockListSettings,
POST_UPDATE_TRANSACTION_ID,
isPermalinkEditable,
getPermalink,
getPermalinkParts,
- INSERTER_UTILITY_HIGH,
- INSERTER_UTILITY_MEDIUM,
- INSERTER_UTILITY_LOW,
isPostSavingLocked,
+ canUserUseUnfilteredHTML,
} = selectors;
describe( 'selectors', () => {
@@ -201,54 +157,6 @@ describe( 'selectors', () => {
setDefaultBlockName( undefined );
} );
- describe( 'hasEditorUndo', () => {
- it( 'should return true when the past history is not empty', () => {
- const state = {
- editor: {
- past: [
- {},
- ],
- },
- };
-
- expect( hasEditorUndo( state ) ).toBe( true );
- } );
-
- it( 'should return false when the past history is empty', () => {
- const state = {
- editor: {
- past: [],
- },
- };
-
- expect( hasEditorUndo( state ) ).toBe( false );
- } );
- } );
-
- describe( 'hasEditorRedo', () => {
- it( 'should return true when the future history is not empty', () => {
- const state = {
- editor: {
- future: [
- {},
- ],
- },
- };
-
- expect( hasEditorRedo( state ) ).toBe( true );
- } );
-
- it( 'should return false when the future history is empty', () => {
- const state = {
- editor: {
- future: [],
- },
- };
-
- expect( hasEditorRedo( state ) ).toBe( false );
- } );
- } );
-
describe( 'isEditedPostNew', () => {
it( 'should return true when the post is new', () => {
const state = {
@@ -291,12 +199,11 @@ describe( 'selectors', () => {
it( 'should return false if no dirty blocks nor content property edit', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
};
@@ -306,12 +213,11 @@ describe( 'selectors', () => {
it( 'should return true if dirty blocks', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
};
@@ -321,13 +227,12 @@ describe( 'selectors', () => {
it( 'should return true if content property edit', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {
- content: 'text mode edited',
- },
+ blocks: {
+ isDirty: false,
+ value: [],
+ },
+ edits: {
+ content: 'text mode edited',
},
},
};
@@ -341,12 +246,11 @@ describe( 'selectors', () => {
const state = {
optimist: [],
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
};
@@ -357,12 +261,11 @@ describe( 'selectors', () => {
const state = {
optimist: [],
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
};
@@ -373,13 +276,12 @@ describe( 'selectors', () => {
const state = {
optimist: [],
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {
- excerpt: 'hello world',
- },
+ blocks: {
+ isDirty: false,
+ value: [],
+ },
+ edits: {
+ excerpt: 'hello world',
},
},
};
@@ -393,23 +295,21 @@ describe( 'selectors', () => {
{
beforeState: {
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
},
},
],
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
};
@@ -421,12 +321,11 @@ describe( 'selectors', () => {
it( 'should return true when the post is not dirty and has not been saved before', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
id: 1,
@@ -443,12 +342,11 @@ describe( 'selectors', () => {
it( 'should return false when the post is not dirty but the post has been saved', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
id: 1,
@@ -465,12 +363,11 @@ describe( 'selectors', () => {
it( 'should return false when the post is dirty but the post has not been saved', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
currentPost: {
id: 1,
@@ -546,9 +443,7 @@ describe( 'selectors', () => {
const state = {
currentPost: { slug: 'post slug' },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -560,10 +455,8 @@ describe( 'selectors', () => {
const state = {
currentPost: { slug: 'old slug' },
editor: {
- present: {
- edits: {
- slug: 'new slug',
- },
+ edits: {
+ slug: 'new slug',
},
},
initialEdits: {},
@@ -578,9 +471,7 @@ describe( 'selectors', () => {
title: 'sassel',
},
editor: {
- present: {
- edits: { status: 'private' },
- },
+ edits: { status: 'private' },
},
initialEdits: {},
};
@@ -594,9 +485,7 @@ describe( 'selectors', () => {
title: 'sassel',
},
editor: {
- present: {
- edits: { title: 'youcha' },
- },
+ edits: { title: 'youcha' },
},
initialEdits: {},
};
@@ -608,9 +497,7 @@ describe( 'selectors', () => {
const state = {
currentPost: {},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -627,11 +514,9 @@ describe( 'selectors', () => {
},
},
editor: {
- present: {
- edits: {
- meta: {
- b: 2,
- },
+ edits: {
+ meta: {
+ b: 2,
},
},
},
@@ -749,9 +634,7 @@ describe( 'selectors', () => {
it( 'should return the post edits', () => {
const state = {
editor: {
- present: {
- edits: { title: 'terga' },
- },
+ edits: { title: 'terga' },
},
initialEdits: {},
};
@@ -762,9 +645,7 @@ describe( 'selectors', () => {
it( 'should return value from initial edits', () => {
const state = {
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: { title: 'terga' },
};
@@ -775,9 +656,7 @@ describe( 'selectors', () => {
it( 'should prefer value from edits over initial edits', () => {
const state = {
editor: {
- present: {
- edits: { title: 'werga' },
- },
+ edits: { title: 'werga' },
},
initialEdits: { title: 'terga' },
};
@@ -808,9 +687,7 @@ describe( 'selectors', () => {
status: 'draft',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -824,9 +701,7 @@ describe( 'selectors', () => {
status: 'private',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -841,9 +716,7 @@ describe( 'selectors', () => {
password: 'chicken',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -858,11 +731,9 @@ describe( 'selectors', () => {
password: 'chicken',
},
editor: {
- present: {
- edits: {
- status: 'private',
- password: null,
- },
+ edits: {
+ status: 'private',
+ password: null,
},
},
initialEdits: {},
@@ -991,12 +862,11 @@ describe( 'selectors', () => {
it( 'should return true for pending posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'pending',
@@ -1012,12 +882,11 @@ describe( 'selectors', () => {
it( 'should return true for draft posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'draft',
@@ -1033,12 +902,11 @@ describe( 'selectors', () => {
it( 'should return false for published posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'publish',
@@ -1054,12 +922,11 @@ describe( 'selectors', () => {
it( 'should return true for published, dirty posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'publish',
@@ -1075,12 +942,11 @@ describe( 'selectors', () => {
it( 'should return false for private posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'private',
@@ -1096,12 +962,11 @@ describe( 'selectors', () => {
it( 'should return false for scheduled posts', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
currentPost: {
status: 'future',
@@ -1120,12 +985,11 @@ describe( 'selectors', () => {
status: 'private',
},
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ isDirty: true,
+ value: [],
},
+ edits: {},
},
saving: {
requesting: false,
@@ -1162,14 +1026,10 @@ describe( 'selectors', () => {
it( 'should return false if the post has no title, excerpt, content', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1182,14 +1042,10 @@ describe( 'selectors', () => {
it( 'should return false if the post has a title but save already in progress', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1206,14 +1062,10 @@ describe( 'selectors', () => {
it( 'should return true if the post has a title', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1228,14 +1080,13 @@ describe( 'selectors', () => {
it( 'should return true if the post has an excerpt', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1250,26 +1101,19 @@ describe( 'selectors', () => {
it( 'should return true if the post has content', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-block-a',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/test-block-a',
+ isValid: true,
+ attributes: {
text: '',
},
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1282,25 +1126,19 @@ describe( 'selectors', () => {
it( 'should return false if the post has no title, excerpt and empty classic block', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- },
- },
- attributes: {
- 123: {
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1313,26 +1151,19 @@ describe( 'selectors', () => {
it( 'should return true if the post has a title and empty classic block', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1349,14 +1180,10 @@ describe( 'selectors', () => {
it( 'should return false if the post is not saveable', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1376,14 +1203,10 @@ describe( 'selectors', () => {
it( 'should return true if there is not yet an autosave', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1399,12 +1222,11 @@ describe( 'selectors', () => {
it( 'should return false if none of title, excerpt, or content have changed', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ value: [],
+ isDirty: false,
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1424,12 +1246,11 @@ describe( 'selectors', () => {
it( 'should return true if content has changes', () => {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: true,
- },
- edits: {},
+ blocks: {
+ value: [],
+ isDirty: true,
},
+ edits: {},
},
currentPost: {
title: 'foo',
@@ -1450,12 +1271,11 @@ describe( 'selectors', () => {
for ( const constantField of without( [ 'title', 'excerpt' ], variantField ) ) {
const state = {
editor: {
- present: {
- blocks: {
- isDirty: false,
- },
- edits: {},
+ blocks: {
+ isDirty: false,
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1522,14 +1342,10 @@ describe( 'selectors', () => {
it( 'should return true if no blocks and no content', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1541,26 +1357,17 @@ describe( 'selectors', () => {
it( 'should return false if blocks', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-block-a',
- isValid: true,
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-block-a',
+ isValid: true,
attributes: {
- 123: {
- text: '',
- },
- },
- order: {
- '': [ 123 ],
+ text: '',
},
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1577,25 +1384,16 @@ describe( 'selectors', () => {
// See: https://github.com/WordPress/gutenberg/pull/13086
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 'block1',
+ name: 'core/test-default',
attributes: {
- block1: {
- modified: false,
- },
+ modified: false,
},
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1607,27 +1405,18 @@ describe( 'selectors', () => {
it( 'should return true if blocks, but empty content edit', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-block-a',
- isValid: true,
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-block-a',
+ isValid: true,
attributes: {
- 123: {
- text: '',
- },
+ text: '',
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {
- content: '',
- },
+ } ],
+ },
+ edits: {
+ content: '',
},
},
initialEdits: {},
@@ -1642,14 +1431,10 @@ describe( 'selectors', () => {
it( 'should return true if the post has an empty content property', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1663,15 +1448,11 @@ describe( 'selectors', () => {
it( 'should return false if edits include a non-empty content property', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {
- content: 'sassel',
- },
+ blocks: {
+ value: [],
+ },
+ edits: {
+ content: 'sassel',
},
},
initialEdits: {},
@@ -1684,26 +1465,17 @@ describe( 'selectors', () => {
it( 'should return true if empty classic block', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
attributes: {
- 123: {
- content: '',
- },
+ content: '',
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {},
@@ -1715,26 +1487,17 @@ describe( 'selectors', () => {
it( 'should return true if empty content freeform block', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
attributes: {
- 123: {
- content: '',
- },
+ content: '',
},
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1748,26 +1511,17 @@ describe( 'selectors', () => {
it( 'should return false if non-empty content freeform block', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
+ blocks: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
attributes: {
- 123: {
- content: 'Test Data',
- },
- },
- order: {
- '': [ 123 ],
+ content: 'Test Data',
},
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1781,34 +1535,27 @@ describe( 'selectors', () => {
it( 'should return false for multiple empty freeform blocks', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- 456: {
- clientId: 456,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
- 456: {
+ },
+ {
+ clientId: 456,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
},
- order: {
- '': [ 123, 456 ],
- },
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
currentPost: {
@@ -1826,9 +1573,7 @@ describe( 'selectors', () => {
const date = new Date( time );
const state = {
editor: {
- present: {
- edits: { date: date.toUTCString() },
- },
+ edits: { date: date.toUTCString() },
},
initialEdits: {},
};
@@ -1839,9 +1584,7 @@ describe( 'selectors', () => {
it( 'should return false for posts with an old date', () => {
const state = {
editor: {
- present: {
- edits: { date: '2016-05-30T17:21:39' },
- },
+ edits: { date: '2016-05-30T17:21:39' },
},
initialEdits: {},
};
@@ -1859,9 +1602,7 @@ describe( 'selectors', () => {
status: 'draft',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -1877,9 +1618,7 @@ describe( 'selectors', () => {
status: 'auto-draft',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -1895,9 +1634,7 @@ describe( 'selectors', () => {
status: 'draft',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -1913,9 +1650,7 @@ describe( 'selectors', () => {
status: 'auto-draft',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -1931,9 +1666,7 @@ describe( 'selectors', () => {
status: 'publish',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -1942,3038 +1675,354 @@ describe( 'selectors', () => {
} );
} );
- describe( 'getBlockDependantsCacheBust', () => {
- const rootBlock = { clientId: 123, name: 'core/paragraph' };
- const rootBlockAttributes = {};
- const rootOrder = [ 123 ];
-
- it( 'returns an unchanging reference', () => {
- const rootBlockOrder = [];
-
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
- } );
-
- it( 'returns a new reference on added inner block', () => {
+ describe( 'isSavingPost', () => {
+ it( 'should return true if the post is currently being saved', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: {},
- },
- order: {
- '': rootOrder,
- 123: [ 456 ],
- 456: [],
- },
- },
- edits: {},
- },
+ saving: {
+ requesting: true,
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( isSavingPost( state ) ).toBe( true );
} );
- it( 'returns an unchanging reference on unchanging inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlock = { clientId: 456, name: 'core/paragraph' };
- const childBlockAttributes = {};
- const childBlockOrder = [];
-
+ it( 'should return false if the post is not currently being saved', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
- },
- edits: {},
- },
+ saving: {
+ requesting: false,
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( isSavingPost( state ) ).toBe( false );
} );
+ } );
- it( 'returns a new reference on updated inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlockOrder = [];
-
+ describe( 'didPostSaveRequestSucceed', () => {
+ it( 'should return true if the post save request is successful', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: {},
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: { content: [ 'foo' ] },
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
- },
- edits: {},
- },
+ saving: {
+ successful: true,
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( didPostSaveRequestSucceed( state ) ).toBe( true );
} );
- it( 'returns a new reference on updated grandchild inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlock = { clientId: 456, name: 'core/paragraph' };
- const childBlockAttributes = {};
- const childBlockOrder = [ 789 ];
- const grandChildBlockOrder = [];
-
+ it( 'should return true if the post save request has failed', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- 789: {},
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- 789: grandChildBlockOrder,
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- 789: { content: [ 'foo' ] },
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- 789: grandChildBlockOrder,
- },
- },
- edits: {},
- },
+ saving: {
+ successful: false,
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( didPostSaveRequestSucceed( state ) ).toBe( false );
} );
} );
- describe( 'getBlockName', () => {
- it( 'returns null if no block by clientId', () => {
+ describe( 'didPostSaveRequestFail', () => {
+ it( 'should return true if the post save request has failed', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
+ saving: {
+ error: 'error',
},
- initialEdits: {},
};
- const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
-
- expect( name ).toBe( null );
+ expect( didPostSaveRequestFail( state ) ).toBe( true );
} );
- it( 'returns block name', () => {
+ it( 'should return true if the post save request is successful', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- name: 'core/paragraph',
- },
- },
- attributes: {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {},
- },
- order: {
- '': [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': [],
- },
- },
- edits: {},
- },
+ saving: {
+ error: false,
},
- initialEdits: {},
};
- const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
-
- expect( name ).toBe( 'core/paragraph' );
+ expect( didPostSaveRequestFail( state ) ).toBe( false );
} );
} );
- describe( 'getBlock', () => {
- it( 'should return the block', () => {
+ describe( 'getSuggestedPostFormat', () => {
+ it( 'returns null if cannot be determined', () => {
const state = {
- currentPost: {},
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- 123: [],
- },
- },
- edits: {},
+ blocks: {
+ value: [],
},
+ edits: {},
},
initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [],
- } );
- } );
-
- it( 'should return null if the block is not present in state', () => {
- const state = {
currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
};
- expect( getBlock( state, 123 ) ).toBe( null );
+ expect( getSuggestedPostFormat( state ) ).toBeNull();
} );
- it( 'should include inner blocks', () => {
+ it( 'returns null if there is more than one block in the post', () => {
const state = {
- currentPost: {},
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/paragraph' },
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/image',
+ attributes: {},
},
- order: {
- '': [ 123 ],
- 123: [ 456 ],
- 456: [],
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
},
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
+ currentPost: {},
};
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [ {
- clientId: 456,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
+ expect( getSuggestedPostFormat( state ) ).toBeNull();
} );
- it( 'should merge meta attributes for the block', () => {
- registerBlockType( 'core/meta-block', {
- save: ( props ) => props.attributes.text,
- category: 'common',
- title: 'test block',
- attributes: {
- foo: {
- type: 'string',
- source: 'meta',
- meta: 'foo',
- },
- },
- } );
-
+ it( 'returns Image if the first block is of type `core/image`', () => {
const state = {
- currentPost: {
- meta: {
- foo: 'bar',
- },
- },
editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/meta-block' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- 123: [],
+ blocks: {
+ value: [
+ {
+ clientId: 123,
+ name: 'core/image',
+ attributes: {},
},
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/meta-block',
- attributes: {
- foo: 'bar',
- },
- innerBlocks: [],
- } );
-
- unregisterBlockType( 'core/meta-block' );
- } );
- } );
-
- describe( 'getBlocks', () => {
- it( 'should return the ordered blocks', () => {
- const state = {
currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 123, 23 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
};
-
- expect( getBlocks( state ) ).toEqual( [
- { clientId: 123, name: 'core/paragraph', attributes: {}, innerBlocks: [] },
- { clientId: 23, name: 'core/heading', attributes: {}, innerBlocks: [] },
- ] );
- } );
- } );
-
- describe( 'getClientIdsOfDescendants', () => {
- it( 'should return the ids of any descendants, given an array of clientIds', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
- 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
- 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
- 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
- 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
- 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
- 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
- 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
- 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
- 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
- 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
- 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
- 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
- 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
- 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
- },
- attributes: {
- 'uuid-2': {},
- 'uuid-4': {},
- 'uuid-6': {},
- 'uuid-8': {},
- 'uuid-10': {},
- 'uuid-12': {},
- 'uuid-14': {},
- 'uuid-16': {},
- 'uuid-18': {},
- 'uuid-20': {},
- 'uuid-22': {},
- 'uuid-24': {},
- 'uuid-26': {},
- 'uuid-28': {},
- 'uuid-30': {},
- },
- order: {
- '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
- 'uuid-2': [ ],
- 'uuid-4': [ ],
- 'uuid-6': [ ],
- 'uuid-8': [ ],
- 'uuid-10': [ 'uuid-12', 'uuid-14' ],
- 'uuid-12': [ 'uuid-16' ],
- 'uuid-14': [ 'uuid-18' ],
- 'uuid-16': [ ],
- 'uuid-18': [ 'uuid-24' ],
- 'uuid-20': [ ],
- 'uuid-22': [ ],
- 'uuid-24': [ 'uuid-26', 'uuid-28' ],
- 'uuid-26': [ ],
- 'uuid-28': [ 'uuid-30' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
- expect( getClientIdsOfDescendants( state, [ 'uuid-10' ] ) ).toEqual( [
- 'uuid-12',
- 'uuid-14',
- 'uuid-16',
- 'uuid-18',
- 'uuid-24',
- 'uuid-26',
- 'uuid-28',
- 'uuid-30',
- ] );
- } );
- } );
-
- describe( 'getClientIdsWithDescendants', () => {
- it( 'should return the ids for top-level blocks and their descendants of any depth (for nested blocks).', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
- 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
- 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
- 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
- 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
- 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
- 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
- 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
- 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
- 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
- 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
- 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
- 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
- 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
- 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
- },
- attributes: {
- 'uuid-2': {},
- 'uuid-4': {},
- 'uuid-6': {},
- 'uuid-8': {},
- 'uuid-10': {},
- 'uuid-12': {},
- 'uuid-14': {},
- 'uuid-16': {},
- 'uuid-18': {},
- 'uuid-20': {},
- 'uuid-22': {},
- 'uuid-24': {},
- 'uuid-26': {},
- 'uuid-28': {},
- 'uuid-30': {},
- },
- order: {
- '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
- 'uuid-2': [ ],
- 'uuid-4': [ ],
- 'uuid-6': [ ],
- 'uuid-8': [ ],
- 'uuid-10': [ 'uuid-12', 'uuid-14' ],
- 'uuid-12': [ 'uuid-16' ],
- 'uuid-14': [ 'uuid-18' ],
- 'uuid-16': [ ],
- 'uuid-18': [ 'uuid-24' ],
- 'uuid-20': [ ],
- 'uuid-22': [ ],
- 'uuid-24': [ 'uuid-26', 'uuid-28' ],
- 'uuid-26': [ ],
- 'uuid-28': [ 'uuid-30' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
- expect( getClientIdsWithDescendants( state ) ).toEqual( [
- 'uuid-6',
- 'uuid-8',
- 'uuid-10',
- 'uuid-22',
- 'uuid-12',
- 'uuid-14',
- 'uuid-16',
- 'uuid-18',
- 'uuid-24',
- 'uuid-26',
- 'uuid-28',
- 'uuid-30',
- ] );
- } );
- } );
-
- describe( 'getBlockCount', () => {
- it( 'should return the number of top-level blocks in the post', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockCount( state ) ).toBe( 2 );
- } );
-
- it( 'should return the number of blocks in a nested context', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/columns' },
- 456: { clientId: 456, name: 'core/paragraph' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
- 789: {},
- },
- order: {
- '': [ 123 ],
- 123: [ 456, 789 ],
- },
- },
- },
- },
- };
-
- expect( getBlockCount( state, '123' ) ).toBe( 2 );
- } );
- } );
-
- describe( 'hasSelectedBlock', () => {
- it( 'should return false if no selection', () => {
- const state = {
- blockSelection: {
- start: null,
- end: null,
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( false );
- } );
-
- it( 'should return false if multi-selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( false );
- } );
-
- it( 'should return true if singular selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( true );
- } );
- } );
-
- describe( 'getGlobalBlockCount', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/heading' },
- 456: { clientId: 456, name: 'core/paragraph' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
- 789: {},
- },
- order: {
- '': [ 123, 456 ],
- },
- },
- },
- },
- };
-
- it( 'should return the global number of blocks in the post', () => {
- expect( getGlobalBlockCount( state ) ).toBe( 2 );
- } );
-
- it( 'should return the global number of blocks in the post of a given type', () => {
- expect( getGlobalBlockCount( state, 'core/paragraph' ) ).toBe( 1 );
- } );
-
- it( 'should return 0 if no blocks exist', () => {
- const emptyState = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- },
- },
- };
- expect( getGlobalBlockCount( emptyState ) ).toBe( 0 );
- expect( getGlobalBlockCount( emptyState, 'core/heading' ) ).toBe( 0 );
- } );
- } );
-
- describe( 'getSelectedBlockClientId', () => {
- it( 'should return null if no block is selected', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getSelectedBlockClientId( state ) ).toBe( null );
- } );
-
- it( 'should return null if there is multi selection', () => {
- const state = {
- blockSelection: { start: 23, end: 123 },
- };
-
- expect( getSelectedBlockClientId( state ) ).toBe( null );
- } );
-
- it( 'should return the selected block ClientId', () => {
- const state = {
- editor: { present: { blocks: { byClientId: { 23: { name: 'fake block' } } } } },
- blockSelection: { start: 23, end: 23 },
- };
-
- expect( getSelectedBlockClientId( state ) ).toEqual( 23 );
- } );
- } );
-
- describe( 'getSelectedBlock', () => {
- it( 'should return null if no block is selected', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: null, end: null },
- };
-
- expect( getSelectedBlock( state ) ).toBe( null );
- } );
-
- it( 'should return null if there is multi selection', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: 23, end: 123 },
- };
-
- expect( getSelectedBlock( state ) ).toBe( null );
- } );
-
- it( 'should return the selected block', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: 23, end: 23 },
- };
-
- expect( getSelectedBlock( state ) ).toEqual( {
- clientId: 23,
- name: 'core/heading',
- attributes: {},
- innerBlocks: [],
- } );
- } );
- } );
-
- describe( 'getBlockRootClientId', () => {
- it( 'should return null if the block does not exist', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {},
- },
- },
- },
- };
-
- expect( getBlockRootClientId( state, 56 ) ).toBeNull();
- } );
-
- it( 'should return root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockRootClientId( state, 56 ) ).toBe( '123' );
- } );
- } );
-
- describe( 'getBlockHierarchyRootClientId', () => {
- it( 'should return the given block if the block has no parents', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {},
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( 56 );
- } );
-
- it( 'should return root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( '123' );
- } );
-
- it( 'should return the top level root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ '123', '23' ],
- 123: [ '456', '56' ],
- 56: [ '12' ],
- },
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, '12' ) ).toBe( '123' );
- } );
- } );
-
- describe( 'getMultiSelectedBlockClientIds', () => {
- it( 'should return empty if there is no multi selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [] );
- } );
-
- it( 'should return selected block clientIds if there is multi selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 4, 3, 2 ] );
- } );
-
- it( 'should return selected block clientIds if there is multi selection (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- 4: [ 9, 8, 7, 6 ],
- },
- },
- },
- },
- blockSelection: { start: 7, end: 9 },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 9, 8, 7 ] );
- } );
- } );
-
- describe( 'getMultiSelectedBlocks', () => {
- it( 'should return the same reference on subsequent invocations of empty selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: null, end: null },
- currentPost: {},
- };
-
- expect(
- getMultiSelectedBlocks( state )
- ).toBe( getMultiSelectedBlocks( state ) );
- } );
- } );
-
- describe( 'getMultiSelectedBlocksStartClientId', () => {
- it( 'returns null if there is no multi selection', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlocksStartClientId( state ) ).toBeNull();
- } );
-
- it( 'returns multi selection start', () => {
- const state = {
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlocksStartClientId( state ) ).toBe( 2 );
- } );
- } );
-
- describe( 'getMultiSelectedBlocksEndClientId', () => {
- it( 'returns null if there is no multi selection', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlocksEndClientId( state ) ).toBeNull();
- } );
-
- it( 'returns multi selection end', () => {
- const state = {
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlocksEndClientId( state ) ).toBe( 4 );
- } );
- } );
-
- describe( 'getBlockOrder', () => {
- it( 'should return the ordered block ClientIds of top-level blocks by default', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockOrder( state ) ).toEqual( [ 123, 23 ] );
- } );
-
- it( 'should return the ordered block ClientIds at a specified rootClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456 ],
- },
- },
- },
- },
- };
-
- expect( getBlockOrder( state, '123' ) ).toEqual( [ 456 ] );
- } );
- } );
-
- describe( 'getBlockIndex', () => {
- it( 'should return the block order', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockIndex( state, 23 ) ).toBe( 1 );
- } );
-
- it( 'should return the block order (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockIndex( state, 56, '123' ) ).toBe( 1 );
- } );
- } );
-
- describe( 'getPreviousBlockClientId', () => {
- it( 'should return the previous block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 23 ) ).toEqual( 123 );
- } );
-
- it( 'should return the previous block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 56, '123' ) ).toEqual( 456 );
- } );
-
- it( 'should return null for the first block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 123 ) ).toBeNull();
- } );
-
- it( 'should return null for the first block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 456, '123' ) ).toBeNull();
- } );
- } );
-
- describe( 'getNextBlockClientId', () => {
- it( 'should return the following block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 123 ) ).toEqual( 23 );
- } );
-
- it( 'should return the following block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 456, '123' ) ).toEqual( 56 );
- } );
-
- it( 'should return null for the last block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 23 ) ).toBeNull();
- } );
-
- it( 'should return null for the last block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 56, '123' ) ).toBeNull();
- } );
- } );
-
- describe( 'isBlockSelected', () => {
- it( 'should return true if the block is selected', () => {
- const state = {
- blockSelection: { start: 123, end: 123 },
- };
-
- expect( isBlockSelected( state, 123 ) ).toBe( true );
- } );
-
- it( 'should return false if a multi-selection range exists', () => {
- const state = {
- blockSelection: { start: 123, end: 124 },
- };
-
- expect( isBlockSelected( state, 123 ) ).toBe( false );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( isBlockSelected( state, 23 ) ).toBe( false );
- } );
- } );
-
- describe( 'hasSelectedInnerBlock', () => {
- it( 'should return false if the selected block is a child of the given ClientId', () => {
- const state = {
- blockSelection: { start: 5, end: 5 },
- editor: {
- present: {
- blocks: {
- order: {
- 4: [ 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( hasSelectedInnerBlock( state, 4 ) ).toBe( false );
- } );
-
- it( 'should return true if the selected block is a child of the given ClientId', () => {
- const state = {
- blockSelection: { start: 3, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- 4: [ 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( hasSelectedInnerBlock( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return true if a multi selection exists that contains children of the block with the given ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- 6: [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
- expect( hasSelectedInnerBlock( state, 6 ) ).toBe( true );
- } );
-
- it( 'should return false if a multi selection exists bot does not contains children of the block with the given ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- 3: [ 2, 1 ],
- 6: [ 5, 4 ],
- },
- },
- },
- },
- blockSelection: { start: 5, end: 4 },
- };
- expect( hasSelectedInnerBlock( state, 3 ) ).toBe( false );
- } );
- } );
-
- describe( 'isBlockWithinSelection', () => {
- it( 'should return true if the block is selected but not the last', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is the last selected', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 3 ) ).toBe( false );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 2 ) ).toBe( false );
- } );
-
- it( 'should return false if there is no selection', () => {
- const state = {
- blockSelection: {},
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 4 ) ).toBe( false );
- } );
- } );
-
- describe( 'hasMultiSelection', () => {
- it( 'should return false if no selection', () => {
- const state = {
- blockSelection: {
- start: null,
- end: null,
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( false );
- } );
-
- it( 'should return false if singular selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( false );
- } );
-
- it( 'should return true if multi-selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( true );
- } );
- } );
-
- describe( 'isBlockMultiSelected', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- it( 'should return true if the block is multi selected', () => {
- expect( isBlockMultiSelected( state, 3 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not multi selected', () => {
- expect( isBlockMultiSelected( state, 5 ) ).toBe( false );
- } );
- } );
-
- describe( 'isFirstMultiSelectedBlock', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- it( 'should return true if the block is first in multi selection', () => {
- expect( isFirstMultiSelectedBlock( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not first in multi selection', () => {
- expect( isFirstMultiSelectedBlock( state, 3 ) ).toBe( false );
- } );
- } );
-
- describe( 'getBlockMode', () => {
- it( 'should return "visual" if unset', () => {
- const state = {
- blocksMode: {},
- };
-
- expect( getBlockMode( state, 123 ) ).toEqual( 'visual' );
- } );
-
- it( 'should return the block mode', () => {
- const state = {
- blocksMode: {
- 123: 'html',
- },
- };
-
- expect( getBlockMode( state, 123 ) ).toEqual( 'html' );
- } );
- } );
-
- describe( 'isTyping', () => {
- it( 'should return the isTyping flag if the block is selected', () => {
- const state = {
- isTyping: true,
- };
-
- expect( isTyping( state ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- isTyping: false,
- };
-
- expect( isTyping( state ) ).toBe( false );
- } );
- } );
-
- describe( 'isCaretWithinFormattedText', () => {
- it( 'returns true if the isCaretWithinFormattedText state is also true', () => {
- const state = {
- isCaretWithinFormattedText: true,
- };
-
- expect( isCaretWithinFormattedText( state ) ).toBe( true );
- } );
-
- it( 'returns false if the isCaretWithinFormattedText state is also false', () => {
- const state = {
- isCaretWithinFormattedText: false,
- };
-
- expect( isCaretWithinFormattedText( state ) ).toBe( false );
- } );
- } );
-
- describe( 'isSelectionEnabled', () => {
- it( 'should return true if selection is enable', () => {
- const state = {
- blockSelection: {
- isEnabled: true,
- },
- };
-
- expect( isSelectionEnabled( state ) ).toBe( true );
- } );
-
- it( 'should return false if selection is disabled', () => {
- const state = {
- blockSelection: {
- isEnabled: false,
- },
- };
-
- expect( isSelectionEnabled( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getBlockInsertionPoint', () => {
- it( 'should return the explicitly assigned insertion point', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId2',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [ 'clientId2' ],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: {
- rootClientId: undefined,
- index: 0,
- },
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 0,
- } );
- } );
-
- it( 'should return an object for the selected block', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId1',
- end: 'clientId1',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- },
- attributes: {
- clientId1: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 1,
- } );
- } );
-
- it( 'should return an object for the nested selected block', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId2',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [ 'clientId2' ],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: 'clientId1',
- index: 1,
- } );
- } );
-
- it( 'should return an object for the last multi selected clientId', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId1',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1', 'clientId2' ],
- clientId1: [],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 2,
- } );
- } );
-
- it( 'should return an object for the last block if no selection', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: null,
- end: null,
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1', 'clientId2' ],
- clientId1: [],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 2,
- } );
- } );
- } );
-
- describe( 'isBlockInsertionPointVisible', () => {
- it( 'should return false if no assigned insertion point', () => {
- const state = {
- insertionPoint: null,
- };
-
- expect( isBlockInsertionPointVisible( state ) ).toBe( false );
- } );
-
- it( 'should return true if assigned insertion point', () => {
- const state = {
- insertionPoint: {
- rootClientId: undefined,
- index: 5,
- },
- };
-
- expect( isBlockInsertionPointVisible( state ) ).toBe( true );
- } );
- } );
-
- describe( 'isSavingPost', () => {
- it( 'should return true if the post is currently being saved', () => {
- const state = {
- saving: {
- requesting: true,
- },
- };
-
- expect( isSavingPost( state ) ).toBe( true );
- } );
-
- it( 'should return false if the post is not currently being saved', () => {
- const state = {
- saving: {
- requesting: false,
- },
- };
-
- expect( isSavingPost( state ) ).toBe( false );
- } );
- } );
-
- describe( 'didPostSaveRequestSucceed', () => {
- it( 'should return true if the post save request is successful', () => {
- const state = {
- saving: {
- successful: true,
- },
- };
-
- expect( didPostSaveRequestSucceed( state ) ).toBe( true );
- } );
-
- it( 'should return true if the post save request has failed', () => {
- const state = {
- saving: {
- successful: false,
- },
- };
-
- expect( didPostSaveRequestSucceed( state ) ).toBe( false );
- } );
- } );
-
- describe( 'didPostSaveRequestFail', () => {
- it( 'should return true if the post save request has failed', () => {
- const state = {
- saving: {
- error: 'error',
- },
- };
-
- expect( didPostSaveRequestFail( state ) ).toBe( true );
- } );
-
- it( 'should return true if the post save request is successful', () => {
- const state = {
- saving: {
- error: false,
- },
- };
-
- expect( didPostSaveRequestFail( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getSuggestedPostFormat', () => {
- it( 'returns null if cannot be determined', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBeNull();
- } );
-
- it( 'returns null if there is more than one block in the post', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/image' },
- 456: { clientId: 456, name: 'core/quote' },
- },
- attributes: {
- 123: {},
- 456: {},
- },
- order: {
- '': [ 123, 456 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBeNull();
- } );
-
- it( 'returns Image if the first block is of type `core/image`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/image' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'image' );
- } );
-
- it( 'returns Quote if the first block is of type `core/quote`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 456: { clientId: 456, name: 'core/quote' },
- },
- attributes: {
- 456: {},
- },
- order: {
- '': [ 456 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
- } );
-
- it( 'returns Video if the first block is of type `core-embed/youtube`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 567: { clientId: 567, name: 'core-embed/youtube' },
- },
- attributes: {
- 567: {},
- },
- order: {
- '': [ 567 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'video' );
- } );
-
- it( 'returns Quote if the first block is of type `core/quote` and second is of type `core/paragraph`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 456: { clientId: 456, name: 'core/quote' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 456: {},
- 789: {},
- },
- order: {
- '': [ 456, 789 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
- } );
- } );
-
- describe( 'getBlocksForSerialization', () => {
- it( 'should return blocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- block2: {
- clientId: 'block2',
- name: 'core/heading',
- },
- },
- attributes: {
- block1: {
- modified: false,
- },
- block2: {},
- },
- order: {
- '': [ 'block1', 'block2' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [
- { clientId: 'block1', name: 'core/test-default', attributes: { modified: false }, innerBlocks: [] },
- { clientId: 'block2', name: 'core/heading', attributes: {}, innerBlocks: [] },
- ] );
- } );
-
- it( 'should return an empty set if content is a single unmodified default block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
- attributes: {
- block1: {
- modified: false,
- },
- },
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [] );
- } );
-
- it( 'should return a set including a single modified default block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
- attributes: {
- block1: {
- modified: true,
- },
- },
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [
- { clientId: 'block1', name: 'core/test-default', attributes: { modified: true }, innerBlocks: [] },
- ] );
- } );
- } );
-
- describe( 'getEditedPostContent', () => {
- it( 'defers to returning an edited post attribute', () => {
- const block = createBlock( 'core/block' );
-
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ block.clientId ]: omit( block, 'attributes' ),
- },
- attributes: {
- [ block.clientId ]: block.attributes,
- },
- order: {
- '': [ block.clientId ],
- },
- },
- edits: {
- content: 'custom edit',
- },
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'custom edit' );
- } );
-
- it( 'returns serialization of blocks', () => {
- const block = createBlock( 'core/block' );
-
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ block.clientId ]: omit( block, 'attributes' ),
- },
- attributes: {
- [ block.clientId ]: block.attributes,
- },
- order: {
- '': [ block.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
-
- it( 'returns removep\'d serialization of blocks for single unknown', () => {
- const unknownBlock = createBlock( 'core/test-freeform', {
- content: '
foo
',
- } );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ unknownBlock.clientId ]: omit( unknownBlock, 'attributes' ),
- },
- attributes: {
- [ unknownBlock.clientId ]: unknownBlock.attributes,
- },
- order: {
- '': [ unknownBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'foo' );
- } );
-
- it( 'returns non-removep\'d serialization of blocks for multiple unknown', () => {
- const firstUnknown = createBlock( 'core/test-freeform', {
- content: 'foo
',
- } );
- const secondUnknown = createBlock( 'core/test-freeform', {
- content: 'bar
',
- } );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ firstUnknown.clientId ]: omit( firstUnknown, 'attributes' ),
- [ secondUnknown.clientId ]: omit( secondUnknown, 'attributes' ),
- },
- attributes: {
- [ firstUnknown.clientId ]: firstUnknown.attributes,
- [ secondUnknown.clientId ]: secondUnknown.attributes,
- },
- order: {
- '': [ firstUnknown.clientId, secondUnknown.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'foo
\n\nbar
' );
- } );
-
- it( 'returns empty string for single unmodified default block', () => {
- const defaultBlock = createBlock( getDefaultBlockName() );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ defaultBlock.clientId ]: omit( defaultBlock, 'attributes' ),
- },
- attributes: {
- [ defaultBlock.clientId ]: defaultBlock.attributes,
- },
- order: {
- '': [ defaultBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
-
- it( 'should not return empty string for modified default block', () => {
- const defaultBlock = createBlock( getDefaultBlockName() );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ defaultBlock.clientId ]: {
- ...omit( defaultBlock, 'attributes' ),
- },
- },
- attributes: {
- [ defaultBlock.clientId ]: {
- ...defaultBlock.attributes,
- modified: true,
- },
- },
- order: {
- '': [ defaultBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
- } );
-
- describe( 'canInsertBlockType', () => {
- it( 'should deny blocks that are not registered', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/invalid' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that are not allowed by the editor', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- allowedBlockTypes: [],
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
- } );
-
- it( 'should allow blocks that are allowed by the editor', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- allowedBlockTypes: [ 'core/test-block-a' ],
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( true );
- } );
-
- it( 'should deny blocks when the editor has a template lock', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- templateLock: 'all',
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that restrict parent from being inserted into the root', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that restrict parent from being inserted into a restricted parent', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( false );
- } );
-
- it( 'should allow blocks that restrict parent to be inserted into an allowed parent', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
- } );
-
- it( 'should deny restricted blocks from being inserted into a block that restricts allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [ 'core/test-block-c' ],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( false );
- } );
-
- it( 'should allow allowed blocks to be inserted into a block that restricts allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [ 'core/test-block-b' ],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( true );
- } );
-
- it( 'should prioritise parent over allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
- } );
- } );
-
- describe( 'getInserterItems', () => {
- it( 'should properly list block type and reusable block items', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
- },
- },
- currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state );
- const testBlockAItem = items.find( ( item ) => item.id === 'core/test-block-a' );
- expect( testBlockAItem ).toEqual( {
- id: 'core/test-block-a',
- name: 'core/test-block-a',
- initialAttributes: {},
- title: 'Test Block A',
- icon: {
- src: 'test',
- },
- category: 'formatting',
- keywords: [ 'testing' ],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- hasChildBlocksWithInserterSupport: false,
- } );
- const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' );
- expect( reusableBlockItem ).toEqual( {
- id: 'core/block/1',
- name: 'core/block',
- initialAttributes: { ref: 1 },
- title: 'Reusable Block 1',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- } );
+
+ expect( getSuggestedPostFormat( state ) ).toBe( 'image' );
} );
- it( 'should not list a reusable block item if it is being inserted inside it self', () => {
+ it( 'returns Quote if the first block is of type `core/quote`', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1ref: {
- name: 'core/block',
- clientId: 'block1ref',
- },
- itselfBlock1: { name: 'core/test-block-a' },
- itselfBlock2: { name: 'core/test-block-b' },
- },
- attributes: {
- block1ref: {
- attributes: {
- ref: 1,
- },
- },
- itselfBlock1: {},
- itselfBlock2: {},
- },
- order: {
- '': [ 'block1ref' ],
+ blocks: {
+ value: [
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
},
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'itselfBlock1', title: 'Reusable Block 1' },
- 2: { clientId: 'itselfBlock2', title: 'Reusable Block 2' },
- },
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state, 'itselfBlock1' );
- const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
- expect( reusableBlockItems ).toHaveLength( 1 );
- expect( reusableBlockItems[ 0 ] ).toEqual( {
- id: 'core/block/2',
- name: 'core/block',
- initialAttributes: { ref: 2 },
- title: 'Reusable Block 2',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- } );
+ };
+
+ expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
} );
- it( 'should not list a reusable block item if it is being inserted inside a descendent', () => {
+ it( 'returns Video if the first block is of type `core-embed/youtube`', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block2ref: {
- name: 'core/block',
- clientId: 'block1ref',
- },
- referredBlock1: { name: 'core/test-block-a' },
- referredBlock2: { name: 'core/test-block-b' },
- childReferredBlock2: { name: 'core/test-block-a' },
- grandchildReferredBlock2: { name: 'core/test-block-b' },
- },
- attributes: {
- block2ref: {
- attributes: {
- ref: 2,
- },
- },
- referredBlock1: {},
- referredBlock2: {},
- childReferredBlock2: {},
- grandchildReferredBlock2: {},
- },
- order: {
- '': [ 'block2ref' ],
- referredBlock2: [ 'childReferredBlock2' ],
- childReferredBlock2: [ 'grandchildReferredBlock2' ],
+ blocks: {
+ value: [
+ {
+ clientId: 567,
+ name: 'core-embed/youtube',
+ attributes: {},
},
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'referredBlock1', title: 'Reusable Block 1' },
- 2: { clientId: 'referredBlock2', title: 'Reusable Block 2' },
- },
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state, 'grandchildReferredBlock2' );
- const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
- expect( reusableBlockItems ).toHaveLength( 1 );
- expect( reusableBlockItems[ 0 ] ).toEqual( {
- id: 'core/block/1',
- name: 'core/block',
- initialAttributes: { ref: 1 },
- title: 'Reusable Block 1',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- } );
+ };
+
+ expect( getSuggestedPostFormat( state ) ).toBe( 'video' );
} );
- it( 'should order items by descending utility and frecency', () => {
+
+ it( 'returns Quote if the first block is of type `core/quote` and second is of type `core/paragraph`', () => {
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- block2: { name: 'core/test-block-a' },
+ blocks: {
+ value: [
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
},
- attributes: {
- block1: {},
- block2: {},
+ {
+ clientId: 789,
+ name: 'core/paragraph',
+ attributes: {},
},
- order: {},
- },
- edits: {},
+ ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
- 2: { clientId: 'block1', title: 'Reusable Block 2' },
- },
- },
currentPost: {},
- preferences: {
- insertUsage: {
- 'core/block/1': { count: 10, time: 1000 },
- 'core/block/2': { count: 20, time: 1000 },
+ };
+
+ expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
+ } );
+ } );
+
+ describe( 'getEditedPostContent', () => {
+ let originalDefaultBlockName;
+
+ beforeAll( () => {
+ originalDefaultBlockName = getDefaultBlockName();
+
+ registerBlockType( 'core/default', {
+ category: 'common',
+ title: 'default',
+ attributes: {
+ modified: {
+ type: 'boolean',
+ default: false,
},
},
- blockListSettings: {},
- settings: {},
- };
- const itemIDs = getInserterItems( state ).map( ( item ) => item.id );
- expect( itemIDs ).toEqual( [
- 'core/block/2',
- 'core/block/1',
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- ] );
+ save: () => null,
+ } );
+ setDefaultBlockName( 'core/default' );
+ } );
+
+ afterAll( () => {
+ setDefaultBlockName( originalDefaultBlockName );
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
} );
- it( 'should correctly cache the return values', () => {
+ it( 'defers to returning an edited post attribute', () => {
+ const block = createBlock( 'core/block' );
+
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- block2: { name: 'core/test-block-a' },
- block3: { name: 'core/test-block-a' },
- block4: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- block2: {},
- block3: {},
- block4: {},
- },
- order: {
- '': [ 'block3', 'block4' ],
- },
- },
- edits: {},
+ blocks: {
+ value: [ block ],
},
- },
- initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
- 2: { clientId: 'block1', title: 'Reusable Block 2' },
+ edits: {
+ content: 'custom edit',
},
},
+ initialEdits: {},
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const stateSecondBlockRestricted = {
- ...state,
- blockListSettings: {
- block4: {
- allowedBlocks: [ 'core/test-block-b' ],
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'custom edit' );
+ } );
+
+ it( 'returns serialization of blocks', () => {
+ const block = createBlock( 'core/block' );
+
+ const state = {
+ editor: {
+ blocks: {
+ value: [ block ],
},
+ edits: {},
},
+ initialEdits: {},
+ currentPost: {},
};
- const firstBlockFirstCall = getInserterItems( state, 'block3' );
- const firstBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block3' );
- expect( firstBlockFirstCall ).toBe( firstBlockSecondCall );
- expect( firstBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- 'core/block/1',
- 'core/block/2',
- ] );
+ const content = getEditedPostContent( state );
- const secondBlockFirstCall = getInserterItems( state, 'block4' );
- const secondBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block4' );
- expect( secondBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- 'core/block/1',
- 'core/block/2',
- ] );
- expect( secondBlockSecondCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- ] );
+ expect( content ).toBe( '' );
} );
- it( 'should set isDisabled when a block with `multiple: false` has been used', () => {
+ it( 'returns removep\'d serialization of blocks for single unknown', () => {
+ const unknownBlock = createBlock( 'core/test-freeform', {
+ content: 'foo
',
+ } );
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { clientId: 'block1', name: 'core/test-block-b' },
- },
- attributes: {
- block1: { attribute: {} },
- },
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
+ blocks: {
+ value: [ unknownBlock ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( testBlockBItem.isDisabled ).toBe( true );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'foo' );
} );
- it( 'should give common blocks a low utility', () => {
+ it( 'returns non-removep\'d serialization of blocks for multiple unknown', () => {
+ const firstUnknown = createBlock( 'core/test-freeform', {
+ content: 'foo
',
+ } );
+ const secondUnknown = createBlock( 'core/test-freeform', {
+ content: 'bar
',
+ } );
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [ firstUnknown, secondUnknown ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( testBlockBItem.utility ).toBe( INSERTER_UTILITY_LOW );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'foo
\n\nbar
' );
} );
- it( 'should give used blocks a medium utility and set a frecency', () => {
+ it( 'returns empty string for single unmodified default block', () => {
+ const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
+ blocks: {
+ value: [ defaultBlock ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {
- 'core/test-block-b': { count: 10, time: 1000 },
- },
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const reusableBlock2Item = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( reusableBlock2Item.utility ).toBe( INSERTER_UTILITY_MEDIUM );
- expect( reusableBlock2Item.frecency ).toBe( 2.5 );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( '' );
} );
- it( 'should give contextual blocks a high utility', () => {
+ it( 'should not return empty string for modified default block', () => {
+ const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
+ blocks: {
+ value: [ {
+ ...defaultBlock,
attributes: {
- block1: { attribute: {} },
+ ...defaultBlock.attributes,
+ modified: true,
},
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
+ } ],
},
+ edits: {},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state, 'block1' );
- const testBlockCItem = items.find( ( item ) => item.id === 'core/test-block-c' );
- expect( testBlockCItem.utility ).toBe( INSERTER_UTILITY_HIGH );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( '' );
} );
} );
@@ -5289,92 +2338,12 @@ describe( 'selectors', () => {
} );
} );
- describe( 'isValidTemplate', () => {
- it( 'should return true if template is valid', () => {
- const state = {
- template: { isValid: true },
- };
-
- expect( isValidTemplate( state ) ).toBe( true );
- } );
-
- it( 'should return false if template is not valid', () => {
- const state = {
- template: { isValid: false },
- };
-
- expect( isValidTemplate( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getTemplate', () => {
- it( 'should return the template object', () => {
- const template = [];
- const state = {
- settings: { template },
- };
-
- expect( getTemplate( state ) ).toBe( template );
- } );
- } );
-
- describe( 'getTemplateLock', () => {
- it( 'should return the general template lock if no clientId was set', () => {
- const state = {
- settings: { templateLock: 'all' },
- };
-
- expect( getTemplateLock( state ) ).toBe( 'all' );
- } );
-
- it( 'should return null if the specified clientId was not found ', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- templateLock: 'insert',
- },
- },
- };
-
- expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
- } );
-
- it( 'should return null if template lock was not set on the specified block', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- test: 'tes1t',
- },
- },
- };
-
- expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
- } );
-
- it( 'should return the template lock for the specified clientId', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- templateLock: 'insert',
- },
- },
- };
-
- expect( getTemplateLock( state, 'chicken' ) ).toBe( 'insert' );
- } );
- } );
-
describe( 'isPermalinkEditable', () => {
it( 'should be false if there is no permalink', () => {
const state = {
currentPost: { permalink_template: '' },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5386,9 +2355,7 @@ describe( 'selectors', () => {
const state = {
currentPost: { permalink_template: 'http://foo.test/bar/%baz%/' },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5400,9 +2367,7 @@ describe( 'selectors', () => {
const state = {
currentPost: { permalink_template: 'http://foo.test/bar/%postname%/' },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5414,9 +2379,7 @@ describe( 'selectors', () => {
const state = {
currentPost: { permalink_template: 'http://foo.test/bar/%pagename%/' },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5431,9 +2394,7 @@ describe( 'selectors', () => {
const state = {
currentPost: { permalink_template: url },
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5448,9 +2409,7 @@ describe( 'selectors', () => {
slug: 'baz',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5461,9 +2420,7 @@ describe( 'selectors', () => {
it( 'should return null if the post has no permalink template', () => {
const state = {
currentPost: {},
- editor: {
- present: {},
- },
+ editor: {},
};
expect( getPermalink( state ) ).toBeNull();
@@ -5483,9 +2440,7 @@ describe( 'selectors', () => {
slug: 'baz',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5504,9 +2459,7 @@ describe( 'selectors', () => {
slug: 'baz',
},
editor: {
- present: {
- edits: {},
- },
+ edits: {},
},
initialEdits: {},
};
@@ -5517,42 +2470,13 @@ describe( 'selectors', () => {
it( 'should return null if the post has no permalink template', () => {
const state = {
currentPost: {},
- editor: {
- present: {},
- },
+ editor: {},
};
expect( getPermalinkParts( state ) ).toBeNull();
} );
} );
- describe( 'getBlockListSettings', () => {
- it( 'should return the settings of a block', () => {
- const state = {
- blockListSettings: {
- chicken: {
- setting1: false,
- },
- ribs: {
- setting2: true,
- },
- },
- };
-
- expect( getBlockListSettings( state, 'chicken' ) ).toEqual( {
- setting1: false,
- } );
- } );
-
- it( 'should return undefined if settings for the block don’t exist', () => {
- const state = {
- blockListSettings: {},
- };
-
- expect( getBlockListSettings( state, 'chicken' ) ).toBe( undefined );
- } );
- } );
-
describe( 'canUserUseUnfilteredHTML', () => {
it( 'should return true if the _links object contains the property wp:action-unfiltered-html', () => {
const state = {