From a7e85b3f757b3d8156131f10a05aa82d1a665478 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 28 Mar 2019 17:11:35 -0400 Subject: [PATCH] Testing: Reimplement Ctrl+A as emulated in pressKeyWithModifier --- packages/e2e-test-utils/README.md | 8 -- packages/e2e-test-utils/src/index.js | 2 - packages/e2e-test-utils/src/login-user.js | 6 +- .../src/press-key-with-modifier.js | 76 +++++++++++++++++++ .../e2e-test-utils/src/select-all-blocks.js | 19 ----- packages/e2e-test-utils/src/select-all.js | 15 ---- .../specs/block-hierarchy-navigation.test.js | 3 +- .../specs/multi-block-selection.test.js | 13 ++-- .../e2e-tests/specs/reusable-blocks.test.js | 5 +- packages/e2e-tests/specs/rich-text.test.js | 5 +- 10 files changed, 90 insertions(+), 62 deletions(-) delete mode 100644 packages/e2e-test-utils/src/select-all-blocks.js delete mode 100644 packages/e2e-test-utils/src/select-all.js diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index 35cd91fd6c14b..8cfd7554eb782 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -377,14 +377,6 @@ _Parameters_ - _searchTerm_ `string`: The text to search the inserter for. -# **selectAllBlocks** - -Selects all blocks present in the block editor. - -_Returns_ - -- `Promise`: Promise resolving once active element selected. - # **selectBlockByClientId** Given the clientId of a block, selects the block on the editor. diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index e0832b1d9a5e4..e984658a99f6d 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -1,4 +1,3 @@ -export { __unstableSelectAll } from './select-all'; export { activatePlugin } from './activate-plugin'; export { arePrePublishChecksEnabled } from './are-pre-publish-checks-enabled'; export { clearLocalStorage } from './clear-local-storage'; @@ -38,7 +37,6 @@ export { publishPost } from './publish-post'; export { publishPostWithPrePublishChecksDisabled } from './publish-post-with-pre-publish-checks-disabled'; export { saveDraft } from './save-draft'; export { searchForBlock } from './search-for-block'; -export { selectAllBlocks } from './select-all-blocks'; export { selectBlockByClientId } from './select-block-by-client-id'; export { setBrowserViewport } from './set-browser-viewport'; export { setPostContent } from './set-post-content'; diff --git a/packages/e2e-test-utils/src/login-user.js b/packages/e2e-test-utils/src/login-user.js index 63465582efb81..d06878d69186e 100644 --- a/packages/e2e-test-utils/src/login-user.js +++ b/packages/e2e-test-utils/src/login-user.js @@ -4,7 +4,7 @@ import { WP_USERNAME, WP_PASSWORD } from './shared/config'; import { createURL } from './create-url'; import { isCurrentURL } from './is-current-url'; -import { __unstableSelectAll } from './select-all'; +import { pressKeyWithModifier } from './press-key-with-modifier'; /** * Performs log in with specified username and password. @@ -20,10 +20,10 @@ export async function loginUser( username = WP_USERNAME, password = WP_PASSWORD } await page.focus( '#user_login' ); - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await page.type( '#user_login', username ); await page.focus( '#user_pass' ); - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await page.type( '#user_pass', password ); await Promise.all( [ page.waitForNavigation(), page.click( '#wp-submit' ) ] ); diff --git a/packages/e2e-test-utils/src/press-key-with-modifier.js b/packages/e2e-test-utils/src/press-key-with-modifier.js index e20100ba54e5f..59f5c8330672e 100644 --- a/packages/e2e-test-utils/src/press-key-with-modifier.js +++ b/packages/e2e-test-utils/src/press-key-with-modifier.js @@ -8,6 +8,78 @@ import { capitalize } from 'lodash'; */ import { modifiers, SHIFT, ALT, CTRL } from '@wordpress/keycodes'; +/** + * Emulates a Ctrl+A SelectAll key combination by dispatching custom keyboard + * events and using the results of those events to determine whether to call + * `document.execCommand( 'selectall' );`. This is necessary because Puppeteer + * does not emulate Ctrl+A SelectAll in macOS. Events are dispatched to ensure + * that any `Event#preventDefault` which would have normally occurred in the + * application as a result of Ctrl+A is respected. + * + * @link https://github.com/GoogleChrome/puppeteer/issues/1313 + * @link https://w3c.github.io/uievents/tools/key-event-viewer.html + * + * @return {Promise} Promise resolving once the SelectAll emulation completes. + */ +async function emulateSelectAll() { + await page.evaluate( () => { + const isMac = /Mac|iPod|iPhone|iPad/.test( window.navigator.platform ); + + document.activeElement.dispatchEvent( + new KeyboardEvent( 'keydown', { + bubbles: true, + cancelable: true, + key: isMac ? 'Meta' : 'Control', + code: isMac ? 'MetaLeft' : 'ControlLeft', + location: window.KeyboardEvent.DOM_KEY_LOCATION_LEFT, + getModifierState: ( keyArg ) => keyArg === ( isMac ? 'Meta' : 'Control' ), + ctrlKey: ! isMac, + metaKey: isMac, + charCode: 0, + keyCode: isMac ? 93 : 17, + which: isMac ? 93 : 17, + } ) + ); + + const preventableEvent = new KeyboardEvent( 'keydown', { + bubbles: true, + cancelable: true, + key: 'a', + code: 'KeyA', + location: window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD, + getModifierState: ( keyArg ) => keyArg === ( isMac ? 'Meta' : 'Control' ), + ctrlKey: ! isMac, + metaKey: isMac, + charCode: 0, + keyCode: 65, + which: 65, + } ); + + const wasPrevented = ( + ! document.activeElement.dispatchEvent( preventableEvent ) || + preventableEvent.defaultPrevented + ); + + if ( ! wasPrevented ) { + document.execCommand( 'selectall', false, null ); + } + + document.activeElement.dispatchEvent( + new KeyboardEvent( 'keyup', { + bubbles: true, + cancelable: true, + key: isMac ? 'Meta' : 'Control', + code: isMac ? 'MetaLeft' : 'ControlLeft', + location: window.KeyboardEvent.DOM_KEY_LOCATION_LEFT, + getModifierState: () => false, + charCode: 0, + keyCode: isMac ? 93 : 17, + which: isMac ? 93 : 17, + } ), + ); + } ); +} + /** * Performs a key press with modifier (Shift, Control, Meta, Alt), where each modifier * is normalized to platform-specific modifier. @@ -16,6 +88,10 @@ import { modifiers, SHIFT, ALT, CTRL } from '@wordpress/keycodes'; * @param {string} key Key to press while modifier held. */ export async function pressKeyWithModifier( modifier, key ) { + if ( modifier.toLowerCase() === 'primary' && key.toLowerCase() === 'a' ) { + return await emulateSelectAll(); + } + const isAppleOS = () => process.platform === 'darwin'; const overWrittenModifiers = { ...modifiers, diff --git a/packages/e2e-test-utils/src/select-all-blocks.js b/packages/e2e-test-utils/src/select-all-blocks.js deleted file mode 100644 index adfe905c63188..0000000000000 --- a/packages/e2e-test-utils/src/select-all-blocks.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Internal dependencies - */ -import { pressKeyWithModifier } from './press-key-with-modifier'; -import { __unstableSelectAll } from './select-all'; - -/** - * Selects all blocks present in the block editor. - * - * @return {Promise} Promise resolving once active element selected. - */ -export async function selectAllBlocks() { - // NOTE: `__unstableSelectAll` is used for cross-platform compatibility - // alternative to Cmd+A. The second issuance of the key combination is - // handled internerally by the block editor's KeyboardShortcuts utility, - // and is not subject to the same buggy browser/OS emulation. - await __unstableSelectAll(); - await pressKeyWithModifier( 'primary', 'a' ); -} diff --git a/packages/e2e-test-utils/src/select-all.js b/packages/e2e-test-utils/src/select-all.js deleted file mode 100644 index 1f670bb865bf3..0000000000000 --- a/packages/e2e-test-utils/src/select-all.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Selects all text within the current active element field. Throws if there is - * no active element. - * - * This serves as a temporary solution to a bug present as of Puppeteer 1.6.1 - * in which the Cmd+A keyboard combination does not work correctly in macOS - * environments. Once the bug is resolved, the utility will be removed. - * - * @link https://github.com/GoogleChrome/puppeteer/issues/1313 - * - * @return {Promise} Promise resolving once active element selected. - */ -export async function __unstableSelectAll() { - await page.evaluate( () => document.execCommand( 'selectall', false, null ) ); -} diff --git a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js index 63ea25ca8b88a..c53adb01730bd 100644 --- a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js +++ b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js @@ -7,7 +7,6 @@ import { getEditedPostContent, pressKeyTimes, pressKeyWithModifier, - __unstableSelectAll, } from '@wordpress/e2e-test-utils'; async function openBlockNavigator() { @@ -100,7 +99,7 @@ describe( 'Navigating the block hierarchy', () => { await page.keyboard.press( 'Space' ); // Replace its content. - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await page.keyboard.type( 'and I say hello' ); expect( await getEditedPostContent() ).toMatchSnapshot(); diff --git a/packages/e2e-tests/specs/multi-block-selection.test.js b/packages/e2e-tests/specs/multi-block-selection.test.js index 44701ca1e0ce2..6bf7a62bf7789 100644 --- a/packages/e2e-tests/specs/multi-block-selection.test.js +++ b/packages/e2e-tests/specs/multi-block-selection.test.js @@ -8,8 +8,6 @@ import { pressKeyWithModifier, pressKeyTimes, getEditedPostContent, - selectAllBlocks, - __unstableSelectAll, } from '@wordpress/e2e-test-utils'; describe( 'Multi-block selection', () => { @@ -76,11 +74,7 @@ describe( 'Multi-block selection', () => { // Select all via double shortcut. await page.click( firstBlockSelector ); - // NOTE: `__unstableSelectAll` is used for cross-platform compatibility - // alternative to Cmd+A. The second issuance of the key combination is - // handled internerally by the block editor's KeyboardShortcuts utility - // and is not subject to the same buggy emulation. - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await pressKeyWithModifier( 'primary', 'a' ); await expectMultiSelected( blocks, true ); } ); @@ -132,7 +126,10 @@ describe( 'Multi-block selection', () => { await page.keyboard.type( 'Second Paragraph' ); await insertBlock( 'Paragraph' ); await page.keyboard.type( 'Third Paragraph' ); - await selectAllBlocks(); + + // Multiselect via keyboard. + await pressKeyWithModifier( 'primary', 'a' ); + await pressKeyWithModifier( 'primary', 'a' ); // TODO: It would be great to do this test by spying on `wp.a11y.speak`, // but it's very difficult to do that because `wp.a11y` has diff --git a/packages/e2e-tests/specs/reusable-blocks.test.js b/packages/e2e-tests/specs/reusable-blocks.test.js index 7e25f743f0c82..73d7b9117bf43 100644 --- a/packages/e2e-tests/specs/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/reusable-blocks.test.js @@ -5,9 +5,9 @@ import { insertBlock, createNewPost, clickBlockToolbarButton, + pressKeyWithModifier, searchForBlock, getEditedPostContent, - selectAllBlocks, } from '@wordpress/e2e-test-utils'; function waitForAndAcceptDialog() { @@ -210,7 +210,8 @@ describe( 'Reusable Blocks', () => { await page.keyboard.type( 'Second paragraph' ); // Select all the blocks - await selectAllBlocks(); + await pressKeyWithModifier( 'primary', 'a' ); + await pressKeyWithModifier( 'primary', 'a' ); // Convert block to a reusable block await page.waitForSelector( 'button[aria-label="More options"]' ); diff --git a/packages/e2e-tests/specs/rich-text.test.js b/packages/e2e-tests/specs/rich-text.test.js index 0034ac3b106e0..dcb1434abd89b 100644 --- a/packages/e2e-tests/specs/rich-text.test.js +++ b/packages/e2e-tests/specs/rich-text.test.js @@ -7,7 +7,6 @@ import { insertBlock, clickBlockAppender, pressKeyWithModifier, - __unstableSelectAll, } from '@wordpress/e2e-test-utils'; describe( 'RichText', () => { @@ -31,7 +30,7 @@ describe( 'RichText', () => { it( 'should apply formatting with access shortcut', async () => { await clickBlockAppender(); await page.keyboard.type( 'test' ); - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await pressKeyWithModifier( 'access', 'd' ); expect( await getEditedPostContent() ).toMatchSnapshot(); @@ -40,7 +39,7 @@ describe( 'RichText', () => { it( 'should apply formatting with primary shortcut', async () => { await clickBlockAppender(); await page.keyboard.type( 'test' ); - await __unstableSelectAll(); + await pressKeyWithModifier( 'primary', 'a' ); await pressKeyWithModifier( 'primary', 'b' ); expect( await getEditedPostContent() ).toMatchSnapshot();