diff --git a/packages/e2e-test-utils-playwright/src/admin/index.ts b/packages/e2e-test-utils-playwright/src/admin/index.ts index 41289f6a0bbbb..ee7ebe406fc25 100644 --- a/packages/e2e-test-utils-playwright/src/admin/index.ts +++ b/packages/e2e-test-utils-playwright/src/admin/index.ts @@ -33,8 +33,8 @@ export class Admin { this.pageUtils = pageUtils; } - createNewPost = createNewPost; - getPageError = getPageError; - visitAdminPage = visitAdminPage; - visitSiteEditor = visitSiteEditor; + createNewPost = createNewPost.bind( this ); + getPageError = getPageError.bind( this ); + visitAdminPage = visitAdminPage.bind( this ); + visitSiteEditor = visitSiteEditor.bind( this ); } diff --git a/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts b/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts index ab66de030feb2..17fccd4c59918 100644 --- a/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts +++ b/packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts @@ -8,7 +8,7 @@ import { addQueryArgs } from '@wordpress/url'; */ import type { Admin } from './'; -interface SiteEditorQueryParams { +export interface SiteEditorQueryParams { postId: string | number; postType: string; } diff --git a/packages/e2e-test-utils-playwright/src/editor/index.ts b/packages/e2e-test-utils-playwright/src/editor/index.ts index 680ad831221e1..6bae1b0be5f37 100644 --- a/packages/e2e-test-utils-playwright/src/editor/index.ts +++ b/packages/e2e-test-utils-playwright/src/editor/index.ts @@ -16,6 +16,7 @@ import { publishPost } from './publish-post'; import { selectBlocks } from './select-blocks'; import { showBlockToolbar } from './show-block-toolbar'; import { saveSiteEditorEntities } from './site-editor'; +import { transformBlockTo } from './transform-block-to'; type EditorConstructorProps = { page: Page; @@ -52,7 +53,6 @@ export class Editor { return frame; } - clickBlockOptionsMenuItem = clickBlockOptionsMenuItem.bind( this ); clickBlockToolbarButton = clickBlockToolbarButton.bind( this ); getEditedPostContent = getEditedPostContent.bind( this ); @@ -63,4 +63,5 @@ export class Editor { saveSiteEditorEntities = saveSiteEditorEntities.bind( this ); selectBlocks = selectBlocks.bind( this ); showBlockToolbar = showBlockToolbar.bind( this ); + transformBlockTo = transformBlockTo.bind( this ); } diff --git a/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts b/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts new file mode 100644 index 0000000000000..e5f4c64d7c6bc --- /dev/null +++ b/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +import type { Editor } from './index'; + +/** + * Clicks the default block appender. + * + * @param {Editor} this + * @param {string} name Block name. + */ +export async function transformBlockTo( this: Editor, name: string ) { + await this.page.evaluate( + ( [ blockName ] ) => { + // @ts-ignore (Reason: wp isn't typed) + const clientIds = window.wp.data + .select( 'core/block-editor' ) + .getSelectedBlockClientIds(); + // @ts-ignore (Reason: wp isn't typed) + const blocks = window.wp.data + .select( 'core/block-editor' ) + .getBlocksByClientId( clientIds ); + // @ts-ignore (Reason: wp isn't typed) + window.wp.data.dispatch( 'core/block-editor' ).replaceBlocks( + clientIds, + // @ts-ignore (Reason: wp isn't typed) + window.wp.blocks.switchToBlockType( blocks, blockName ) + ); + }, + [ name ] + ); +} 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 c3c2f62eb0620..d147365fe5e0e 100644 --- a/packages/e2e-test-utils-playwright/src/page-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/page-utils/index.ts @@ -29,11 +29,11 @@ class PageUtils { this.browser = this.context.browser()!; } - isCurrentURL = isCurrentURL; - pressKeyTimes = pressKeyTimes; - pressKeyWithModifier = pressKeyWithModifier; - setBrowserViewport = setBrowserViewport; - setClipboardData = setClipboardData; + isCurrentURL = isCurrentURL.bind( this ); + pressKeyTimes = pressKeyTimes.bind( this ); + pressKeyWithModifier = pressKeyWithModifier.bind( this ); + setBrowserViewport = setBrowserViewport.bind( this ); + setClipboardData = setClipboardData.bind( this ); } export { PageUtils }; diff --git a/packages/e2e-test-utils-playwright/src/request-utils/index.ts b/packages/e2e-test-utils-playwright/src/request-utils/index.ts index 8f35d0be38de6..151013a8fe000 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/index.ts @@ -108,25 +108,27 @@ class RequestUtils { this.baseURL = baseURL; } - login = login; - setupRest = setupRest; - rest = rest; - getMaxBatchSize = getMaxBatchSize; - batchRest = batchRest; - getPluginsMap = getPluginsMap; - activatePlugin = activatePlugin; - deactivatePlugin = deactivatePlugin; - activateTheme = activateTheme; + login = login.bind( this ); + setupRest = setupRest.bind( this ); + // .bind() drops the generic types. Re-casting it to keep the type signature. + rest = rest.bind( this ) as typeof rest; + getMaxBatchSize = getMaxBatchSize.bind( this ); + // .bind() drops the generic types. Re-casting it to keep the type signature. + batchRest = batchRest.bind( this ) as typeof batchRest; + getPluginsMap = getPluginsMap.bind( this ); + activatePlugin = activatePlugin.bind( this ); + deactivatePlugin = deactivatePlugin.bind( this ); + activateTheme = activateTheme.bind( this ); deleteAllBlocks = deleteAllBlocks; - deleteAllPosts = deleteAllPosts; - deleteAllWidgets = deleteAllWidgets; - addWidgetBlock = addWidgetBlock; - deleteAllTemplates = deleteAllTemplates; - resetPreferences = resetPreferences; - listMedia = listMedia; - uploadMedia = uploadMedia; - deleteMedia = deleteMedia; - deleteAllMedia = deleteAllMedia; + deleteAllPosts = deleteAllPosts.bind( this ); + deleteAllWidgets = deleteAllWidgets.bind( this ); + addWidgetBlock = addWidgetBlock.bind( this ); + deleteAllTemplates = deleteAllTemplates.bind( this ); + resetPreferences = resetPreferences.bind( this ); + listMedia = listMedia.bind( this ); + uploadMedia = uploadMedia.bind( this ); + deleteMedia = deleteMedia.bind( this ); + deleteAllMedia = deleteAllMedia.bind( this ); } export type { StorageState }; diff --git a/packages/e2e-test-utils-playwright/src/request-utils/media.ts b/packages/e2e-test-utils-playwright/src/request-utils/media.ts index a93f4afd09503..ac4fb0827b92a 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/media.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/media.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; */ import type { RequestUtils } from './index'; -interface Media { +export interface Media { id: number; title: { raw: string; diff --git a/packages/e2e-test-utils-playwright/src/request-utils/rest.ts b/packages/e2e-test-utils-playwright/src/request-utils/rest.ts index f9633f0a5fe31..6fa0467c303d5 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/rest.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/rest.ts @@ -70,7 +70,7 @@ type RequestFetchOptions = Exclude< Parameters< APIRequestContext[ 'fetch' ] >[ 1 ], undefined >; -interface RestOptions extends RequestFetchOptions { +export interface RestOptions extends RequestFetchOptions { path: string; } @@ -152,7 +152,7 @@ async function getMaxBatchSize( this: RequestUtils, forceRefetch = false ) { return this.maxBatchSize; } -interface BatchRequest { +export interface BatchRequest { method?: string; path: string; headers?: Record< string, string | string[] >; diff --git a/packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap deleted file mode 100644 index a07d125536586..0000000000000 --- a/packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap +++ /dev/null @@ -1,337 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`List can be converted to a quote 1`] = ` -" -

one

two

-" -`; - -exports[`List can be converted to paragraphs 1`] = ` -" -

one

- - - -

two

-" -`; - -exports[`List can be converted when nested to paragraphs 1`] = ` -" -

one

- - - -

two

-" -`; - -exports[`List can be created by converting a paragraph 1`] = ` -" - -" -`; - -exports[`List can be created by converting a paragraph with line breaks 1`] = ` -" - -" -`; - -exports[`List can be created by converting a quote 1`] = ` -" - -" -`; - -exports[`List can be created by converting multiple paragraphs 1`] = ` -" - -" -`; - -exports[`List can be created by typing "/list" 1`] = ` -" - -" -`; - -exports[`List can be created by typing an asterisk in front of text of a paragraph block 1`] = ` -" - -" -`; - -exports[`List can be created by using a number at the start of a paragraph block 1`] = ` -" -
  1. A list item
-" -`; - -exports[`List can be created by using an asterisk at the start of a paragraph block 1`] = ` -" - -" -`; - -exports[`List can undo asterisk transform 1`] = ` -" -

1.

-" -`; - -exports[`List first empty list item is graciously removed 1`] = ` -" - -" -`; - -exports[`List should be immeadiately saved on indentation 1`] = ` -" - -" -`; - -exports[`List should change the base list type 1`] = ` -" -
-" -`; - -exports[`List should change the indented list type 1`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 1`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 2`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 3`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 4`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 5`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 6`] = ` -" - -" -`; - -exports[`List should create and remove indented list with keyboard only 7`] = `""`; - -exports[`List should create paragraph on split at end and merge back with content 1`] = ` -" - - - - -

-" -`; - -exports[`List should create paragraph on split at end and merge back with content 2`] = ` -" - -" -`; - -exports[`List should indent and outdent level 1 1`] = ` -" - -" -`; - -exports[`List should indent and outdent level 1 2`] = ` -" - -" -`; - -exports[`List should indent and outdent level 2 1`] = ` -" - -" -`; - -exports[`List should indent and outdent level 2 2`] = ` -" - -" -`; - -exports[`List should indent and outdent level 2 3`] = ` -" - -" -`; - -exports[`List should insert a line break on shift+enter 1`] = ` -" - -" -`; - -exports[`List should insert a line break on shift+enter in a non trailing list item 1`] = ` -" - -" -`; - -exports[`List should not change the contents when you change the list type to Unordered 1`] = `"
  • a
  • b
  • c
  • "`; - -exports[`List should not change the contents when you change the list type to Ordered 1`] = `"
  • 1
  • 2
  • 3
  • "`; - -exports[`List should not indent list on space with modifier 1`] = ` -" - -" -`; - -exports[`List should not transform lines in block when transforming multiple blocks 1`] = ` -" - -" -`; - -exports[`List should not undo asterisk transform with backspace after selection change 1`] = `""`; - -exports[`List should not undo asterisk transform with backspace after typing 1`] = `""`; - -exports[`List should only convert to list when shortcut ends with space 1`] = ` -" -

    -" -`; - -exports[`List should outdent with children 1`] = ` -" - -" -`; - -exports[`List should outdent with children 2`] = ` -" - -" -`; - -exports[`List should place the caret in the right place with nested list 1`] = ` -" - -" -`; - -exports[`List should preserve indentation after merging backward and forward 1`] = ` -" - -" -`; - -exports[`List should preserve indentation after merging backward and forward 2`] = ` -" - -" -`; - -exports[`List should split indented list item 1`] = ` -" - -" -`; - -exports[`List should split into two ordered lists with paragraph 1`] = ` -" -
    1. one
    - - - -

    - - - -
    1. two
    -" -`; - -exports[`List should split into two with paragraph and merge lists 1`] = ` -" - -" -`; - -exports[`List should split into two with paragraph and merge lists 2`] = ` -" - - - - -

    - - - - -" -`; - -exports[`List should split into two with paragraph and merge lists 3`] = ` -" - - - - - -" -`; - -exports[`List should undo asterisk transform with backspace 1`] = ` -" -

    *

    -" -`; - -exports[`List should undo asterisk transform with backspace after selection changes 1`] = ` -" -

    *

    -" -`; - -exports[`List should undo asterisk transform with backspace after selection changes without requestIdleCallback 1`] = ` -" -

    *

    -" -`; - -exports[`List should undo asterisk transform with backspace setting isTyping state 1`] = ` -" -

    *

    -" -`; - -exports[`List should undo asterisk transform with escape 1`] = ` -" -

    *

    -" -`; diff --git a/packages/e2e-tests/specs/editor/blocks/list.test.js b/packages/e2e-tests/specs/editor/blocks/list.test.js deleted file mode 100644 index 59f0607789c31..0000000000000 --- a/packages/e2e-tests/specs/editor/blocks/list.test.js +++ /dev/null @@ -1,542 +0,0 @@ -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - clickBlockToolbarButton, - getEditedPostContent, - createNewPost, - pressKeyTimes, - transformBlockTo, - pressKeyWithModifier, - insertBlock, - showBlockToolbar, -} from '@wordpress/e2e-test-utils'; - -describe( 'List', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'can be created by using an asterisk at the start of a paragraph block', async () => { - // Create a block with some text that will trigger a list creation. - await clickBlockAppender(); - await page.keyboard.type( '* A list item' ); - - // Create a second list item. - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'Another list item' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by typing an asterisk in front of text of a paragraph block', async () => { - // Create a list with the slash block shortcut. - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - await pressKeyTimes( 'ArrowLeft', 4 ); - await page.keyboard.type( '* ' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by using a number at the start of a paragraph block', async () => { - // Create a block with some text that will trigger a list creation. - await clickBlockAppender(); - await page.keyboard.type( '1) A list item' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can undo asterisk transform', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1. ' ); - await pressKeyWithModifier( 'primary', 'z' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should undo asterisk transform with backspace', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* ' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should undo asterisk transform with backspace after selection changes', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* ' ); - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should undo asterisk transform with backspace setting isTyping state', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* ' ); - await showBlockToolbar(); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should undo asterisk transform with backspace after selection changes without requestIdleCallback', async () => { - await clickBlockAppender(); - await page.evaluate( () => delete window.requestIdleCallback ); - await page.keyboard.type( '* ' ); - await new Promise( ( resolve ) => setTimeout( resolve, 100 ) ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should undo asterisk transform with escape', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* ' ); - await page.keyboard.press( 'Escape' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should not undo asterisk transform with backspace after typing', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* a' ); - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should not undo asterisk transform with backspace after selection change', async () => { - await clickBlockAppender(); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '* ' ); - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'ArrowDown' ); - await page.keyboard.press( 'Backspace' ); - - // Expect list to be deleted. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by typing "/list"', async () => { - // Create a list with the slash block shortcut. - await clickBlockAppender(); - await page.keyboard.type( '/list' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'List')]` - ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'I’m a list' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting a paragraph', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - await transformBlockTo( 'List' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting multiple paragraphs', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]' ); - await page.keyboard.up( 'Shift' ); - await transformBlockTo( 'List' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting a paragraph with line breaks', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'one' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.type( 'two' ); - await transformBlockTo( 'List' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should not transform lines in block when transforming multiple blocks', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'one' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.type( '...' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]' ); - await page.keyboard.up( 'Shift' ); - await transformBlockTo( 'List' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be converted to paragraphs', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await transformBlockTo( 'Paragraph' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be converted when nested to paragraphs', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await clickBlockToolbarButton( 'Indent' ); - await page.keyboard.type( 'two' ); - await transformBlockTo( 'Paragraph' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting a quote', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await transformBlockTo( 'List' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be converted to a quote', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await transformBlockTo( 'Quote' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should create paragraph on split at end and merge back with content', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.type( 'two' ); - await pressKeyTimes( 'ArrowLeft', 'two'.length ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should split into two with paragraph and merge lists', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // Should remove paragraph without creating empty list item. - await page.keyboard.press( 'Backspace' ); - - // Should merge lists into one. - await page.keyboard.press( 'ArrowDown' ); - await pressKeyTimes( 'ArrowLeft', 'two'.length ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should split into two ordered lists with paragraph', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1. one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should split indented list item', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await clickBlockToolbarButton( 'Indent' ); - await page.keyboard.type( 'two' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'three' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should be immeadiately saved on indentation', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should change the base list type', async () => { - await insertBlock( 'List' ); - const button = await page.waitForSelector( - 'button[aria-label="Ordered"]' - ); - await button.click(); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should change the indented list type', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( '1' ); - - await clickBlockToolbarButton( 'Ordered' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should indent and outdent level 1', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( '1' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primaryShift', 'm' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should indent and outdent level 2', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( 'i' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primaryShift', 'm' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primaryShift', 'm' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should outdent with children', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( 'b' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'primary', 'm' ); - await page.keyboard.type( 'c' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'ArrowUp' ); - await pressKeyWithModifier( 'primaryShift', 'm' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should insert a line break on shift+enter', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should insert a line break on shift+enter in a non trailing list item', async () => { - await insertBlock( 'List' ); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'b' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'c' ); - await page.keyboard.press( 'ArrowUp' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should create and remove indented list with keyboard only', async () => { - await clickBlockAppender(); - - await page.keyboard.type( '* 1' ); // Should be at level 0. - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( ' a' ); // Should be at level 1. - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( ' i' ); // Should be at level 2. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); // Should be at level 1. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); // Should be at level 0. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); // Should be at level 1. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); // Should be at level 0. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); // Should be at level 0. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); // Should remove list. - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // That's 9 key presses to create the list, and 9 key presses to remove - // the list. ;) - } ); - - it( 'should place the caret in the right place with nested list', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* 1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( ' a' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'Enter' ); - // The caret should land in the second item. - await page.keyboard.type( '2' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should not indent list on space with modifier', async () => { - await clickBlockAppender(); - - await page.keyboard.type( '* 1' ); - await page.keyboard.press( 'Enter' ); - await pressKeyWithModifier( 'shift', 'Space' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should only convert to list when shortcut ends with space', async () => { - await clickBlockAppender(); - - // Tests the shortcut with a non breaking space. - await page.keyboard.type( '* ' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should preserve indentation after merging backward and forward', async () => { - await clickBlockAppender(); - - // Tests the shortcut with a non breaking space. - await page.keyboard.type( '* 1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Space' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - - // Create a new paragraph. - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); - - // Merge the pragraph back. No list items should be joined. - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // Again create a new paragraph. - await page.keyboard.press( 'Enter' ); - - // Move to the end of the list. - await page.keyboard.press( 'ArrowLeft' ); - - // Merge forward. No list items should be joined. - await page.keyboard.press( 'Delete' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'first empty list item is graciously removed', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* 1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should not change the contents when you change the list type to Ordered', async () => { - await clickBlockAppender(); - await page.keyboard.type( '* 1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - await clickBlockToolbarButton( 'Ordered' ); - - const content = await page.$eval( - '.wp-block-list', - ( el ) => el.innerHTML - ); - expect( content ).toMatchSnapshot(); - } ); - - it( 'should not change the contents when you change the list type to Unordered', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1. a' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'b' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'c' ); - await clickBlockToolbarButton( 'Unordered' ); - - const content = await page.$eval( - '.wp-block-list', - ( el ) => el.innerHTML - ); - expect( content ).toMatchSnapshot(); - } ); -} ); diff --git a/test/e2e/specs/editor/blocks/list.spec.js b/test/e2e/specs/editor/blocks/list.spec.js new file mode 100644 index 0000000000000..eb5fec4d1d129 --- /dev/null +++ b/test/e2e/specs/editor/blocks/list.spec.js @@ -0,0 +1,872 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'List', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'can be created by using an asterisk at the start of a paragraph block', async ( { + editor, + page, + } ) => { + // Create a block with some text that will trigger a list creation. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* A list item' ); + + // Create a second list item. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Another list item' ); + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be created by typing an asterisk in front of text of a paragraph block', async ( { + editor, + page, + pageUtils, + } ) => { + // Create a list with the slash block shortcut. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'test' ); + await pageUtils.pressKeyTimes( 'ArrowLeft', 4 ); + await page.keyboard.type( '* ' ); + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be created by using a number at the start of a paragraph block', async ( { + editor, + page, + } ) => { + // Create a block with some text that will trigger a list creation. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1) A list item' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +
    1. A list item
    +` + ); + } ); + + test( 'can undo asterisk transform', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1. ' ); + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    1.

    +` + ); + } ); + + test( 'should undo asterisk transform with backspace', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* ' ); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *

    +` + ); + } ); + + test( 'should undo asterisk transform with backspace after selection changes', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* ' ); + await expect( page.locator( '[data-type="core/list"]' ) ).toBeVisible(); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *

    +` + ); + } ); + + test( 'should undo asterisk transform with backspace setting isTyping state', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* ' ); + await editor.showBlockToolbar(); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *

    +` + ); + } ); + + test( 'should undo asterisk transform with backspace after selection changes without requestIdleCallback', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.evaluate( () => delete window.requestIdleCallback ); + await page.keyboard.type( '* ' ); + await expect( page.locator( '[data-type="core/list"]' ) ).toBeVisible(); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *

    +` + ); + } ); + + test( 'should undo asterisk transform with escape', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* ' ); + await page.keyboard.press( 'Escape' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *

    +` + ); + } ); + + test( 'should not undo asterisk transform with backspace after typing', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* a' ); + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + } ); + + test( 'should not undo asterisk transform with backspace after selection change', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '* ' ); + await expect( page.locator( '[data-type="core/list"]' ) ).toBeVisible(); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + } ); + + test( 'can be created by typing "/list"', async ( { editor, page } ) => { + // Create a list with the slash block shortcut. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '/list' ); + await expect( + page.locator( 'role=option[name="List"i][selected]' ) + ).toBeVisible(); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'I’m a list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be created by converting a paragraph', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'test' ); + await editor.transformBlockTo( 'core/list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be created by converting multiple paragraphs', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.down( 'Shift' ); + await page.click( '[data-type="core/paragraph"] >> nth=0' ); + await page.keyboard.up( 'Shift' ); + await editor.transformBlockTo( 'core/list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be created by converting a paragraph with line breaks', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'one' ); + await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await page.keyboard.type( 'two' ); + await editor.transformBlockTo( 'core/list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should not transform lines in block when transforming multiple blocks', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'one' ); + await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await page.keyboard.type( '...' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.down( 'Shift' ); + await page.click( '[data-type="core/paragraph"] >> nth=0' ); + await page.keyboard.up( 'Shift' ); + await editor.transformBlockTo( 'core/list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be converted to paragraphs', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await editor.transformBlockTo( 'core/paragraph' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    one

    + + + +

    two

    +` + ); + } ); + + test( 'can be converted when nested to paragraphs', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await editor.clickBlockToolbarButton( 'Indent' ); + await page.keyboard.type( 'two' ); + await editor.transformBlockTo( 'core/paragraph' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    one

    + + + +

    two

    +` + ); + } ); + + test( 'can be created by converting a quote', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await editor.transformBlockTo( 'core/list' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'can be converted to a quote', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await editor.transformBlockTo( 'core/quote' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    one

    two

    +` + ); + } ); + + test( 'should create paragraph on split at end and merge back with content', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + + + + +

    +` + ); + + await page.keyboard.type( 'two' ); + await pageUtils.pressKeyTimes( 'ArrowLeft', 'two'.length ); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should split into two with paragraph and merge lists', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + + + + +

    + + + + +` + ); + + // Should remove paragraph without creating empty list item. + await page.keyboard.press( 'Backspace' ); + + // Should merge lists into one. + await page.keyboard.press( 'ArrowDown' ); + await pageUtils.pressKeyTimes( 'ArrowLeft', 'two'.length ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + + + + + +` + ); + } ); + + test( 'should split into two ordered lists with paragraph', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1. one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +
    1. one
    + + + +

    + + + +
    1. two
    +` + ); + } ); + + test( 'should split indented list item', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await editor.clickBlockToolbarButton( 'Indent' ); + await page.keyboard.type( 'two' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'three' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should be immeadiately saved on indentation', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should change the base list type', async ( { editor } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await editor.clickBlockToolbarButton( 'Ordered' ); + await expect.poll( editor.getEditedPostContent ).toBe( + ` +
    +` + ); + } ); + + test( 'should change the indented list type', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( '1' ); + + await editor.clickBlockToolbarButton( 'Ordered' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should indent and outdent level 1', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( '1' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'm' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should indent and outdent level 2', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( 'i' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'm' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'm' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should outdent with children', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( 'b' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( 'c' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'ArrowUp' ); + await pageUtils.pressKeyWithModifier( 'primaryShift', 'm' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should insert a line break on shift+enter', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should insert a line break on shift+enter in a non trailing list item', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/list' } ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'b' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'c' ); + await page.keyboard.press( 'ArrowUp' ); + await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should create and remove indented list with keyboard only', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + await page.keyboard.type( '* 1' ); // Should be at level 0. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' a' ); // Should be at level 1. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' i' ); // Should be at level 2. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should be at level 1. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); // Should be at level 1. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should remove list. + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + + // That's 9 key presses to create the list, and 9 key presses to remove + // the list. ;) + } ); + + test( 'should place the caret in the right place with nested list', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' a' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'Enter' ); + // The caret should land in the second item. + await page.keyboard.type( '2' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should not indent list on space with modifier', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await pageUtils.pressKeyWithModifier( 'shift', 'Space' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should only convert to list when shortcut ends with space', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + // Tests the shortcut with a non breaking space. + await page.keyboard.type( '*\u00a0' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

    *\u00a0

    +` + ); + } ); + + test( 'should preserve indentation after merging backward and forward', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + // Tests the shortcut with a non breaking space. + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Space' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '3' ); + + // Create a new paragraph. + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + + // Merge the pragraph back. No list items should be joined. + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + + // Again create a new paragraph. + await page.keyboard.press( 'Enter' ); + + // Move to the end of the list. + await page.keyboard.press( 'ArrowLeft' ); + + // Merge forward. No list items should be joined. + await page.keyboard.press( 'Delete' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'first empty list item is graciously removed', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); + + test( 'should not change the contents when you change the list type to Ordered', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '3' ); + await editor.clickBlockToolbarButton( 'Ordered' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` +
    1. 1
    2. 2
    3. 3
    +` + ); + } ); + + test( 'should not change the contents when you change the list type to Unordered', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1. a' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'b' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'c' ); + await editor.clickBlockToolbarButton( 'Unordered' ); + + await expect.poll( editor.getEditedPostContent ).toBe( + ` + +` + ); + } ); +} );