diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index d8f14e5e6c343b..6bf7e5065b3d14 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -173,6 +173,14 @@ _Returns_ - `Promise`: Promise resolving once the edit post sidebar is opened. +# **externalWrapperHasFocus** + +Asserts that the element with keyboard focus is a block's external wrapper + +_Parameters_ + +- _blockType_ `string`: The expected value of the data-type attribute of the block's external wrapper + # **findSidebarPanelToggleButtonWithTitle** Finds a sidebar panel with the provided title. @@ -243,6 +251,14 @@ _Returns_ - `Promise`: Promise resolving with post content markup. +# **getElementSelectorList** + +Returns an array of classnames that match a given selector on the current page + +_Parameters_ + +- _selector_ `string`: The selector to query on the page + # **hasBlockSwitcher** Returns a boolean indicating if the current selected block has a block switcher or not. @@ -251,6 +267,15 @@ _Returns_ - `Promise`: Promise resolving with a boolean. +# **insertAndPopulateBlock** + +Inserts a content block and then, if it has text content areas, fills them with text + +_Parameters_ + +- _blockName_ `string`: The type of block to insert +- _content_ `string`: The text to enter into each contenteditable area + # **insertBlock** Opens the inserter, searches for the given term, then selects the first @@ -261,6 +286,10 @@ _Parameters_ - _searchTerm_ `string`: The text to search the inserter for. - _panelName_ `string`: The inserter panel to open (if it's closed by default). +# **inserterToggleHasFocus** + +Asserts that a content block's inserter toggle has keyboard focus + # **installPlugin** Installs a plugin from the WP.org repository. @@ -315,6 +344,10 @@ _Returns_ - `Promise`: Promise that uses `mockCheck` to see if a request should be mocked with `mock`, and optionally transforms the response with `responseObjectTransform`. +# **navigateToContentEditorTop** + +Undocumented declaration. + # **observeFocusLoss** Binds to the document on page load which throws an error if a `focusout` @@ -469,6 +502,59 @@ running the test is not already the admin user). Switches the current user to whichever user we should be running the tests as (if we're not already that user). +# **tabThroughBlock** + +Tabs through a content block and asserts that the external wrapper, inserter toggle, mover controls, and toolbar buttons all receive keyboard focus. + +_Parameters_ + +- _blockType_ `string`: The expected value of the data-type attribute of the block's external wrapper + +# **tabThroughBlockMoverControl** + +Navigates through the block mover control using the keyboard. Asserts that the 'move up' and 'move down' controls receive focus. + +# **tabThroughBlockToolbar** + +Navigate through a block's toolbar using the keyboard. Asserts that each button receives focus. + +# **tabThroughFileBlock** + +Tabs through a content block with file upload buttons, such as an Image, Gallery, Audio, or Cover block + +_Parameters_ + +- _blockType_ `string`: The expected value of the data-type attribute of the block's external wrapper + +# **tabThroughPlaceholderButtons** + +Tabs through the file upload buttons that appear in a file content block's placeholder area + +# **tabThroughTextBlock** + +Tabs through a content block with text content areas, such as a Heading, Quote, or Paragraph block. Asserts that the text content areas all receive focus. + +_Parameters_ + +- _blockType_ `string`: The expected value of the data-type attribute of the block's external wrapper +- _content_ `string`: The expected title of the block + +# **textContentAreas** + +Returns a list of a block's contenteditable elements. + +_Parameters_ + +- _empty_ `boolean`: When true, restricts the list to contenteditable elements with no value + +# **textContentAreasHaveFocus** + +Tabs through the text content areas of a block and asserts the expected values + +_Parameters_ + +- _content_ `string`: The expected value of the block's contenteditable elements + # **toggleScreenOption** Toggles the screen option with the given label. diff --git a/packages/e2e-test-utils/src/external-wrapper-has-focus.js b/packages/e2e-test-utils/src/external-wrapper-has-focus.js new file mode 100644 index 00000000000000..ef7bb4adb39e31 --- /dev/null +++ b/packages/e2e-test-utils/src/external-wrapper-has-focus.js @@ -0,0 +1,10 @@ +/** + * Asserts that the element with keyboard focus is a block's external wrapper + * + * @param {string} blockType The expected value of the data-type attribute of the block's external wrapper + */ + +export async function externalWrapperHasFocus( blockType ) { + const activeElementDataType = await page.evaluate( () => document.activeElement.dataset.type ); + await expect( activeElementDataType ).toEqual( blockType ); +} diff --git a/packages/e2e-test-utils/src/get-element-list.js b/packages/e2e-test-utils/src/get-element-list.js new file mode 100644 index 00000000000000..9f5682649faecb --- /dev/null +++ b/packages/e2e-test-utils/src/get-element-list.js @@ -0,0 +1,16 @@ +/*eslint no-shadow: ["error", { "allow": ["selector"] }]*/ + +/** + * Returns an array of classnames that match a given selector on the current page + * + * @param {string} selector The selector to query on the page + */ +export const getElementSelectorList = async ( selector ) => { + return await page.evaluate( ( selector ) => { + return Array.from( + document.querySelectorAll( + selector + ) + ).map( ( elem ) => elem.className ); + }, selector ); +}; diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index 64e0a22a585b1e..83be1a81a4ee38 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -14,6 +14,7 @@ export { dragAndResize } from './drag-and-resize'; export { enablePageDialogAccept } from './enable-page-dialog-accept'; export { enablePrePublishChecks } from './enable-pre-publish-checks'; export { ensureSidebarOpened } from './ensure-sidebar-opened'; +export { externalWrapperHasFocus } from './external-wrapper-has-focus'; export { findSidebarPanelToggleButtonWithTitle } from './find-sidebar-panel-toggle-button-with-title'; export { findSidebarPanelWithTitle } from './find-sidebar-panel-with-title'; export { getAllBlockInserterItemTitles } from './get-all-block-inserter-item-titles'; @@ -21,12 +22,16 @@ export { getAllBlocks } from './get-all-blocks'; export { getAvailableBlockTransforms } from './get-available-block-transforms'; export { getBlockSetting } from './get-block-setting'; export { getEditedPostContent } from './get-edited-post-content'; +export { getElementSelectorList } from './get-element-list'; export { hasBlockSwitcher } from './has-block-switcher'; +export { insertAndPopulateBlock } from './insert-and-populate-block'; export { insertBlock } from './insert-block'; +export { inserterToggleHasFocus } from './inserter-toggle-has-focus'; export { installPlugin } from './install-plugin'; export { isCurrentURL } from './is-current-url'; export { isInDefaultBlock } from './is-in-default-block'; export { loginUser } from './login-user'; +export { navigateToContentEditorTop } from './navigate-to-content-editor-top'; export { observeFocusLoss } from './observe-focus-loss'; export { openAllBlockInserterCategories } from './open-all-block-inserter-categories'; export { openDocumentSettingsSidebar } from './open-document-settings-sidebar'; @@ -44,6 +49,14 @@ export { setPostContent } from './set-post-content'; export { switchEditorModeTo } from './switch-editor-mode-to'; export { switchUserToAdmin } from './switch-user-to-admin'; export { switchUserToTest } from './switch-user-to-test'; +export { tabThroughBlock } from './tab-through-block'; +export { tabThroughBlockMoverControl } from './tab-through-block-mover-control'; +export { tabThroughBlockToolbar } from './tab-through-block-toolbar'; +export { tabThroughFileBlock } from './tab-through-file-block'; +export { tabThroughPlaceholderButtons } from './tab-through-placeholder-buttons'; +export { tabThroughTextBlock } from './tab-through-text-block'; +export { textContentAreas } from './text-content-areas'; +export { textContentAreasHaveFocus } from './text-content-areas-have-focus'; export { toggleScreenOption } from './toggle-screen-option'; export { transformBlockTo } from './transform-block-to'; export { uninstallPlugin } from './uninstall-plugin'; diff --git a/packages/e2e-test-utils/src/insert-and-populate-block.js b/packages/e2e-test-utils/src/insert-and-populate-block.js new file mode 100644 index 00000000000000..65bd3e42c91b7f --- /dev/null +++ b/packages/e2e-test-utils/src/insert-and-populate-block.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { + insertBlock, + textContentAreas, +} from '@wordpress/e2e-test-utils'; + +/** + * Inserts a content block and then, if it has text content areas, fills them with text + * + * @param {string} blockName The type of block to insert + * @param {string} content The text to enter into each contenteditable area + */ +export async function insertAndPopulateBlock( blockName, content ) { + await insertBlock( blockName ); + // typing populates the first content area + await page.keyboard.type( content ); + + // if there are more contenteditable elements, select and populate them too: + const blocks = await textContentAreas( { empty: true } ); + + for ( let i = 0; i < blocks.length; i++ ) { + await page.keyboard.press( 'Tab' ); + await page.keyboard.type( content ); + } + await page.keyboard.press( 'Enter' ); +} diff --git a/packages/e2e-test-utils/src/inserter-toggle-has-focus.js b/packages/e2e-test-utils/src/inserter-toggle-has-focus.js new file mode 100644 index 00000000000000..77deaf219605aa --- /dev/null +++ b/packages/e2e-test-utils/src/inserter-toggle-has-focus.js @@ -0,0 +1,8 @@ +/** + * Asserts that a content block's inserter toggle has keyboard focus + */ + +export async function inserterToggleHasFocus() { + const isFocusedInserterToggle = await page.evaluate( () => document.activeElement.classList.contains( 'block-editor-inserter__toggle' ) ); + await expect( isFocusedInserterToggle ).toBe( true ); +} diff --git a/packages/e2e-test-utils/src/navigate-to-content-editor-top.js b/packages/e2e-test-utils/src/navigate-to-content-editor-top.js new file mode 100644 index 00000000000000..3b74f1c840fdcd --- /dev/null +++ b/packages/e2e-test-utils/src/navigate-to-content-editor-top.js @@ -0,0 +1,17 @@ +/** + * Navigates to the top of the content editor using the keyboard. + */ + +/** + * Internal dependencies + */ +import { pressKeyWithModifier } from './press-key-with-modifier'; + +export async function navigateToContentEditorTop() { + // Use 'Ctrl+`' to return to the top of the editor + await pressKeyWithModifier( 'ctrl', '`' ); + await pressKeyWithModifier( 'ctrl', '`' ); + + // Tab into the Title block + await page.keyboard.press( 'Tab' ); +} diff --git a/packages/e2e-test-utils/src/tab-through-block-mover-control.js b/packages/e2e-test-utils/src/tab-through-block-mover-control.js new file mode 100644 index 00000000000000..6ddb983cb902c5 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-block-mover-control.js @@ -0,0 +1,18 @@ +/** + * Navigates through the block mover control using the keyboard. Asserts that the 'move up' and 'move down' controls receive focus. + */ +export async function tabThroughBlockMoverControl() { + // Tab to focus on the 'move up' control + await page.keyboard.press( 'Tab' ); + const isFocusedMoveUpControl = await page.evaluate( () => + document.activeElement.classList.contains( 'block-editor-block-mover__control' ) + ); + await expect( isFocusedMoveUpControl ).toBe( true ); + + // Tab to focus on the 'move down' control + await page.keyboard.press( 'Tab' ); + const isFocusedMoveDownControl = await page.evaluate( () => + document.activeElement.classList.contains( 'block-editor-block-mover__control' ) + ); + await expect( isFocusedMoveDownControl ).toBe( true ); +} diff --git a/packages/e2e-test-utils/src/tab-through-block-toolbar.js b/packages/e2e-test-utils/src/tab-through-block-toolbar.js new file mode 100644 index 00000000000000..7ffb1dc694d225 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-block-toolbar.js @@ -0,0 +1,20 @@ +/** + * Navigate through a block's toolbar using the keyboard. Asserts that each button receives focus. + */ + +export async function tabThroughBlockToolbar() { + const blockToolbarButtons = await page.evaluate( () => { + // return an array with the classNames of the block toolbar's buttons + return [].slice.call( + document.querySelectorAll( '.block-editor-block-contextual-toolbar button:not([disabled])' ) + ).map( ( elem ) => elem.className ); + } ); + + for ( const buttonClassName of blockToolbarButtons ) { + await page.keyboard.press( 'Tab' ); + const focusedBlockToolBarButton = await page.evaluate( () => + document.activeElement.className + ); + await expect( focusedBlockToolBarButton ).toEqual( buttonClassName ); + } +} diff --git a/packages/e2e-test-utils/src/tab-through-block.js b/packages/e2e-test-utils/src/tab-through-block.js new file mode 100644 index 00000000000000..54a3962e867038 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-block.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { + externalWrapperHasFocus, + inserterToggleHasFocus, + tabThroughBlockMoverControl, + tabThroughBlockToolbar, +} from '@wordpress/e2e-test-utils'; + +/** + * Tabs through a content block and asserts that the external wrapper, inserter toggle, mover controls, and toolbar buttons all receive keyboard focus. + * + * @param {string} blockType The expected value of the data-type attribute of the block's external wrapper + */ + +export async function tabThroughBlock( blockType ) { + // Tab to the next block + await page.keyboard.press( 'Tab' ); + await externalWrapperHasFocus( blockType ); + + // Tab causes 'add block' button to receive focus + await page.keyboard.press( 'Tab' ); + await inserterToggleHasFocus(); + + await tabThroughBlockMoverControl(); + await tabThroughBlockToolbar(); +} diff --git a/packages/e2e-test-utils/src/tab-through-file-block.js b/packages/e2e-test-utils/src/tab-through-file-block.js new file mode 100644 index 00000000000000..bd17b212895bf6 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-file-block.js @@ -0,0 +1,17 @@ + +/** + * Internal dependencies + */ + +import { tabThroughBlock } from './tab-through-block'; +import { tabThroughPlaceholderButtons } from './tab-through-placeholder-buttons'; + +/** + * Tabs through a content block with file upload buttons, such as an Image, Gallery, Audio, or Cover block + * + * @param {string} blockType The expected value of the data-type attribute of the block's external wrapper + */ +export async function tabThroughFileBlock( blockType ) { + await tabThroughBlock( blockType ); + await tabThroughPlaceholderButtons(); +} diff --git a/packages/e2e-test-utils/src/tab-through-placeholder-buttons.js b/packages/e2e-test-utils/src/tab-through-placeholder-buttons.js new file mode 100644 index 00000000000000..14e1107b4f8b88 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-placeholder-buttons.js @@ -0,0 +1,21 @@ +/** + * WordPress dependencies + */ +import { + getElementSelectorList, +} from '@wordpress/e2e-test-utils'; + +/** + * Tabs through the file upload buttons that appear in a file content block's placeholder area + */ +export const tabThroughPlaceholderButtons = async () => { + const placeholderButtons = await getElementSelectorList( '.wp-block.is-selected .block-editor-media-placeholder button:not([disabled])' ); + + for ( const buttonClassName of placeholderButtons ) { + await page.keyboard.press( 'Tab' ); + const focusePlaceholderButton = await page.evaluate( () => + document.activeElement.className + ); + await expect( focusePlaceholderButton ).toEqual( buttonClassName ); + } +}; diff --git a/packages/e2e-test-utils/src/tab-through-text-block.js b/packages/e2e-test-utils/src/tab-through-text-block.js new file mode 100644 index 00000000000000..f7953051e2b469 --- /dev/null +++ b/packages/e2e-test-utils/src/tab-through-text-block.js @@ -0,0 +1,20 @@ +/** + * Internal dependencies + */ +import { tabThroughBlock } from './tab-through-block'; +import { textContentAreasHaveFocus } from './text-content-areas-have-focus'; + +/** + * Tabs through a content block with text content areas, such as a Heading, Quote, or Paragraph block. Asserts that the text content areas all receive focus. + * + * @param {string} blockType The expected value of the data-type attribute of the block's external wrapper + * @param {string} content The expected title of the block + */ + +export async function tabThroughTextBlock( blockType, content ) { + await tabThroughBlock( blockType ); + + // Tab causes the block text content to receive focus + await page.keyboard.press( 'Tab' ); + await textContentAreasHaveFocus( content ); +} diff --git a/packages/e2e-test-utils/src/text-content-areas-have-focus.js b/packages/e2e-test-utils/src/text-content-areas-have-focus.js new file mode 100644 index 00000000000000..1633f1d19d3a46 --- /dev/null +++ b/packages/e2e-test-utils/src/text-content-areas-have-focus.js @@ -0,0 +1,26 @@ +/** + * Internal dependencies + */ +import { textContentAreas } from './text-content-areas'; + +/** + * Tabs through the text content areas of a block and asserts the expected values + * + * @param {string} content The expected value of the block's contenteditable elements + */ + +export async function textContentAreasHaveFocus( content ) { + const blocks = await textContentAreas( { empty: false } ); + const isFocusedTextContentArea = await page.evaluate( () => document.activeElement.contentEditable ); + const textContentAreaContent = await page.evaluate( () => document.activeElement.innerHTML ); + + for ( let i = 0; i < blocks.length; i++ ) { + if ( i > 0 ) { + await page.keyboard.press( 'Tab' ); + } + + // The value of 'contentEditable' should be the string 'true' + await expect( isFocusedTextContentArea ).toBe( 'true' ); + await expect( textContentAreaContent ).toContain( content ); + } +} diff --git a/packages/e2e-test-utils/src/text-content-areas.js b/packages/e2e-test-utils/src/text-content-areas.js new file mode 100644 index 00000000000000..2aa83c6fad7fcd --- /dev/null +++ b/packages/e2e-test-utils/src/text-content-areas.js @@ -0,0 +1,21 @@ +/** + * Internal dependencies + */ +import { getElementSelectorList } from './get-element-list'; + +/** + * Returns a list of a block's contenteditable elements. + * + * @param {boolean} empty When true, restricts the list to contenteditable elements with no value + */ + +export async function textContentAreas( { empty = false } ) { + const selectors = [ + '.wp-block.is-selected [contenteditable]', + '.wp-block.is-typing [contenteditable]', + ].map( ( selector ) => { + return empty ? selector + '[data-is-placeholder-visible="true"]' : selector; + }, empty ).join( ',' ); + + return await getElementSelectorList( selectors ); +} diff --git a/packages/e2e-tests/specs/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/keyboard-navigable-blocks.test.js index a8ebe96c34fcd2..fc69f63e52804d 100644 --- a/packages/e2e-tests/specs/keyboard-navigable-blocks.test.js +++ b/packages/e2e-tests/specs/keyboard-navigable-blocks.test.js @@ -3,121 +3,41 @@ */ import { createNewPost, - insertBlock, - pressKeyWithModifier, + insertAndPopulateBlock, + navigateToContentEditorTop, + tabThroughTextBlock, + tabThroughFileBlock, } from '@wordpress/e2e-test-utils'; -const navigateToContentEditorTop = async () => { - // Use 'Ctrl+`' to return to the top of the editor - await pressKeyWithModifier( 'ctrl', '`' ); - await pressKeyWithModifier( 'ctrl', '`' ); - - // Tab into the Title block - await page.keyboard.press( 'Tab' ); -}; - -const tabThroughParagraphBlock = async ( paragraphText ) => { - // Tab to the next paragraph block - await page.keyboard.press( 'Tab' ); - - // The block external focusable wrapper has focus - const isFocusedParagraphBlock = await page.evaluate( - () => document.activeElement.dataset.type - ); - await expect( isFocusedParagraphBlock ).toEqual( 'core/paragraph' ); - - // Tab causes 'add block' button to receive focus - await page.keyboard.press( 'Tab' ); - const isFocusedParagraphInserterToggle = await page.evaluate( () => - document.activeElement.classList.contains( 'block-editor-inserter__toggle' ) - ); - await expect( isFocusedParagraphInserterToggle ).toBe( true ); - - await tabThroughBlockMoverControl(); - await tabThroughBlockToolbar(); - - // Tab causes the paragraph content to receive focus - await page.keyboard.press( 'Tab' ); - const isFocusedParagraphContent = await page.evaluate( - () => document.activeElement.contentEditable - ); - // The value of 'contentEditable' should be the string 'true' - await expect( isFocusedParagraphContent ).toBe( 'true' ); - - const paragraphEditableContent = await page.evaluate( - () => document.activeElement.innerHTML - ); - await expect( paragraphEditableContent ).toBe( paragraphText ); -}; - -const tabThroughBlockMoverControl = async () => { - // Tab to focus on the 'move up' control - await page.keyboard.press( 'Tab' ); - const isFocusedMoveUpControl = await page.evaluate( () => - document.activeElement.classList.contains( 'block-editor-block-mover__control' ) - ); - await expect( isFocusedMoveUpControl ).toBe( true ); - - // Tab to focus on the 'move down' control - await page.keyboard.press( 'Tab' ); - const isFocusedMoveDownControl = await page.evaluate( () => - document.activeElement.classList.contains( 'block-editor-block-mover__control' ) - ); - await expect( isFocusedMoveDownControl ).toBe( true ); -}; - -const tabThroughBlockToolbar = async () => { - // Tab to focus on the 'block switcher' control - await page.keyboard.press( 'Tab' ); - const isFocusedBlockSwitcherControl = await page.evaluate( () => - document.activeElement.classList.contains( - 'block-editor-block-switcher__toggle' - ) - ); - await expect( isFocusedBlockSwitcherControl ).toBe( true ); - - // Tab to focus on the 'Change text alignment' dropdown control - await page.keyboard.press( 'Tab' ); - const isFocusedChangeTextAlignmentControl = await page.evaluate( () => - document.activeElement.classList.contains( 'components-dropdown-menu__toggle' ) - ); - await expect( isFocusedChangeTextAlignmentControl ).toBe( true ); - - // Tab to focus on the 'More formatting' dropdown toggle - await page.keyboard.press( 'Tab' ); - const isFocusedBlockSettingsDropdown = await page.evaluate( () => - document.activeElement.classList.contains( 'components-dropdown-menu__toggle' ) - ); - await expect( isFocusedBlockSettingsDropdown ).toBe( true ); -}; +/** + * External dependencies + */ describe( 'Order of block keyboard navigation', () => { beforeEach( async () => { await createNewPost(); } ); + it( 'permits tabbing through blocks in the expected order', async () => { + await insertAndPopulateBlock( 'Heading', 'Heading Block Content' ); + await insertAndPopulateBlock( 'Quote', 'Quote Block Content' ); + await insertAndPopulateBlock( 'List', 'List Block Content' ); + await insertAndPopulateBlock( 'Paragraph', 'Paragraph Block Content' ); + await insertAndPopulateBlock( 'Image', 'Image Block Content' ); + await insertAndPopulateBlock( 'Gallery', 'Gallery Block Content' ); + await insertAndPopulateBlock( 'Audio', 'Audio Block Content' ); + await insertAndPopulateBlock( 'Cover', 'Cover Block Content' ); + await insertAndPopulateBlock( 'File', 'File Block Content' ); - it( 'permits tabbing through paragraph blocks in the expected order', async () => { - const paragraphBlocks = [ 'Paragraph 0', 'Paragraph 1', 'Paragraph 2' ]; - - // create 3 paragraphs blocks with some content - for ( const paragraphBlock of paragraphBlocks ) { - await insertBlock( 'Paragraph' ); - await page.keyboard.type( paragraphBlock ); - await page.keyboard.press( 'Enter' ); - } - - await navigateToContentEditorTop(); - - for ( const paragraphBlock of paragraphBlocks ) { - await tabThroughParagraphBlock( paragraphBlock ); - } - - // Repeat the same steps to ensure that there is no change introduced in how the focus is handled. - // This prevents the previous regression explained in: https://github.com/WordPress/gutenberg/issues/11773. await navigateToContentEditorTop(); - for ( const paragraphBlock of paragraphBlocks ) { - await tabThroughParagraphBlock( paragraphBlock ); - } + await tabThroughTextBlock( 'core/heading', 'Heading Block Content' ); + await tabThroughTextBlock( 'core/quote', 'Quote Block Content' ); + await tabThroughTextBlock( 'core/list', 'List Block Content' ); + await tabThroughTextBlock( 'core/paragraph', 'Paragraph Block Content' ); + await tabThroughFileBlock( 'core/image' ); + await tabThroughFileBlock( 'core/gallery' ); + await tabThroughFileBlock( 'core/audio' ); + await tabThroughFileBlock( 'core/cover' ); + await tabThroughFileBlock( 'core/file' ); } ); } ); diff --git a/packages/e2e-tests/specs/keyboard-navigable-content-editor.test.js b/packages/e2e-tests/specs/keyboard-navigable-content-editor.test.js new file mode 100644 index 00000000000000..82e6ecfef19eed --- /dev/null +++ b/packages/e2e-tests/specs/keyboard-navigable-content-editor.test.js @@ -0,0 +1,42 @@ +/** + * WordPress dependencies + */ +import { + createNewPost, + insertAndPopulateBlock, + navigateToContentEditorTop, + tabThroughTextBlock, +} from '@wordpress/e2e-test-utils'; + +/** + * External dependencies + */ + +describe( 'Order of block keyboard navigation', () => { + beforeEach( async () => { + await createNewPost(); + } ); + + it( 'permits tabbing through paragraph blocks and the top of the content in the expected order', async () => { + const paragraphBlocks = [ 'Paragraph 0', 'Paragraph 1', 'Paragraph 2' ]; + + // create 3 paragraphs blocks with some content + for ( const paragraphBlock of paragraphBlocks ) { + await insertAndPopulateBlock( 'Paragraph', paragraphBlock ); + } + + await navigateToContentEditorTop(); + + for ( const paragraphBlock of paragraphBlocks ) { + await tabThroughTextBlock( 'core/paragraph', paragraphBlock ); + } + + // Repeat the same steps to ensure that there is no change introduced in how the focus is handled. + // This prevents the previous regression explained in: https://github.com/WordPress/gutenberg/issues/11773. + await navigateToContentEditorTop(); + + for ( const paragraphBlock of paragraphBlocks ) { + await tabThroughTextBlock( 'core/paragraph', paragraphBlock ); + } + } ); +} );