Skip to content

Commit 339a6f5

Browse files
committed
Fix e2e test and add the pageUtils.dragFiles e2e util
1 parent 50c6d11 commit 339a6f5

File tree

7 files changed

+180
-35
lines changed

7 files changed

+180
-35
lines changed

package-lock.json

+16-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"@types/highlight-words-core": "1.2.1",
122122
"@types/istanbul-lib-report": "3.0.0",
123123
"@types/lodash": "4.14.172",
124+
"@types/mime": "2.0.3",
124125
"@types/npm-package-arg": "6.1.1",
125126
"@types/prettier": "2.4.4",
126127
"@types/qs": "6.9.7",

packages/block-library/src/paragraph/edit.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import classnames from 'classnames';
66
/**
77
* WordPress dependencies
88
*/
9-
import { useRef } from '@wordpress/element';
9+
import { useState } from '@wordpress/element';
1010
import { __, _x, isRTL } from '@wordpress/i18n';
1111
import {
1212
ToolbarButton,
@@ -62,9 +62,15 @@ function ParagraphBlock( {
6262
} ) {
6363
const { align, content, direction, dropCap, placeholder } = attributes;
6464
const isDropCapFeatureEnabled = useSetting( 'typography.dropCap' );
65-
const ref = useRef();
65+
const [ paragraphElement, setParagraphElement ] = useState( null );
66+
const refCallback = ( element ) => {
67+
setParagraphElement( element );
68+
};
6669
const blockProps = useBlockProps( {
67-
ref: useMergeRefs( [ useOnEnter( { clientId, content } ), ref ] ),
70+
ref: useMergeRefs( [
71+
useOnEnter( { clientId, content } ),
72+
refCallback,
73+
] ),
6874
className: classnames( {
6975
'has-drop-cap': dropCap,
7076
[ `has-text-align-${ align }` ]: align,
@@ -119,7 +125,7 @@ function ParagraphBlock( {
119125
) }
120126
{ ! content && (
121127
<Popover
122-
anchorRef={ ref.current }
128+
anchorRef={ paragraphElement }
123129
animate={ false }
124130
position="top right left"
125131
focusOnMount={ false }
@@ -129,8 +135,8 @@ function ParagraphBlock( {
129135
<DropZone
130136
style={ {
131137
// TODO: Ideally we should observe the size of the paragraph block.
132-
width: ref.current?.offsetWidth,
133-
height: ref.current?.offsetHeight,
138+
width: paragraphElement?.offsetWidth,
139+
height: paragraphElement?.offsetHeight,
134140
} }
135141
onFilesDrop={ ( files ) => {
136142
if ( files.length === 1 ) {

packages/e2e-test-utils-playwright/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"@wordpress/keycodes": "file:../keycodes",
3636
"@wordpress/url": "file:../url",
3737
"form-data": "^4.0.0",
38-
"lodash": "^4.17.21"
38+
"lodash": "^4.17.21",
39+
"mime": "^3.0.0"
3940
},
4041
"peerDependencies": {
4142
"@playwright/test": ">=1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { readFile } from 'fs/promises';
5+
import { basename } from 'path';
6+
import { getType } from 'mime';
7+
8+
/**
9+
* Internal dependencies
10+
*/
11+
import type { PageUtils } from './index';
12+
13+
type FileObject = {
14+
name: string;
15+
mimeType?: string;
16+
buffer: Buffer;
17+
};
18+
19+
/**
20+
* Simulate dragging files from outside the current page.
21+
*
22+
* @param this
23+
* @param files The files to be dragged.
24+
* @return The methods of the drag operation.
25+
*/
26+
async function dragFiles(
27+
this: PageUtils,
28+
files: string | string[] | FileObject | FileObject[]
29+
) {
30+
const filesList = Array.isArray( files ) ? files : [ files ];
31+
const fileObjects = await Promise.all(
32+
filesList.map( async ( filePathOrObject ) => {
33+
if ( typeof filePathOrObject !== 'string' ) {
34+
return {
35+
name: filePathOrObject.name,
36+
mimeType:
37+
filePathOrObject.mimeType ||
38+
getType( filePathOrObject.name ),
39+
base64: filePathOrObject.buffer.toString( 'base64' ),
40+
};
41+
}
42+
const base64 = await readFile( filePathOrObject, 'base64' );
43+
const name = basename( filePathOrObject );
44+
return {
45+
name,
46+
mimeType: getType( filePathOrObject ),
47+
base64,
48+
};
49+
} )
50+
);
51+
52+
const dataTransfer = await this.page.evaluateHandle(
53+
async ( _fileObjects ) => {
54+
const dt = new DataTransfer();
55+
const fileInstances = await Promise.all(
56+
_fileObjects.map( async ( fileObject ) => {
57+
const blob = await fetch(
58+
`data:${ fileObject.mimeType };base64,${ fileObject.base64 }`
59+
).then( ( res ) => res.blob() );
60+
return new File( [ blob ], fileObject.name, {
61+
type: fileObject.mimeType ?? undefined,
62+
} );
63+
} )
64+
);
65+
66+
fileInstances.forEach( ( file ) => {
67+
dt.items.add( file );
68+
} );
69+
70+
return dt;
71+
},
72+
fileObjects
73+
);
74+
75+
// Simulate dragging over the document.
76+
await this.page.dispatchEvent( 'html', 'dragenter', { dataTransfer } );
77+
78+
const position = {
79+
x: 0,
80+
y: 0,
81+
};
82+
83+
const getCurrentTopMostElement = async () => {
84+
const elementFromPosition = await this.page.evaluateHandle(
85+
( point ) => {
86+
const element = document.elementFromPoint( point.x, point.y );
87+
return element;
88+
},
89+
position
90+
);
91+
92+
return elementFromPosition.asElement();
93+
};
94+
95+
return {
96+
/**
97+
* Move the cursor and drag the files to the specified position.
98+
*
99+
* @param x The X coordinate.
100+
* @param y The Y coordinate.
101+
*/
102+
dragTo: async ( x: number, y: number ) => {
103+
position.x = x;
104+
position.y = y;
105+
106+
const elementHandle = await getCurrentTopMostElement();
107+
108+
if ( ! elementHandle ) {
109+
return;
110+
}
111+
112+
await elementHandle.dispatchEvent( 'dragenter', { dataTransfer } );
113+
},
114+
/**
115+
* Drop the files at the current position.
116+
*/
117+
drop: async () => {
118+
const elementHandle = await getCurrentTopMostElement();
119+
120+
if ( ! elementHandle ) {
121+
throw new Error(
122+
`No element at position (${ position.x }, ${ position.y }) to drop on`
123+
);
124+
}
125+
126+
await elementHandle.dispatchEvent( 'drop', { dataTransfer } );
127+
},
128+
};
129+
}
130+
131+
export { dragFiles };

packages/e2e-test-utils-playwright/src/page-utils/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { Browser, Page, BrowserContext } from '@playwright/test';
66
/**
77
* Internal dependencies
88
*/
9+
import { dragFiles } from './drag-files';
910
import { isCurrentURL } from './is-current-url';
1011
import {
1112
setClipboardData,
@@ -29,6 +30,7 @@ class PageUtils {
2930
this.browser = this.context.browser()!;
3031
}
3132

33+
dragFiles = dragFiles.bind( this );
3234
isCurrentURL = isCurrentURL.bind( this );
3335
pressKeyTimes = pressKeyTimes.bind( this );
3436
pressKeyWithModifier = pressKeyWithModifier.bind( this );

test/e2e/specs/editor/blocks/paragraph.spec.js

+16-27
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* External dependencies
33
*/
44
const path = require( 'path' );
5-
const fs = require( 'fs/promises' );
65

76
/**
87
* WordPress dependencies
@@ -49,53 +48,43 @@ test.describe( 'Paragraph', () => {
4948
test( 'should allow dropping an image on en empty paragraph block', async ( {
5049
editor,
5150
page,
51+
pageUtils,
5252
} ) => {
5353
await editor.insertBlock( { name: 'core/paragraph' } );
5454
const emptyParagraphBlock = page.locator(
5555
'[data-type="core/paragraph"]'
5656
);
5757

58+
const testImageName = '10x10_e2e_test_image_z9T8jK.png';
5859
const testImagePath = path.join(
5960
__dirname,
60-
'../../../assets/10x10_e2e_test_image_z9T8jK.png'
61-
);
62-
const testImage = await fs.readFile( testImagePath, 'base64' );
63-
const dataTransfer = await page.evaluateHandle(
64-
async ( [ base64 ] ) => {
65-
const blobResponse = await window.fetch(
66-
`data:image/png;base64,${ base64 }`
67-
);
68-
const blob = await blobResponse.blob();
69-
const file = new window.File( [ blob ], 'test-image.png', {
70-
type: 'image/png',
71-
} );
72-
const dt = new window.DataTransfer();
73-
dt.items.add( file );
74-
return dt;
75-
},
76-
[ testImage ]
61+
'../../../assets',
62+
testImageName
7763
);
7864

79-
const dropZone = emptyParagraphBlock.locator(
80-
'[data-is-drop-zone="true"]'
81-
);
82-
// Simulate dragging from outside the browser.
83-
await page.dispatchEvent( 'html', 'dragenter', { dataTransfer } );
84-
await dropZone.dispatchEvent( 'dragenter', { dataTransfer } );
65+
const { dragTo, drop } = await pageUtils.dragFiles( testImagePath );
66+
67+
const { x, y, width, height } = await emptyParagraphBlock.boundingBox();
68+
const centerPosition = {
69+
x: x + width / 2,
70+
y: y + height / 2,
71+
};
72+
73+
await dragTo( centerPosition.x, centerPosition.y );
8574

8675
await expect(
87-
emptyParagraphBlock.locator( 'text="Drop files to upload"' )
76+
page.locator( 'text="Drop files to upload"' )
8877
).toBeVisible();
8978

90-
await dropZone.dispatchEvent( 'drop', { dataTransfer } );
79+
await drop();
9180

9281
const imageBlock = page.locator(
9382
'role=document[name="Block: Image"i]'
9483
);
9584
await expect( imageBlock ).toBeVisible();
9685
await expect( imageBlock.locator( 'role=img' ) ).toHaveAttribute(
9786
'src',
98-
/test-image\.png$/
87+
new RegExp( testImageName.replace( '.', '\\.' ) )
9988
);
10089
} );
10190
} );

0 commit comments

Comments
 (0)