UNPKG

@wordpress/block-editor

Version:
183 lines (170 loc) 5.94 kB
/** * External dependencies */ import { act, advanceAnimationByFrames, fireEvent, initializeEditor, screen, waitForStoreResolvers, within, } from 'test/helpers'; import { fireGestureHandler } from 'react-native-gesture-handler/jest-utils'; import { State } from 'react-native-gesture-handler'; // Touch event type constants have been extracted from original source code, as they are not exported in the package. // Reference: https://github.com/software-mansion/react-native-gesture-handler/blob/90895e5f38616a6be256fceec6c6a391cd9ad7c7/src/TouchEventType.ts export const TouchEventType = { UNDETERMINED: 0, TOUCHES_DOWN: 1, TOUCHES_MOVE: 2, TOUCHES_UP: 3, TOUCHES_CANCELLED: 4, }; const DEFAULT_TOUCH_EVENTS = [ { id: 1, eventType: TouchEventType.TOUCHES_DOWN, x: 0, y: 0, }, ]; /** * @typedef {Object} WPBlockWithLayout * @property {string} name Name of the block (e.g. Paragraph). * @property {string} html HTML content. * @property {Object} layout Layout data. * @property {Object} layout.x X position. * @property {Object} layout.y Y position. * @property {Object} layout.width Width. * @property {Object} layout.height Height. */ /** * Initialize the editor with an array of blocks that include their HTML and layout. * * @param {WPBlockWithLayout[]} blocks Initial blocks. * * @return {import('@testing-library/react-native').RenderAPI} The Testing Library screen. */ export const initializeWithBlocksLayouts = async ( blocks ) => { const initialHtml = blocks.map( ( block ) => block.html ).join( '\n' ); await initializeEditor( { initialHtml } ); const waitPromises = []; const blockListItems = screen.getAllByTestId( 'block-list-item-cell' ); // Check that rendered block list items match expected block count. expect( blockListItems.length ).toBe( blocks.length ); blocks.forEach( ( block, index ) => { const element = blockListItems[ index ]; // "onLayout" event will populate the blocks layouts data. fireEvent( element, 'layout', { nativeEvent: { layout: block.layout }, } ); if ( block.nestedBlocks ) { // Nested blocks are rendered via the FlatList of the inner block list. // In order to render the items of a FlatList, it's required to trigger the // "onLayout" event. Additionally, the call is wrapped over "waitForStoreResolvers" // because the nested blocks might make API requests (e.g. the Gallery block). waitPromises.push( waitForStoreResolvers( () => fireEvent( within( element ).getByTestId( 'block-list-wrapper' ), 'layout', { nativeEvent: { layout: { width: block.layout.width, height: block.layout.height, }, }, } ) ) ); block.nestedBlocks.forEach( ( nestedBlock, nestedIndex ) => { const nestedA11yLabel = new RegExp( `${ nestedBlock.name } Block\\. Row ${ nestedIndex + 1 }` ); const [ nestedElement ] = within( element ).getAllByLabelText( nestedA11yLabel ); fireEvent( nestedElement, 'layout', { nativeEvent: { layout: nestedBlock.layout }, } ); } ); } } ); await Promise.all( waitPromises ); return screen; }; /** * Fires long-press gesture event on a block. * * @param {import('react-test-renderer').ReactTestInstance} block Block test instance. * @param {string} testID Id for querying the draggable trigger element. * @param {Object} [options] Configuration options for the gesture event. * @param {boolean} [options.failed] Determines if the gesture should fail. * @param {number} [options.triggerIndex] In case there are multiple draggable triggers, this specifies the index to use. */ export const fireLongPress = ( block, testID, { failed = false, triggerIndex } = {} ) => { const draggableTrigger = typeof triggerIndex !== 'undefined' ? within( block ).getAllByTestId( testID )[ triggerIndex ] : within( block ).getByTestId( testID ); if ( failed ) { fireGestureHandler( draggableTrigger, [ { state: State.FAILED } ] ); } else { fireGestureHandler( draggableTrigger, [ { oldState: State.BEGAN, state: State.ACTIVE }, { state: State.ACTIVE }, { state: State.END }, ] ); } // Advance timers one frame to ensure that shared values // are updated and trigger animation reactions. act( () => advanceAnimationByFrames( 1 ) ); }; /** * Fires pan gesture event on a BlockDraggable component. * * @param {import('react-test-renderer').ReactTestInstance} blockDraggable BlockDraggable test instance. * @param {Object} [touchEvents] Array of touch events to dispatch on the pan gesture. */ export const firePanGesture = ( blockDraggable, touchEvents = DEFAULT_TOUCH_EVENTS ) => { const gestureTouchEvents = touchEvents.map( ( { eventType, ...touchEvent } ) => ( { allTouches: [ touchEvent ], eventType, } ) ); fireGestureHandler( blockDraggable, [ // TOUCHES_DOWN event is only received on ACTIVE state, so we have to fire it manually. { oldState: State.BEGAN, state: State.ACTIVE }, ...gestureTouchEvents, { state: State.END }, ] ); // Advance timers one frame to ensure that shared values // are updated and trigger animation reactions. act( () => advanceAnimationByFrames( 1 ) ); }; /** * Gets the draggable chip element. * * @param {import('@testing-library/react-native').RenderAPI} screen The Testing Library screen. * * @return {import('react-test-renderer').ReactTestInstance} Draggable chip test instance. */ export const getDraggableChip = ( { getByTestId } ) => { let draggableChip; try { draggableChip = getByTestId( 'draggable-chip' ); } catch ( e ) { // NOOP. } return draggableChip; };