-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[RNMobile] Add integration tests to cover Drag & Drop functionality (#…
…41364) * Add testID prop to Draggable components * Add unit tests for Draggable component * Set draggingId shared value before enable dragging This change is required for testing, otherwise the dragging id is not passed when the dragging gesture begins. * Mock generateHapticFeedback function * Add testID to draggable chip component * Add testID to BlockDraggable component * Add test helpers for BlockDraggable component Additionally, helpers related to fake timers have been added and updated in the global helpers file. * Add drag and drop integration tests * Update react-native-aztec mock to use AztecInputState
- Loading branch information
Showing
12 changed files
with
1,038 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
packages/block-editor/src/components/block-draggable/test/__snapshots__/index.native.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`BlockDraggable moves blocks: Initial order 1`] = ` | ||
"<!-- wp:paragraph --> | ||
<p>This is a paragraph.</p> | ||
<!-- /wp:paragraph --> | ||
<!-- wp:image {\\"sizeSlug\\":\\"large\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:spacer --> | ||
<div style=\\"height:100px\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div> | ||
<!-- /wp:spacer --> | ||
<!-- wp:gallery {\\"linkTo\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-gallery has-nested-images columns-default is-cropped\\"><!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --></figure> | ||
<!-- /wp:gallery -->" | ||
`; | ||
exports[`BlockDraggable moves blocks: Paragraph block moved from first to second position 1`] = ` | ||
"<!-- wp:image {\\"sizeSlug\\":\\"large\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:paragraph --> | ||
<p>This is a paragraph.</p> | ||
<!-- /wp:paragraph --> | ||
<!-- wp:spacer --> | ||
<div style=\\"height:100px\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div> | ||
<!-- /wp:spacer --> | ||
<!-- wp:gallery {\\"linkTo\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-gallery has-nested-images columns-default is-cropped\\"><!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --></figure> | ||
<!-- /wp:gallery -->" | ||
`; | ||
exports[`BlockDraggable moves blocks: Spacer block moved from third to first position 1`] = ` | ||
"<!-- wp:spacer --> | ||
<div style=\\"height:100px\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div> | ||
<!-- /wp:spacer --> | ||
<!-- wp:image {\\"sizeSlug\\":\\"large\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:paragraph --> | ||
<p>This is a paragraph.</p> | ||
<!-- /wp:paragraph --> | ||
<!-- wp:gallery {\\"linkTo\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-gallery has-nested-images columns-default is-cropped\\"><!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --> | ||
<!-- wp:image {\\"sizeSlug\\":\\"large\\",\\"linkDestination\\":\\"none\\"} --> | ||
<figure class=\\"wp-block-image size-large\\"><img src=\\"https://cldup.com/cXyG__fTLN.jpg\\" alt=\\"\\"/></figure> | ||
<!-- /wp:image --></figure> | ||
<!-- /wp:gallery -->" | ||
`; |
183 changes: 183 additions & 0 deletions
183
packages/block-editor/src/components/block-draggable/test/helpers.native.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { | ||
act, | ||
fireEvent, | ||
initializeEditor, | ||
waitForStoreResolvers, | ||
within, | ||
advanceAnimationByFrame, | ||
} 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' ); | ||
|
||
const screen = await initializeEditor( { initialHtml } ); | ||
const { getByA11yLabel } = screen; | ||
|
||
const waitPromises = []; | ||
blocks.forEach( ( block, index ) => { | ||
const a11yLabel = new RegExp( | ||
`${ block.name } Block\\. Row ${ index + 1 }` | ||
); | ||
const element = getByA11yLabel( a11yLabel ); | ||
// "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 }` | ||
); | ||
fireEvent( | ||
within( element ).getByA11yLabel( nestedA11yLabel ), | ||
'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( () => advanceAnimationByFrame( 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( () => advanceAnimationByFrame( 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; | ||
}; |
Oops, something went wrong.