diff --git a/editor/components/block-switcher/index.js b/editor/components/block-switcher/index.js
index add041a644510c..2dabeb6ed44703 100644
--- a/editor/components/block-switcher/index.js
+++ b/editor/components/block-switcher/index.js
@@ -24,7 +24,7 @@ import { getBlock } from '../../store/selectors';
*/
const { DOWN } = keycodes;
-function BlockSwitcher( { blocks, onTransform, isLocked } ) {
+export function BlockSwitcher( { blocks, onTransform, isLocked } ) {
const allowedBlocks = getPossibleBlockTransformations( blocks );
if ( isLocked || ! allowedBlocks.length ) {
diff --git a/editor/components/block-switcher/multi-blocks-switcher.js b/editor/components/block-switcher/multi-blocks-switcher.js
index 42a7b14f64882d..2772aa64baaa35 100644
--- a/editor/components/block-switcher/multi-blocks-switcher.js
+++ b/editor/components/block-switcher/multi-blocks-switcher.js
@@ -10,7 +10,7 @@ import './style.scss';
import BlockSwitcher from './';
import { getMultiSelectedBlockUids } from '../../store/selectors';
-function MultiBlocksSwitcher( { isMultiBlockSelection, selectedBlockUids } ) {
+export function MultiBlocksSwitcher( { isMultiBlockSelection, selectedBlockUids } ) {
if ( ! isMultiBlockSelection ) {
return null;
}
diff --git a/editor/components/block-switcher/test/__snapshots__/index.js.snap b/editor/components/block-switcher/test/__snapshots__/index.js.snap
new file mode 100644
index 00000000000000..3c19baa2d4fd1f
--- /dev/null
+++ b/editor/components/block-switcher/test/__snapshots__/index.js.snap
@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`BlockSwitcher should render switcher with blocks 1`] = `
+
+`;
diff --git a/editor/components/block-switcher/test/__snapshots__/multi-blocks-switcher.js.snap b/editor/components/block-switcher/test/__snapshots__/multi-blocks-switcher.js.snap
new file mode 100644
index 00000000000000..d4fa9836f70171
--- /dev/null
+++ b/editor/components/block-switcher/test/__snapshots__/multi-blocks-switcher.js.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MultiBlocksSwitcher should return a BlockSwitcher element matching the snapshot. 1`] = `
+
+`;
diff --git a/editor/components/block-switcher/test/index.js b/editor/components/block-switcher/test/index.js
new file mode 100644
index 00000000000000..156f419a72f22b
--- /dev/null
+++ b/editor/components/block-switcher/test/index.js
@@ -0,0 +1,158 @@
+/**
+ * External dependencies
+ */
+import { shallow } from 'enzyme';
+
+/**
+ * WordPress dependencies
+ */
+import { registerCoreBlocks } from '@wordpress/blocks';
+import { keycodes } from '@wordpress/utils';
+
+/**
+ * Internal dependencies
+ */
+import { BlockSwitcher } from '../';
+
+const { DOWN } = keycodes;
+
+describe( 'BlockSwitcher', () => {
+ const headingBlock1 = {
+ attributes: {
+ content: [ 'How are you?' ],
+ nodeName: 'H2',
+ },
+ isValid: true,
+ name: 'core/heading',
+ originalContent: '
How are you?
',
+ uid: 'a1303fd6-3e60-4fff-a770-0e0ea656c5b9',
+ };
+
+ const textBlock = {
+ attributes: {
+ content: [ 'I am great!' ],
+ nodeName: 'P',
+ },
+ isValid: true,
+ name: 'core/text',
+ originalContent: 'I am great!
',
+ uid: 'b1303fdb-3e60-43faf-a770-2e1ea656c5b8',
+ };
+
+ const headingBlock2 = {
+ attributes: {
+ content: [ 'I am the greatest!' ],
+ nodeName: 'H3',
+ },
+ isValid: true,
+ name: 'core/text',
+ originalContent: 'I am the greatest!
',
+ uid: 'c2403fd2-4e63-5ffa-b71c-1e0ea656c5b0',
+ };
+
+ beforeAll( () => {
+ registerCoreBlocks();
+ } );
+
+ test( 'should not render block switcher without blocks', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper.html() ).toBeNull();
+ } );
+
+ test( 'should render switcher with blocks', () => {
+ const blocks = [
+ headingBlock1,
+ ];
+ const wrapper = shallow( );
+
+ expect( wrapper ).toMatchSnapshot();
+ } );
+
+ test( 'should not render block switcher with multi block of different types.', () => {
+ const blocks = [
+ headingBlock1,
+ textBlock,
+ ];
+ const wrapper = shallow( );
+
+ expect( wrapper.html() ).toBeNull();
+ } );
+
+ test( 'should not render a component when the multi selected types of blocks match.', () => {
+ const blocks = [
+ headingBlock1,
+ headingBlock2,
+ ];
+ const wrapper = shallow( );
+
+ expect( wrapper.html() ).toBeNull();
+ } );
+
+ describe( 'Dropdown', () => {
+ const blocks = [
+ headingBlock1,
+ ];
+
+ const onTransformStub = jest.fn();
+ const getDropdown = () => {
+ const blockSwitcher = shallow( );
+ return blockSwitcher.find( 'Dropdown' );
+ };
+
+ test( 'should dropdown exist', () => {
+ expect( getDropdown() ).toHaveLength( 1 );
+ } );
+
+ describe( '.renderToggle', () => {
+ const onToggleStub = jest.fn();
+ const mockKeyDown = {
+ preventDefault: () => {},
+ stopPropagation: () => {},
+ keyCode: DOWN,
+ };
+
+ afterEach( () => {
+ onToggleStub.mockReset();
+ } );
+
+ test( 'should simulate a keydown event, which should call onToggle and open transform toggle.', () => {
+ const toggleClosed = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: false } ) );
+ const iconButtonClosed = toggleClosed.find( 'IconButton' );
+
+ iconButtonClosed.simulate( 'keydown', mockKeyDown );
+
+ expect( onToggleStub ).toHaveBeenCalledTimes( 1 );
+ } );
+
+ test( 'should simulate a click event, which should call onToggle.', () => {
+ const toggleOpen = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: true } ) );
+ const iconButtonOpen = toggleOpen.find( 'IconButton' );
+
+ iconButtonOpen.simulate( 'keydown', mockKeyDown );
+
+ expect( onToggleStub ).toHaveBeenCalledTimes( 0 );
+ } );
+ } );
+
+ describe( '.renderContent', () => {
+ const onCloseStub = jest.fn();
+
+ const getIconButtons = () => {
+ const content = shallow( getDropdown().props().renderContent( { onClose: onCloseStub } ) );
+ return content.find( 'IconButton' );
+ };
+
+ test( 'should create the iconButtons for the chosen block. A heading block will have 3 items', () => {
+ expect( getIconButtons() ).toHaveLength( 3 );
+ } );
+
+ test( 'should simulate the click event by closing the switcher and causing a block transform on iconButtons.', () => {
+ getIconButtons().first().simulate( 'click' );
+
+ expect( onCloseStub ).toHaveBeenCalledTimes( 1 );
+ expect( onTransformStub ).toHaveBeenCalledTimes( 1 );
+ } );
+ } );
+ } );
+} );
diff --git a/editor/components/block-switcher/test/multi-blocks-switcher.js b/editor/components/block-switcher/test/multi-blocks-switcher.js
new file mode 100644
index 00000000000000..37935f47788f63
--- /dev/null
+++ b/editor/components/block-switcher/test/multi-blocks-switcher.js
@@ -0,0 +1,42 @@
+/**
+ * External dependencies
+ */
+import { shallow } from 'enzyme';
+
+/**
+ * Internal dependencies
+ */
+import { MultiBlocksSwitcher } from '../multi-blocks-switcher';
+
+describe( 'MultiBlocksSwitcher', () => {
+ test( 'should return null when the selection is not a multi block selection.', () => {
+ const isMultiBlockSelection = false;
+ const selectedBlockUids = [
+ 'an-uid',
+ ];
+ const wrapper = shallow(
+
+ );
+
+ expect( wrapper.html() ).toBeNull();
+ } );
+
+ test( 'should return a BlockSwitcher element matching the snapshot.', () => {
+ const isMultiBlockSelection = true;
+ const selectedBlockUids = [
+ 'an-uid',
+ 'another-uid',
+ ];
+ const wrapper = shallow(
+
+ );
+
+ expect( wrapper ).toMatchSnapshot();
+ } );
+} );