From 2a95bfc4f7d3f2cf6c824eff23756afe301edc72 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Sat, 30 Jul 2022 00:17:10 +0800 Subject: [PATCH] Fix e2e test and add the pageUtils.dragFiles e2e util --- package-lock.json | 17 ++- package.json | 1 + packages/block-library/src/paragraph/edit.js | 18 ++- .../e2e-test-utils-playwright/package.json | 3 +- .../src/page-utils/drag-files.ts | 131 ++++++++++++++++++ .../src/page-utils/index.ts | 2 + .../e2e/specs/editor/blocks/paragraph.spec.js | 45 +++--- 7 files changed, 181 insertions(+), 36 deletions(-) create mode 100644 packages/e2e-test-utils-playwright/src/page-utils/drag-files.ts diff --git a/package-lock.json b/package-lock.json index d99d2ce2d5e03..55a964fe652dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15510,6 +15510,12 @@ "@types/unist": "*" } }, + "@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -16978,7 +16984,16 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/url": "file:packages/url", "form-data": "^4.0.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "mime": "^3.0.0" + }, + "dependencies": { + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true + } } }, "@wordpress/e2e-tests": { diff --git a/package.json b/package.json index 837e3c7276a80..c99c6df08db6c 100755 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "@types/highlight-words-core": "1.2.1", "@types/istanbul-lib-report": "3.0.0", "@types/lodash": "4.14.172", + "@types/mime": "2.0.3", "@types/npm-package-arg": "6.1.1", "@types/prettier": "2.4.4", "@types/qs": "6.9.7", diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js index 32e1e402317c6..da3d86994c18c 100644 --- a/packages/block-library/src/paragraph/edit.js +++ b/packages/block-library/src/paragraph/edit.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useRef } from '@wordpress/element'; +import { useState } from '@wordpress/element'; import { __, _x, isRTL } from '@wordpress/i18n'; import { ToolbarButton, @@ -62,9 +62,15 @@ function ParagraphBlock( { } ) { const { align, content, direction, dropCap, placeholder } = attributes; const isDropCapFeatureEnabled = useSetting( 'typography.dropCap' ); - const ref = useRef(); + const [ paragraphElement, setParagraphElement ] = useState( null ); + const refCallback = ( element ) => { + setParagraphElement( element ); + }; const blockProps = useBlockProps( { - ref: useMergeRefs( [ useOnEnter( { clientId, content } ), ref ] ), + ref: useMergeRefs( [ + useOnEnter( { clientId, content } ), + refCallback, + ] ), className: classnames( { 'has-drop-cap': dropCap, [ `has-text-align-${ align }` ]: align, @@ -119,7 +125,7 @@ function ParagraphBlock( { ) } { ! content && ( { if ( files.length === 1 ) { diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json index 0a1631298245f..521c558806888 100644 --- a/packages/e2e-test-utils-playwright/package.json +++ b/packages/e2e-test-utils-playwright/package.json @@ -35,7 +35,8 @@ "@wordpress/keycodes": "file:../keycodes", "@wordpress/url": "file:../url", "form-data": "^4.0.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "mime": "^3.0.0" }, "peerDependencies": { "@playwright/test": ">=1" diff --git a/packages/e2e-test-utils-playwright/src/page-utils/drag-files.ts b/packages/e2e-test-utils-playwright/src/page-utils/drag-files.ts new file mode 100644 index 0000000000000..e7b44fa1ab280 --- /dev/null +++ b/packages/e2e-test-utils-playwright/src/page-utils/drag-files.ts @@ -0,0 +1,131 @@ +/** + * External dependencies + */ +import { readFile } from 'fs/promises'; +import { basename } from 'path'; +import { getType } from 'mime'; + +/** + * Internal dependencies + */ +import type { PageUtils } from './index'; + +type FileObject = { + name: string; + mimeType?: string; + buffer: Buffer; +}; + +/** + * Simulate dragging files from outside the current page. + * + * @param this + * @param files The files to be dragged. + * @return The methods of the drag operation. + */ +async function dragFiles( + this: PageUtils, + files: string | string[] | FileObject | FileObject[] +) { + const filesList = Array.isArray( files ) ? files : [ files ]; + const fileObjects = await Promise.all( + filesList.map( async ( filePathOrObject ) => { + if ( typeof filePathOrObject !== 'string' ) { + return { + name: filePathOrObject.name, + mimeType: + filePathOrObject.mimeType || + getType( filePathOrObject.name ), + base64: filePathOrObject.buffer.toString( 'base64' ), + }; + } + const base64 = await readFile( filePathOrObject, 'base64' ); + const name = basename( filePathOrObject ); + return { + name, + mimeType: getType( filePathOrObject ), + base64, + }; + } ) + ); + + const dataTransfer = await this.page.evaluateHandle( + async ( _fileObjects ) => { + const dt = new DataTransfer(); + const fileInstances = await Promise.all( + _fileObjects.map( async ( fileObject ) => { + const blob = await fetch( + `data:${ fileObject.mimeType };base64,${ fileObject.base64 }` + ).then( ( res ) => res.blob() ); + return new File( [ blob ], fileObject.name, { + type: fileObject.mimeType ?? undefined, + } ); + } ) + ); + + fileInstances.forEach( ( file ) => { + dt.items.add( file ); + } ); + + return dt; + }, + fileObjects + ); + + // Simulate dragging over the document. + await this.page.dispatchEvent( 'html', 'dragenter', { dataTransfer } ); + + const position = { + x: 0, + y: 0, + }; + + const getCurrentTopMostElement = async () => { + const elementFromPosition = await this.page.evaluateHandle( + ( point ) => { + const element = document.elementFromPoint( point.x, point.y ); + return element; + }, + position + ); + + return elementFromPosition.asElement(); + }; + + return { + /** + * Move the cursor and drag the files to the specified position. + * + * @param x The X coordinate. + * @param y The Y coordinate. + */ + dragTo: async ( x: number, y: number ) => { + position.x = x; + position.y = y; + + const elementHandle = await getCurrentTopMostElement(); + + if ( ! elementHandle ) { + return; + } + + await elementHandle.dispatchEvent( 'dragenter', { dataTransfer } ); + }, + /** + * Drop the files at the current position. + */ + drop: async () => { + const elementHandle = await getCurrentTopMostElement(); + + if ( ! elementHandle ) { + throw new Error( + `No element at position (${ position.x }, ${ position.y }) to drop on` + ); + } + + await elementHandle.dispatchEvent( 'drop', { dataTransfer } ); + }, + }; +} + +export { dragFiles }; diff --git a/packages/e2e-test-utils-playwright/src/page-utils/index.ts b/packages/e2e-test-utils-playwright/src/page-utils/index.ts index d147365fe5e0e..95d64a022e22e 100644 --- a/packages/e2e-test-utils-playwright/src/page-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/page-utils/index.ts @@ -6,6 +6,7 @@ import type { Browser, Page, BrowserContext } from '@playwright/test'; /** * Internal dependencies */ +import { dragFiles } from './drag-files'; import { isCurrentURL } from './is-current-url'; import { setClipboardData, @@ -29,6 +30,7 @@ class PageUtils { this.browser = this.context.browser()!; } + dragFiles = dragFiles.bind( this ); isCurrentURL = isCurrentURL.bind( this ); pressKeyTimes = pressKeyTimes.bind( this ); pressKeyWithModifier = pressKeyWithModifier.bind( this ); diff --git a/test/e2e/specs/editor/blocks/paragraph.spec.js b/test/e2e/specs/editor/blocks/paragraph.spec.js index 921874cb8b11b..50240ee22f645 100644 --- a/test/e2e/specs/editor/blocks/paragraph.spec.js +++ b/test/e2e/specs/editor/blocks/paragraph.spec.js @@ -2,7 +2,6 @@ * External dependencies */ const path = require( 'path' ); -const fs = require( 'fs/promises' ); /** * WordPress dependencies @@ -46,48 +45,38 @@ test.describe( 'Paragraph', () => { expect( firstBlockTagName ).toBe( 'P' ); } ); - test( 'should allow dropping an image on en empty paragraph block', async ( { + test.only( 'should allow dropping an image on en empty paragraph block', async ( { editor, page, + pageUtils, } ) => { await editor.insertBlock( { name: 'core/paragraph' } ); const emptyParagraphBlock = page.locator( '[data-type="core/paragraph"]' ); + const testImageName = '10x10_e2e_test_image_z9T8jK.png'; const testImagePath = path.join( __dirname, - '../../../assets/10x10_e2e_test_image_z9T8jK.png' - ); - const testImage = await fs.readFile( testImagePath, 'base64' ); - const dataTransfer = await page.evaluateHandle( - async ( [ base64 ] ) => { - const blobResponse = await window.fetch( - `data:image/png;base64,${ base64 }` - ); - const blob = await blobResponse.blob(); - const file = new window.File( [ blob ], 'test-image.png', { - type: 'image/png', - } ); - const dt = new window.DataTransfer(); - dt.items.add( file ); - return dt; - }, - [ testImage ] + '../../../assets', + testImageName ); - const dropZone = emptyParagraphBlock.locator( - '[data-is-drop-zone="true"]' - ); - // Simulate dragging from outside the browser. - await page.dispatchEvent( 'html', 'dragenter', { dataTransfer } ); - await dropZone.dispatchEvent( 'dragenter', { dataTransfer } ); + const { dragTo, drop } = await pageUtils.dragFiles( testImagePath ); + + const { x, y, width, height } = await emptyParagraphBlock.boundingBox(); + const centerPosition = { + x: x + width / 2, + y: y + height / 2, + }; + + await dragTo( centerPosition.x, centerPosition.y ); await expect( - emptyParagraphBlock.locator( 'text="Drop files to upload"' ) + page.locator( 'text="Drop files to upload"' ) ).toBeVisible(); - await dropZone.dispatchEvent( 'drop', { dataTransfer } ); + await drop(); const imageBlock = page.locator( 'role=document[name="Block: Image"i]' @@ -95,7 +84,7 @@ test.describe( 'Paragraph', () => { await expect( imageBlock ).toBeVisible(); await expect( imageBlock.locator( 'role=img' ) ).toHaveAttribute( 'src', - /test-image\.png$/ + new RegExp( testImageName.replace( '.', '\\.' ) ) ); } ); } );