Skip to content

Commit

Permalink
Add unit tests for useBlockSync hook
Browse files Browse the repository at this point in the history
  • Loading branch information
noahtallen committed May 12, 2020
1 parent 8bb2355 commit 7c31ce3
Showing 1 changed file with 368 additions and 0 deletions.
368 changes: 368 additions & 0 deletions packages/block-editor/src/components/provider/test/use-block-sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
/**
* External dependencies
*/
import { create, act } from 'react-test-renderer';

/**
* Internal dependencies
*/
import useBlockSync from '../use-block-sync';
import withRegistryProvider from '../with-registry-provider';
import * as blockEditorActions from '../../../store/actions';

const TestWrapper = withRegistryProvider( ( props ) => {
if ( props.setRegistry ) {
props.setRegistry( props.registry );
}
useBlockSync( props );
return <p>Test.</p>;
} );

describe( 'useBlockSync hook', () => {
afterEach( () => {
jest.clearAllMocks();
} );

it( 'resets the block-editor blocks when the controll value changes', async () => {
const fakeBlocks = [];
const resetBlocks = jest.spyOn( blockEditorActions, 'resetBlocks' );
const replaceInnerBlocks = jest.spyOn(
blockEditorActions,
'replaceInnerBlocks'
);
const onChange = jest.fn();
const onInput = jest.fn();

let root;
await act( async () => {
root = create(
<TestWrapper
value={ fakeBlocks }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

// Reset blocks should be called on mount.
expect( onChange ).not.toHaveBeenCalled();
expect( onInput ).not.toHaveBeenCalled();
expect( replaceInnerBlocks ).not.toHaveBeenCalled();
expect( resetBlocks ).toHaveBeenCalledWith( fakeBlocks );

const testBlocks = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];
await act( async () => {
root.update(
<TestWrapper
value={ testBlocks }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

// Reset blocks should be called when the incoming value changes.
expect( onChange ).not.toHaveBeenCalled();
expect( onInput ).not.toHaveBeenCalled();
expect( replaceInnerBlocks ).not.toHaveBeenCalled();
expect( resetBlocks ).toHaveBeenCalledWith( testBlocks );
} );

it( 'replaces the inner blocks of a block when the control value changes if a clientId is passed', async () => {
const fakeBlocks = [];
const replaceInnerBlocks = jest.spyOn(
blockEditorActions,
'replaceInnerBlocks'
);
const resetBlocks = jest.spyOn( blockEditorActions, 'resetBlocks' );
const onChange = jest.fn();
const onInput = jest.fn();

let root;
await act( async () => {
root = create(
<TestWrapper
clientId="test"
value={ fakeBlocks }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

expect( resetBlocks ).not.toHaveBeenCalled();
expect( onChange ).not.toHaveBeenCalled();
expect( onInput ).not.toHaveBeenCalled();
expect( replaceInnerBlocks ).toHaveBeenCalledWith(
'test', // It should use the given client ID.
fakeBlocks, // It should use the controlled blocks value.
false // It shoudl not update the selection state.
);

const testBlocks = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];
await act( async () => {
root.update(
<TestWrapper
clientId="test"
value={ testBlocks }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

// Reset blocks should be called when the incoming value changes.
expect( onChange ).not.toHaveBeenCalled();
expect( onInput ).not.toHaveBeenCalled();
expect( resetBlocks ).not.toHaveBeenCalled();
expect( replaceInnerBlocks ).toHaveBeenCalledWith(
'test',
testBlocks,
false
);
} );

it( 'does not add the controlled blocks to the block-editor store if the store already contains them', async () => {
const replaceInnerBlocks = jest.spyOn(
blockEditorActions,
'replaceInnerBlocks'
);
const onChange = jest.fn();
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];
let root;
let registry;
const setRegistry = ( reg ) => {
registry = reg;
};
await act( async () => {
root = create(
<TestWrapper
setRegistry={ setRegistry }
clientId="test"
value={ value1 }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

registry
.dispatch( 'core/block-editor' )
.updateBlockAttributes( 'a', { foo: 2 } );

const newBlockValue = registry
.select( 'core/block-editor' )
.getBlocks( 'test' );
replaceInnerBlocks.mockClear();

// Assert that the reference has changed so that the side effect will be
// triggered once more.
expect( newBlockValue ).not.toBe( value1 );

await act( async () => {
root.update(
<TestWrapper
clientId="test"
value={ newBlockValue }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

// replaceInnerBlocks should not be called when the controlling
// block value is the same as what already exists in the store.
expect( replaceInnerBlocks ).not.toHaveBeenCalled();
} );

it( 'sets a block as an inner block controller if a clientId is provided', async () => {
const setAsController = jest.spyOn(
blockEditorActions,
'setHasControlledInnerBlocks'
);

await act( async () => {
create(
<TestWrapper
clientId="test"
value={ [] }
onChange={ jest.fn() }
onInput={ jest.fn() }
/>
);
} );
expect( setAsController ).toHaveBeenCalledWith( 'test', true );
} );

it( 'calls onInput when a non-persistent block change occurs', async () => {
const onChange = jest.fn();
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];
let registry;
const setRegistry = ( reg ) => {
registry = reg;
};
await act( async () => {
create(
<TestWrapper
setRegistry={ setRegistry }
clientId="test"
value={ value1 }
onChange={ onChange }
onInput={ onInput }
/>
);
} );
onChange.mockClear();
onInput.mockClear();

// Create a non-persistent change.
registry
.dispatch( 'core/block-editor' )
.__unstableMarkNextChangeAsNotPersistent();
registry
.dispatch( 'core/block-editor' )
.updateBlockAttributes( 'a', { foo: 2 } );

expect( onInput ).toHaveBeenCalledWith(
[ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ],
{ selectionEnd: {}, selectionStart: {} }
);
expect( onChange ).not.toHaveBeenCalled();
} );

it( 'calls onChange if a persistent change occurs', async () => {
const onChange = jest.fn();
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];
let registry;
const setRegistry = ( reg ) => {
registry = reg;
};
await act( async () => {
create(
<TestWrapper
setRegistry={ setRegistry }
clientId="test"
value={ value1 }
onChange={ onChange }
onInput={ onInput }
/>
);
} );
onChange.mockClear();
onInput.mockClear();

// Create a persistent change.
registry
.dispatch( 'core/block-editor' )
.updateBlockAttributes( 'a', { foo: 2 } );

expect( onChange ).toHaveBeenCalledWith(
[ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ],
{ selectionEnd: {}, selectionStart: {} }
);
expect( onInput ).not.toHaveBeenCalled();
} );

it( 'avoids updating the parent if there is a pending incoming change', async () => {
const replaceInnerBlocks = jest.spyOn(
blockEditorActions,
'replaceInnerBlocks'
);

const onChange = jest.fn();
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];

await act( async () => {
create(
<TestWrapper
clientId="test"
value={ value1 }
onChange={ onChange }
onInput={ onInput }
/>
);
} );
onChange.mockClear();
onInput.mockClear();
replaceInnerBlocks.mockClear();

await act( async () => {
create(
<TestWrapper
clientId="test"
value={ [] }
onChange={ onChange }
onInput={ onInput }
/>
);
} );

expect( replaceInnerBlocks ).toHaveBeenCalledWith( 'test', [], false );
expect( onChange ).not.toHaveBeenCalled();
expect( onInput ).not.toHaveBeenCalled();
} );

it( 'avoids updating the block-editor store if there is a pending outgoint change', async () => {
const replaceInnerBlocks = jest.spyOn(
blockEditorActions,
'replaceInnerBlocks'
);

const onChange = jest.fn();
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
];

let registry;
const setRegistry = ( reg ) => {
registry = reg;
};
await act( async () => {
create(
<TestWrapper
setRegistry={ setRegistry }
clientId="test"
value={ value1 }
onChange={ onChange }
onInput={ onInput }
/>
);
} );
onChange.mockClear();
onInput.mockClear();
replaceInnerBlocks.mockClear();

registry
.dispatch( 'core/block-editor' )
.updateBlockAttributes( 'a', { foo: 2 } );

expect( replaceInnerBlocks ).not.toHaveBeenCalled();
expect( onChange ).toHaveBeenCalledWith(
[ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ],
{ selectionEnd: {}, selectionStart: {} }
);
expect( onInput ).not.toHaveBeenCalled();
} );
} );

0 comments on commit 7c31ce3

Please sign in to comment.