From 2857e99ba04c612518c5031914748185f19534b5 Mon Sep 17 00:00:00 2001 From: dufresnesteven Date: Mon, 3 Feb 2020 12:48:20 +0900 Subject: [PATCH 01/11] Block Directory: Add error messages inline. --- .../downloadable-block-header/index.js | 8 +- .../downloadable-block-header/test/index.js | 30 ++- .../downloadable-block-list-item/index.js | 4 +- .../downloadable-block-notice/index.js | 38 ++++ .../downloadable-block-notice/style.scss | 7 + .../test/fixtures/index.js | 3 + .../downloadable-block-notice/test/index.js | 59 ++++++ .../downloadable-blocks-list/index.js | 193 +++++++++++------- .../test/fixtures/index.js | 23 +++ .../downloadable-blocks-list/test/index.js | 80 ++++++++ packages/block-directory/src/store/actions.js | 42 ++++ .../block-directory/src/store/constants.js | 13 ++ packages/block-directory/src/store/reducer.js | 41 +++- .../block-directory/src/store/selectors.js | 22 ++ .../block-directory/src/store/test/actions.js | 147 +++++++++++++ packages/block-directory/src/style.scss | 1 + 16 files changed, 624 insertions(+), 87 deletions(-) create mode 100644 packages/block-directory/src/components/downloadable-block-notice/index.js create mode 100644 packages/block-directory/src/components/downloadable-block-notice/style.scss create mode 100644 packages/block-directory/src/components/downloadable-block-notice/test/fixtures/index.js create mode 100644 packages/block-directory/src/components/downloadable-block-notice/test/index.js create mode 100644 packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js create mode 100644 packages/block-directory/src/components/downloadable-blocks-list/test/index.js create mode 100644 packages/block-directory/src/store/constants.js create mode 100644 packages/block-directory/src/store/test/actions.js diff --git a/packages/block-directory/src/components/downloadable-block-header/index.js b/packages/block-directory/src/components/downloadable-block-header/index.js index 0c37daef7bd65f..ca1b03c5e98da7 100644 --- a/packages/block-directory/src/components/downloadable-block-header/index.js +++ b/packages/block-directory/src/components/downloadable-block-header/index.js @@ -15,6 +15,7 @@ function DownloadableBlockHeader( { title, rating, ratingCount, + isLoading, onClick, } ) { return ( @@ -45,12 +46,15 @@ function DownloadableBlockHeader( { ); diff --git a/packages/block-directory/src/components/downloadable-block-header/test/index.js b/packages/block-directory/src/components/downloadable-block-header/test/index.js index 96ba6247170cb9..8ccff037487534 100644 --- a/packages/block-directory/src/components/downloadable-block-header/test/index.js +++ b/packages/block-directory/src/components/downloadable-block-header/test/index.js @@ -7,6 +7,7 @@ import { shallow } from 'enzyme'; * WordPress dependencies */ import { BlockIcon } from '@wordpress/block-editor'; +import { Button } from '@wordpress/components'; /** * Internal dependencies @@ -14,14 +15,15 @@ import { BlockIcon } from '@wordpress/block-editor'; import DownloadableBlockHeader from '../index'; import { pluginWithImg, pluginWithIcon } from './fixtures'; -const getContainer = ( { icon, title, rating, ratingCount } ) => { +const getContainer = ( { icon, title, rating, ratingCount }, onClick = jest.fn(), isLoading = false ) => { return shallow( {} } + onClick={ onClick } title={ title } rating={ rating } ratingCount={ ratingCount } + isLoading={ isLoading } /> ); }; @@ -50,4 +52,28 @@ describe( 'DownloadableBlockHeader', () => { expect( wrapper.find( BlockIcon ) ).toHaveLength( 1 ); } ); } ); + + describe( 'user interaction', () => { + test( 'should trigger the onClick function', () => { + const onClickMock = jest.fn(); + const wrapper = getContainer( pluginWithIcon, onClickMock ); + const event = { + preventDefault: jest.fn(), + }; + wrapper.find( Button ).simulate( 'click', event ); + expect( onClickMock ).toHaveBeenCalledTimes( 1 ); + expect( event.preventDefault ).toHaveBeenCalled(); + } ); + + test( 'should not trigger the onClick function if loading', () => { + const onClickMock = jest.fn(); + const wrapper = getContainer( pluginWithIcon, onClickMock, true ); + const event = { + preventDefault: jest.fn(), + }; + wrapper.find( Button ).simulate( 'click', event ); + expect( event.preventDefault ).toHaveBeenCalled(); + expect( onClickMock ).toHaveBeenCalledTimes( 0 ); + } ); + } ); } ); diff --git a/packages/block-directory/src/components/downloadable-block-list-item/index.js b/packages/block-directory/src/components/downloadable-block-list-item/index.js index 3eb69eba7d2d70..8ff33e5cabb02a 100644 --- a/packages/block-directory/src/components/downloadable-block-list-item/index.js +++ b/packages/block-directory/src/components/downloadable-block-list-item/index.js @@ -5,7 +5,7 @@ import DownloadableBlockHeader from '../downloadable-block-header'; import DownloadableBlockAuthorInfo from '../downloadable-block-author-info'; import DownloadableBlockInfo from '../downloadable-block-info'; -function DownloadableBlockListItem( { item, onClick } ) { +function DownloadableBlockListItem( { item, onClick, isLoading, children } ) { const { icon, title, @@ -29,9 +29,11 @@ function DownloadableBlockListItem( { item, onClick } ) { title={ title } rating={ rating } ratingCount={ ratingCount } + isLoading={ isLoading } />
+ { children } { + if ( ! errorNotices[ block.id ] ) { + return null; + } + + // A Failed install is the default error as its the first step + let copy = __( 'Block could not be added.' ); + + if ( errorNotices[ block.id ] === DOWNLOAD_ERROR_NOTICE_ID ) { + copy = __( 'Block could not be added. There is a problem with the block.' ); + } + + return ( + + + { copy } + + + + ); +}; + +export default DownloadableBlockNotice; diff --git a/packages/block-directory/src/components/downloadable-block-notice/style.scss b/packages/block-directory/src/components/downloadable-block-notice/style.scss new file mode 100644 index 00000000000000..3b090a5d7a3538 --- /dev/null +++ b/packages/block-directory/src/components/downloadable-block-notice/style.scss @@ -0,0 +1,7 @@ +.block-directory-downloadable-blocks-notice { + margin: 0; +} + +.block-directory-downloadable-blocks-notice-content { + padding-right: 12px; +} diff --git a/packages/block-directory/src/components/downloadable-block-notice/test/fixtures/index.js b/packages/block-directory/src/components/downloadable-block-notice/test/fixtures/index.js new file mode 100644 index 00000000000000..c91c7b0c586958 --- /dev/null +++ b/packages/block-directory/src/components/downloadable-block-notice/test/fixtures/index.js @@ -0,0 +1,3 @@ +export const plugin = { + id: 'boxer-block', +}; diff --git a/packages/block-directory/src/components/downloadable-block-notice/test/index.js b/packages/block-directory/src/components/downloadable-block-notice/test/index.js new file mode 100644 index 00000000000000..3406885eca4e24 --- /dev/null +++ b/packages/block-directory/src/components/downloadable-block-notice/test/index.js @@ -0,0 +1,59 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * WordPress dependencies + */ +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import DownloadableBlockNotice from '../index'; +import { plugin } from './fixtures'; + +import { INSTALL_ERROR_NOTICE_ID } from '../../../store/constants'; + +const getContainer = ( { block, onClick = jest.fn(), errorNotices = {} } ) => { + return shallow( + + ); +}; + +describe( 'DownloadableBlockNotice', () => { + describe( 'Rendering', () => { + it( 'should return null when there are no error notices', () => { + const wrapper = getContainer( { block: plugin } ); + expect( wrapper.isEmptyRender() ).toBe( true ); + } ); + + it( 'should return something when there are error notices', () => { + const errorNotices = { + [ plugin.id ]: INSTALL_ERROR_NOTICE_ID, + }; + const wrapper = getContainer( { block: plugin, errorNotices } ); + expect( wrapper.length ).toBeGreaterThan( 0 ); + } ); + } ); + + describe( 'Behavior', () => { + it( 'should trigger the callback on button click', () => { + const errorNotices = { + [ plugin.id ]: INSTALL_ERROR_NOTICE_ID, + }; + + const onClick = jest.fn(); + const wrapper = getContainer( { block: plugin, onClick, errorNotices } ); + + wrapper.find( Button ).simulate( 'click', { event: {} } ); + + expect( onClick ).toHaveBeenCalledTimes( 1 ); + } ); + } ); +} ); diff --git a/packages/block-directory/src/components/downloadable-blocks-list/index.js b/packages/block-directory/src/components/downloadable-blocks-list/index.js index b63c40d34871f3..35e3183d2a0937 100644 --- a/packages/block-directory/src/components/downloadable-blocks-list/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-list/index.js @@ -6,28 +6,60 @@ import { noop } from 'lodash'; /** * WordPress dependencies */ -import { - getBlockMenuDefaultClassName, - unregisterBlockType, -} from '@wordpress/blocks'; -import { withDispatch } from '@wordpress/data'; +import { getBlockMenuDefaultClassName } from '@wordpress/blocks'; +import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import DownloadableBlockListItem from '../downloadable-block-list-item'; +import DownloadableBlockNotice from '../downloadable-block-notice'; +import { + DOWNLOAD_ERROR_NOTICE_ID, + INSTALL_ERROR_NOTICE_ID, +} from '../../store/constants'; + +/** + * Returns either installAndDownload or download function. + * + * @param {Object} item The block item. + * @param {Object} errorNotices An object with errors. Ie: "my-block/block : block-download-error" + * @param {Function} installAndDownload The function that installs and downloads the block. + * @param {Function} download The function that downloads the block. + * + * @return {Function} Function to continue install process. + */ +const getNoticeCallback = ( + item, + errorNotices, + installAndDownload, + download +) => { + // We don't want to try installing again, the API will throw an install error + if ( + errorNotices[ item.id ] && + errorNotices[ item.id ] === DOWNLOAD_ERROR_NOTICE_ID + ) { + return download; + } -const DOWNLOAD_ERROR_NOTICE_ID = 'block-download-error'; -const INSTALL_ERROR_NOTICE_ID = 'block-install-error'; + return installAndDownload; +}; -function DownloadableBlocksList( { +export function DownloadableBlocksList( { items, onHover = noop, children, - downloadAndInstallBlock, + isLoading, + errorNotices, + installAndDownload, + download, } ) { + if ( ! items.length ) { + return null; + } + return ( /* * Disable reason: The `list` ARIA role is redundant but @@ -35,14 +67,21 @@ function DownloadableBlocksList( { */ /* eslint-disable jsx-a11y/no-redundant-roles */
    - { items && - items.map( ( item ) => ( + { items.map( ( item ) => { + const callBack = getNoticeCallback( + item, + errorNotices, + installAndDownload, + download + ); + + return ( { - downloadAndInstallBlock( item ); + callBack( item ); onHover( null ); } } onFocus={ () => onHover( item ) } @@ -50,8 +89,16 @@ function DownloadableBlocksList( { onMouseLeave={ () => onHover( null ) } onBlur={ () => onHover( null ) } item={ item } - /> - ) ) } + isLoading={ isLoading } + > + + + ); + } ) } { children }
/* eslint-enable jsx-a11y/no-redundant-roles */ @@ -59,79 +106,67 @@ function DownloadableBlocksList( { } export default compose( - withDispatch( ( dispatch, props ) => { - const { installBlock, downloadBlock } = dispatch( + withSelect( ( select ) => { + const { getErrorNotices, isInstalling } = select( 'core/block-directory' ); - const { createErrorNotice, removeNotice } = dispatch( 'core/notices' ); - const { removeBlocks } = dispatch( 'core/block-editor' ); - const { onSelect } = props; + + const errorNotices = getErrorNotices(); + const isLoading = isInstalling(); return { - downloadAndInstallBlock: ( item ) => { - const onDownloadError = () => { - createErrorNotice( __( 'Block previews can’t load.' ), { - id: DOWNLOAD_ERROR_NOTICE_ID, - actions: [ - { - label: __( 'Retry' ), - onClick: () => { - removeNotice( DOWNLOAD_ERROR_NOTICE_ID ); - downloadBlock( - item, - onSuccess, - onDownloadError - ); - }, - }, - ], - } ); - }; + errorNotices, + isLoading, + }; + } ), + withDispatch( ( dispatch, props ) => { + const { + downloadBlock, + installBlock, + setErrorNotice, + clearErrorNotice, + setIsInstalling, + } = dispatch( 'core/block-directory' ); + const { onSelect } = props; + + const download = ( item ) => { + clearErrorNotice( item.id ); + setIsInstalling( true ); + + const onDownloadError = () => { + setErrorNotice( item.id, DOWNLOAD_ERROR_NOTICE_ID ); + setIsInstalling( false ); + }; + + const onDownloadSuccess = () => { + onSelect( item ); + setIsInstalling( false ); + }; + + downloadBlock( item, onDownloadSuccess, onDownloadError ); + }; + + const install = ( item, onSuccess ) => { + clearErrorNotice( item.id ); + setIsInstalling( true ); + + const onInstallBlockError = () => { + setErrorNotice( item.id, INSTALL_ERROR_NOTICE_ID ); + setIsInstalling( false ); + }; + installBlock( item, onSuccess, onInstallBlockError ); + }; + + return { + installAndDownload( item ) { const onSuccess = () => { - const createdBlock = onSelect( item ); - - const onInstallBlockError = () => { - createErrorNotice( - __( "Block previews can't install." ), - { - id: INSTALL_ERROR_NOTICE_ID, - actions: [ - { - label: __( 'Retry' ), - onClick: () => { - removeNotice( - INSTALL_ERROR_NOTICE_ID - ); - installBlock( - item, - noop, - onInstallBlockError - ); - }, - }, - { - label: __( 'Remove' ), - onClick: () => { - removeNotice( - INSTALL_ERROR_NOTICE_ID - ); - removeBlocks( - createdBlock.clientId - ); - unregisterBlockType( item.name ); - }, - }, - ], - } - ); - }; - - installBlock( item, noop, onInstallBlockError ); + download( item ); }; - downloadBlock( item, onSuccess, onDownloadError ); + install( item, onSuccess ); }, + download, }; } ) )( DownloadableBlocksList ); diff --git a/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js b/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js new file mode 100644 index 00000000000000..f6624ac12802be --- /dev/null +++ b/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js @@ -0,0 +1,23 @@ +export const plugin = { + name: 'boxer/boxer', + title: 'Boxer', + description: 'Boxer is a Block that puts your WordPress posts into boxes on a page.', + id: 'boxer-block', + icon: 'block-default', + rating: 5, + rating_count: 1, + active_installs: 0, + author_block_rating: 5, + author_block_count: '1', + author: 'CK Lee', + assets: [ + 'http://plugins.svn.wordpress.org/boxer-block/trunk/build/index.js', + 'http://plugins.svn.wordpress.org/boxer-block/trunk/build/view.js', + ], + humanized_updated: '3 months ago', +}; + +export const items = [ + plugin, + { ...plugin, name: 'my-block/test', id: 'my-block' }, +]; diff --git a/packages/block-directory/src/components/downloadable-blocks-list/test/index.js b/packages/block-directory/src/components/downloadable-blocks-list/test/index.js new file mode 100644 index 00000000000000..b76a90d938e09d --- /dev/null +++ b/packages/block-directory/src/components/downloadable-blocks-list/test/index.js @@ -0,0 +1,80 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * Internal dependencies + */ +import { DownloadableBlocksList } from '../index'; +import DownloadableBlockListItem from '../../downloadable-block-list-item'; +import { items, plugin } from './fixtures'; + +import { DOWNLOAD_ERROR_NOTICE_ID } from '../../../store/constants'; + +const getContainer = ( { + blocks, + selectMock = jest.fn(), + hoverMock = jest.fn(), + isLoading = false, + errorNotices = {}, + installAndDownload = jest.fn(), + download = jest.fn(), +} ) => { + return shallow( + + ); +}; + +describe( 'DownloadableBlocksList', () => { + describe( 'List rendering', () => { + it( 'should render and empty list', () => { + const wrapper = getContainer( { blocks: [] } ); + expect( wrapper.isEmptyRender() ).toBe( true ); + } ); + + it( 'should render plugins items into the list', () => { + const wrapper = getContainer( { blocks: items } ); + + expect( wrapper.find( DownloadableBlockListItem ).length ).toBe( items.length ); + } ); + } ); + describe( 'Behaviour', () => { + it( 'should try to install and download the block plugin', () => { + const installAndDownload = jest.fn(); + const download = jest.fn(); + const errorNotices = {}; + + const wrapper = getContainer( { blocks: [ plugin ], installAndDownload, download, errorNotices } ); + const listItems = wrapper.find( DownloadableBlockListItem ); + + listItems.get( 0 ).props.onClick(); + + expect( installAndDownload ).toHaveBeenCalledTimes( 1 ); + expect( download ).toHaveBeenCalledTimes( 0 ); + } ); + it( 'should try to only download the block plugin to the page', () => { + const installAndDownload = jest.fn(); + const download = jest.fn(); + const errorNotices = { + [ plugin.id ]: DOWNLOAD_ERROR_NOTICE_ID, + }; + + const wrapper = getContainer( { blocks: [ plugin ], installAndDownload, download, errorNotices } ); + const listItems = wrapper.find( DownloadableBlockListItem ); + + listItems.get( 0 ).props.onClick(); + + expect( installAndDownload ).toHaveBeenCalledTimes( 0 ); + expect( download ).toHaveBeenCalledTimes( 1 ); + } ); + } ); +} ); diff --git a/packages/block-directory/src/store/actions.js b/packages/block-directory/src/store/actions.js index 09ba6381b59ae0..deac6f47dd0874 100644 --- a/packages/block-directory/src/store/actions.js +++ b/packages/block-directory/src/store/actions.js @@ -150,3 +150,45 @@ export function removeInstalledBlockType( item ) { item, }; } + +/** + * Returns an action object used to indicate install in progress + * + * @param {boolean} isInstalling Boolean value that tells state whether installation is occurring + * + */ +export function setIsInstalling( isInstalling ) { + return { + type: 'SET_INSTALLING_BLOCK', + isInstalling, + }; +} + +/** + * Sets an error notice string to be displayed to the user + * + * @param {string} blockId The ID of the block plugin. eg: my-block + * @param {string} noticeId The ID of the message used to determine which notice to show. + * + */ +export function setErrorNotice( blockId, noticeId ) { + return { + type: 'SET_ERROR_NOTICE_ID', + blockId, + noticeId, + }; +} + +/** + * Sets the error noticeId to empty for specific block + * + * @param {string} blockId The ID of the block plugin. eg: my-block + * + */ +export function clearErrorNotice( blockId ) { + return { + type: 'SET_ERROR_NOTICE_ID', + blockId, + noticeId: '', + }; +} diff --git a/packages/block-directory/src/store/constants.js b/packages/block-directory/src/store/constants.js new file mode 100644 index 00000000000000..625c780faf399e --- /dev/null +++ b/packages/block-directory/src/store/constants.js @@ -0,0 +1,13 @@ +/** + * ID of error when downloading block fails + * + * @type {string} + */ +export const DOWNLOAD_ERROR_NOTICE_ID = 'block-download-error'; + +/** + * ID of error when installing block fails + * + * @type {string} + */ +export const INSTALL_ERROR_NOTICE_ID = 'block-install-error'; diff --git a/packages/block-directory/src/store/reducer.js b/packages/block-directory/src/store/reducer.js index c0993c56dfaf64..6dd02788297977 100644 --- a/packages/block-directory/src/store/reducer.js +++ b/packages/block-directory/src/store/reducer.js @@ -28,9 +28,10 @@ export const downloadableBlocks = ( case 'RECEIVE_DOWNLOADABLE_BLOCKS': return { ...state, - results: Object.assign( {}, state.results, { + results: { + ...state.results, [ action.filterValue ]: action.downloadableBlocks, - } ), + }, isRequestingDownloadableBlocks: false, }; } @@ -48,6 +49,7 @@ export const downloadableBlocks = ( export const blockManagement = ( state = { installedBlockTypes: [], + isInstalling: false, }, action ) => { @@ -67,12 +69,17 @@ export const blockManagement = ( ( blockType ) => blockType.name !== action.item.name ), }; + case 'SET_INSTALLING_BLOCK': + return { + ...state, + isInstalling: action.isInstalling, + }; } return state; }; /** - * Reducer returns whether the user can install blocks. + * Reducer returning an array of downloadable blocks. * * @param {Object} state Current state. * @param {Object} action Dispatched action. @@ -87,8 +94,36 @@ export function hasPermission( state = true, action ) { return state; } +/** + * Reducer returning an object of error notices. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export const errorNotices = ( + state = { + notices: {}, + }, + action +) => { + switch ( action.type ) { + case 'SET_ERROR_NOTICE_ID': + return { + ...state, + notices: { + ...state.notices, + [ action.blockId ]: action.noticeId, + }, + }; + } + return state; +}; + export default combineReducers( { downloadableBlocks, blockManagement, hasPermission, + errorNotices, } ); diff --git a/packages/block-directory/src/store/selectors.js b/packages/block-directory/src/store/selectors.js index 7a1778b1cc09a6..e3851b2b8eb41f 100644 --- a/packages/block-directory/src/store/selectors.js +++ b/packages/block-directory/src/store/selectors.js @@ -45,3 +45,25 @@ export function hasInstallBlocksPermission( state ) { export function getInstalledBlockTypes( state ) { return state.blockManagement.installedBlockTypes; } + +/** + * Returns true if application is calling install endpoint. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether its currently installing + */ +export function isInstalling( state ) { + return state.blockManagement.isInstalling; +} + +/** + * Returns the error notices + * + * @param {Object} state Global application state. + * + * @return {Object} Object with error notices. + */ +export function getErrorNotices( state ) { + return state.errorNotices.notices; +} diff --git a/packages/block-directory/src/store/test/actions.js b/packages/block-directory/src/store/test/actions.js new file mode 100644 index 00000000000000..b0621133e93e35 --- /dev/null +++ b/packages/block-directory/src/store/test/actions.js @@ -0,0 +1,147 @@ +/** + * WordPress dependencies + */ +import * as blockFunctions from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { + downloadBlock, + installBlock, +} from '../actions'; +import * as controls from '../controls'; + +const ACTIONS = { + apiFetch: 'API_FETCH', + addInstalledBlockType: 'ADD_INSTALLED_BLOCK_TYPE', + removeInstalledBlockType: 'REMOVE_INSTALLED_BLOCK_TYPE', +}; + +jest.mock( '@wordpress/blocks' ); + +describe( 'actions', () => { + const item = { id: 'block/block', name: 'Test Block' }; + const blockPlugin = { + assets: [ 'http://www.wordpress.org/plugins/fakeasset.js' ], + }; + const getBlockTypeMock = jest.spyOn( blockFunctions, 'getBlockTypes' ); + jest.spyOn( controls, 'apiFetch' ); + jest.spyOn( controls, 'loadAssets' ); + + afterEach( () => { + jest.clearAllMocks(); + } ); + + afterAll( () => { + jest.resetAllMocks(); + } ); + + const callsTheApi = ( generator ) => { + return expect( + generator.next( { success: true } ).value.type, + ).toEqual( ACTIONS.apiFetch ); + }; + + const expectTest = ( hasCall, noCall ) => { + expect( hasCall ).toHaveBeenCalledTimes( 1 ); + expect( noCall ).toHaveBeenCalledTimes( 0 ); + }; + + const expectSuccess = ( onSuccess, onError ) => { + expectTest( onSuccess, onError ); + }; + + const expectError = ( onSuccess, onError ) => { + expectTest( onError, onSuccess ); + }; + + describe( 'downloadBlock', () => { + it( 'should throw error if the plugin has no assets', () => { + const onSuccess = jest.fn(); + const onError = jest.fn(); + + const generator = downloadBlock( { + assets: [], + }, onSuccess, onError ); + + // Move onto the onError callback + generator.next(); + + expectError( onSuccess, onError ); + } ); + + it( 'should call on success function', () => { + const onSuccess = jest.fn(); + const onError = jest.fn(); + + // The block is registered + getBlockTypeMock.mockReturnValue( [ item ] ); + + const generator = downloadBlock( blockPlugin, onSuccess, onError ); + + // Trigger the loading of assets + generator.next(); + + // Trigger the block check via getBlockTypes + generator.next(); + + expectSuccess( onSuccess, onError ); + } ); + + it( 'should call on error when no blocks are returned', () => { + const onSuccess = jest.fn(); + const onError = jest.fn(); + + // The block is not registered + getBlockTypeMock.mockReturnValue( [] ); + + const generator = downloadBlock( blockPlugin, onSuccess, onError ); + + // Trigger the loading of assets + generator.next(); + + //Complete + generator.next(); + + expectError( onSuccess, onError ); + } ); + } ); + + describe( 'installBlock', () => { + it( 'should install a block successfully', () => { + const onSuccess = jest.fn(); + const onError = jest.fn(); + + const generator = installBlock( item, onSuccess, onError ); + + // It triggers API_FETCH that wraps @wordpress/api-fetch + callsTheApi( generator ); + + // It triggers ADD_INSTALLED_BLOCK_TYPE + expect( + generator.next( { success: true } ).value.type, + ).toEqual( ACTIONS.addInstalledBlockType ); + + // Move on to success + generator.next(); + + expectSuccess( onSuccess, onError ); + } ); + + it( 'should trigger error state when error is thrown', () => { + const onSuccess = jest.fn(); + const onError = jest.fn(); + + const generator = installBlock( item, onSuccess, onError ); + + // It triggers API_FETCH that wraps @wordpress/api-fetch + callsTheApi( generator ); + + // Move on to error + generator.next(); + + expectError( onSuccess, onError ); + } ); + } ); +} ); diff --git a/packages/block-directory/src/style.scss b/packages/block-directory/src/style.scss index 14f33e678ede45..e0286f8fc42ae0 100644 --- a/packages/block-directory/src/style.scss +++ b/packages/block-directory/src/style.scss @@ -5,3 +5,4 @@ @import "./components/downloadable-blocks-list/style.scss"; @import "./components/downloadable-blocks-panel/style.scss"; @import "./components/block-ratings/style.scss"; +@import "./components/downloadable-block-notice/style.scss"; From 32d370bdd732a64a0b9cec6d8f254f5257f32850 Mon Sep 17 00:00:00 2001 From: dufresnesteven Date: Mon, 3 Feb 2020 13:50:30 +0900 Subject: [PATCH 02/11] Make the linter happy --- .../downloadable-block-header/test/index.js | 6 ++++- .../downloadable-block-notice/index.js | 20 ++++++++++---- .../downloadable-block-notice/test/index.js | 6 ++++- .../test/fixtures/index.js | 3 ++- .../downloadable-blocks-list/test/index.js | 18 ++++++++++--- .../block-directory/src/store/test/actions.js | 27 ++++++++++--------- 6 files changed, 56 insertions(+), 24 deletions(-) diff --git a/packages/block-directory/src/components/downloadable-block-header/test/index.js b/packages/block-directory/src/components/downloadable-block-header/test/index.js index 8ccff037487534..9c5041f9b08fef 100644 --- a/packages/block-directory/src/components/downloadable-block-header/test/index.js +++ b/packages/block-directory/src/components/downloadable-block-header/test/index.js @@ -15,7 +15,11 @@ import { Button } from '@wordpress/components'; import DownloadableBlockHeader from '../index'; import { pluginWithImg, pluginWithIcon } from './fixtures'; -const getContainer = ( { icon, title, rating, ratingCount }, onClick = jest.fn(), isLoading = false ) => { +const getContainer = ( + { icon, title, rating, ratingCount }, + onClick = jest.fn(), + isLoading = false +) => { return shallow( { let copy = __( 'Block could not be added.' ); if ( errorNotices[ block.id ] === DOWNLOAD_ERROR_NOTICE_ID ) { - copy = __( 'Block could not be added. There is a problem with the block.' ); + copy = __( + 'Block could not be added. There is a problem with the block.' + ); } return ( - + { copy } - diff --git a/packages/block-directory/src/components/downloadable-block-notice/test/index.js b/packages/block-directory/src/components/downloadable-block-notice/test/index.js index 3406885eca4e24..8499bba745e575 100644 --- a/packages/block-directory/src/components/downloadable-block-notice/test/index.js +++ b/packages/block-directory/src/components/downloadable-block-notice/test/index.js @@ -49,7 +49,11 @@ describe( 'DownloadableBlockNotice', () => { }; const onClick = jest.fn(); - const wrapper = getContainer( { block: plugin, onClick, errorNotices } ); + const wrapper = getContainer( { + block: plugin, + onClick, + errorNotices, + } ); wrapper.find( Button ).simulate( 'click', { event: {} } ); diff --git a/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js b/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js index f6624ac12802be..899f7f663bac08 100644 --- a/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-list/test/fixtures/index.js @@ -1,7 +1,8 @@ export const plugin = { name: 'boxer/boxer', title: 'Boxer', - description: 'Boxer is a Block that puts your WordPress posts into boxes on a page.', + description: + 'Boxer is a Block that puts your WordPress posts into boxes on a page.', id: 'boxer-block', icon: 'block-default', rating: 5, diff --git a/packages/block-directory/src/components/downloadable-blocks-list/test/index.js b/packages/block-directory/src/components/downloadable-blocks-list/test/index.js index b76a90d938e09d..f859f20cb7de56 100644 --- a/packages/block-directory/src/components/downloadable-blocks-list/test/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-list/test/index.js @@ -44,7 +44,9 @@ describe( 'DownloadableBlocksList', () => { it( 'should render plugins items into the list', () => { const wrapper = getContainer( { blocks: items } ); - expect( wrapper.find( DownloadableBlockListItem ).length ).toBe( items.length ); + expect( wrapper.find( DownloadableBlockListItem ).length ).toBe( + items.length + ); } ); } ); describe( 'Behaviour', () => { @@ -53,7 +55,12 @@ describe( 'DownloadableBlocksList', () => { const download = jest.fn(); const errorNotices = {}; - const wrapper = getContainer( { blocks: [ plugin ], installAndDownload, download, errorNotices } ); + const wrapper = getContainer( { + blocks: [ plugin ], + installAndDownload, + download, + errorNotices, + } ); const listItems = wrapper.find( DownloadableBlockListItem ); listItems.get( 0 ).props.onClick(); @@ -68,7 +75,12 @@ describe( 'DownloadableBlocksList', () => { [ plugin.id ]: DOWNLOAD_ERROR_NOTICE_ID, }; - const wrapper = getContainer( { blocks: [ plugin ], installAndDownload, download, errorNotices } ); + const wrapper = getContainer( { + blocks: [ plugin ], + installAndDownload, + download, + errorNotices, + } ); const listItems = wrapper.find( DownloadableBlockListItem ); listItems.get( 0 ).props.onClick(); diff --git a/packages/block-directory/src/store/test/actions.js b/packages/block-directory/src/store/test/actions.js index b0621133e93e35..113d40d7b0ef6e 100644 --- a/packages/block-directory/src/store/test/actions.js +++ b/packages/block-directory/src/store/test/actions.js @@ -6,10 +6,7 @@ import * as blockFunctions from '@wordpress/blocks'; /** * Internal dependencies */ -import { - downloadBlock, - installBlock, -} from '../actions'; +import { downloadBlock, installBlock } from '../actions'; import * as controls from '../controls'; const ACTIONS = { @@ -38,9 +35,9 @@ describe( 'actions', () => { } ); const callsTheApi = ( generator ) => { - return expect( - generator.next( { success: true } ).value.type, - ).toEqual( ACTIONS.apiFetch ); + return expect( generator.next( { success: true } ).value.type ).toEqual( + ACTIONS.apiFetch + ); }; const expectTest = ( hasCall, noCall ) => { @@ -61,9 +58,13 @@ describe( 'actions', () => { const onSuccess = jest.fn(); const onError = jest.fn(); - const generator = downloadBlock( { - assets: [], - }, onSuccess, onError ); + const generator = downloadBlock( + { + assets: [], + }, + onSuccess, + onError + ); // Move onto the onError callback generator.next(); @@ -119,9 +120,9 @@ describe( 'actions', () => { callsTheApi( generator ); // It triggers ADD_INSTALLED_BLOCK_TYPE - expect( - generator.next( { success: true } ).value.type, - ).toEqual( ACTIONS.addInstalledBlockType ); + expect( generator.next( { success: true } ).value.type ).toEqual( + ACTIONS.addInstalledBlockType + ); // Move on to success generator.next(); From 2b40e0c543f1adb81158b817c78b5425ed8da7ca Mon Sep 17 00:00:00 2001 From: dufresnesteven Date: Wed, 29 Apr 2020 15:12:42 +0900 Subject: [PATCH 03/11] Place the retry button on its own line. --- .../src/components/downloadable-block-notice/index.js | 4 ++-- .../src/components/downloadable-block-notice/style.scss | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/block-directory/src/components/downloadable-block-notice/index.js b/packages/block-directory/src/components/downloadable-block-notice/index.js index e64aaaeb57be74..d8c710f74eb041 100644 --- a/packages/block-directory/src/components/downloadable-block-notice/index.js +++ b/packages/block-directory/src/components/downloadable-block-notice/index.js @@ -29,9 +29,9 @@ const DownloadableBlockNotice = ( { block, errorNotices, onClick } ) => { isDismissible={ false } className="block-directory-downloadable-blocks-notice" > - +
{ copy } - +