Skip to content

Commit

Permalink
Add e2e tests for filtering and searching patterns (#54906)
Browse files Browse the repository at this point in the history
* Add e2e tests for filtering and searching patterns

* Update packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts

Co-authored-by: Aaron Robertshaw <[email protected]>

---------

Co-authored-by: Aaron Robertshaw <[email protected]>
  • Loading branch information
kevin940726 and aaronrobertshaw authored Oct 2, 2023
1 parent 3cc9af1 commit 7d67425
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 63 deletions.
19 changes: 11 additions & 8 deletions packages/e2e-test-utils-playwright/src/admin/visit-site-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface SiteEditorQueryParams {
postType: string;
}

const CANVAS_SELECTOR = 'iframe[title="Editor canvas"i]';
const CANVAS_SELECTOR = 'iframe[title="Editor canvas"i] >> visible=true';

/**
* Visits the Site Editor main page
Expand Down Expand Up @@ -55,13 +55,16 @@ export async function visitSiteEditor(
} );
}

// The site editor initially loads with an empty body,
// we need to wait for the editor canvas to be rendered.
await this.page
.frameLocator( CANVAS_SELECTOR )
.locator( 'body > *' )
.first()
.waitFor();
// Check if the current page has an editor canvas first.
if ( ( await this.page.locator( CANVAS_SELECTOR ).count() ) > 0 ) {
// The site editor initially loads with an empty body,
// we need to wait for the editor canvas to be rendered.
await this.page
.frameLocator( CANVAS_SELECTOR )
.locator( 'body > *' )
.first()
.waitFor();
}

// TODO: Ideally the content underneath the canvas loader should be marked inert until it's ready.
await this.page
Expand Down
28 changes: 0 additions & 28 deletions packages/e2e-test-utils-playwright/src/request-utils/blocks.js

This file was deleted.

64 changes: 64 additions & 0 deletions packages/e2e-test-utils-playwright/src/request-utils/blocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Internal dependencies
*/
import type { RequestUtils } from './index';

type CreateBlockPayload = {
date?: string;
date_gmt?: string;
slug?: string;
title: string;
status: 'publish' | 'future' | 'draft' | 'pending' | 'private';
content?: string;
meta?: unknown;
wp_pattern_category?: number[];
};

/**
* Delete all blocks using REST API.
*
* @see https://developer.wordpress.org/rest-api/reference/blocks/#list-editor-blocks
* @param this
*/
export async function deleteAllBlocks( this: RequestUtils ) {
// List all blocks.
// https://developer.wordpress.org/rest-api/reference/blocks/#list-editor-blocks
const blocks = await this.rest( {
path: '/wp/v2/blocks',
params: {
per_page: 100,
// All possible statuses.
status: 'publish,future,draft,pending,private,trash',
},
} );

// Delete blocks.
// https://developer.wordpress.org/rest-api/reference/blocks/#delete-a-editor-block
// "/wp/v2/posts" not yet supports batch requests.
await this.batchRest(
blocks.map( ( block: { id: number } ) => ( {
method: 'DELETE',
path: `/wp/v2/blocks/${ block.id }?force=true`,
} ) )
);
}

/**
* Creates a new block using the REST API.
*
* @see https://developer.wordpress.org/rest-api/reference/blocks/#create-a-editor-block.
* @param this
* @param payload Block payload.
*/
export async function createBlock(
this: RequestUtils,
payload: CreateBlockPayload
) {
const block = await this.rest( {
path: '/wp/v2/blocks',
method: 'POST',
data: { ...payload },
} );

return block;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
getCurrentThemeGlobalStylesPostId,
getThemeGlobalStylesRevisions,
} from './themes';
import { deleteAllBlocks } from './blocks';
import { createBlock, deleteAllBlocks } from './blocks';
import { createComment, deleteAllComments } from './comments';
import { createPost, deleteAllPosts } from './posts';
import {
Expand Down Expand Up @@ -141,6 +141,8 @@ class RequestUtils {
deactivatePlugin: typeof deactivatePlugin = deactivatePlugin.bind( this );
/** @borrows activateTheme as this.activateTheme */
activateTheme: typeof activateTheme = activateTheme.bind( this );
/** @borrows createBlock as this.createBlock */
createBlock: typeof createBlock = createBlock.bind( this );
/** @borrows deleteAllBlocks as this.deleteAllBlocks */
deleteAllBlocks = deleteAllBlocks.bind( this );
/** @borrows createPost as this.createPost */
Expand Down
6 changes: 1 addition & 5 deletions test/e2e/specs/editor/various/patterns.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,8 @@ test.describe( 'Unsynced pattern', () => {
.click();
await page.getByLabel( 'My unsynced pattern' ).click();

// Just compare the block name and content as the clientIDs will be different.
before.forEach( ( block ) => {
delete block.clientId;
} );
await expect
.poll( editor.getBlocks )
.toMatchObject( [ ...before, ...before ] );
.toEqual( [ ...before, ...before ] );
} );
} );
182 changes: 161 additions & 21 deletions test/e2e/specs/site-editor/patterns.spec.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
/**
* WordPress dependencies
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
const {
test: base,
expect,
} = require( '@wordpress/e2e-test-utils-playwright' );

/** @type {ReturnType<typeof base.extend<{patterns: Patterns}>>} */
const test = base.extend( {
patterns: async ( { page }, use ) => {
await use( new Patterns( { page } ) );
},
} );

test.describe( 'Patterns', () => {
test.beforeAll( async ( { requestUtils } ) => {
await requestUtils.activateTheme( 'emptytheme' );
await requestUtils.deleteAllBlocks();
} );

test.afterAll( async ( { requestUtils } ) => {
await requestUtils.activateTheme( 'twentytwentyone' );
} );
Expand All @@ -16,22 +27,27 @@ test.describe( 'Patterns', () => {
await requestUtils.deleteAllBlocks();
} );

test( 'create a new pattern', async ( { page, editor, admin } ) => {
test( 'create a new pattern', async ( {
page,
editor,
admin,
patterns,
} ) => {
await admin.visitSiteEditor();

const navigation = page.getByRole( 'region', { name: 'Navigation' } );
const patternsContent = page.getByRole( 'region', {
name: 'Patterns content',
} );

await navigation.getByRole( 'button', { name: 'Patterns' } ).click();
await patterns.navigation
.getByRole( 'button', { name: 'Patterns' } )
.click();

await expect(
navigation.getByRole( 'heading', { name: 'Patterns', level: 1 } )
patterns.navigation.getByRole( 'heading', {
name: 'Patterns',
level: 1,
} )
).toBeVisible();
await expect( patternsContent ).toContainText( 'No patterns found.' );
await expect( patterns.content ).toContainText( 'No patterns found.' );

await navigation
await patterns.navigation
.getByRole( 'button', { name: 'Create pattern' } )
.click();

Expand Down Expand Up @@ -83,33 +99,157 @@ test.describe( 'Patterns', () => {
).toContainText( 'Site updated' );

await page.getByRole( 'button', { name: 'Open navigation' } ).click();
await navigation.getByRole( 'button', { name: 'Back' } ).click();
await patterns.navigation
.getByRole( 'button', { name: 'Back' } )
.click();
// TODO: await expect( page ).toHaveTitle( /^Patterns/ );

await expect(
navigation.getByRole( 'button', { name: 'All patterns' } )
patterns.navigation.getByRole( 'button', {
name: 'All patterns',
} )
).toContainText( '1' );
await expect(
navigation.getByRole( 'button', { name: 'My patterns' } )
patterns.navigation.getByRole( 'button', {
name: 'My patterns',
} )
).toContainText( '1' );
await expect(
navigation.getByRole( 'button', { name: 'Uncategorized' } )
patterns.navigation.getByRole( 'button', {
name: 'Uncategorized',
} )
).toContainText( '1' );

await expect(
patternsContent.getByRole( 'heading', {
patterns.content.getByRole( 'heading', {
name: 'All patterns',
level: 2,
} )
).toBeVisible();
const patternsList = patternsContent.getByRole( 'list', {
name: 'All patterns',
} );
await expect( patternsList.getByRole( 'listitem' ) ).toHaveCount( 1 );
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 1 );
await expect(
patternsList
patterns.list
.getByRole( 'heading', { name: 'My pattern' } )
.getByRole( 'button', { name: 'My pattern', exact: true } )
).toBeVisible();
} );

test( 'search and filter patterns', async ( {
admin,
requestUtils,
patterns,
} ) => {
await Promise.all( [
requestUtils.createBlock( {
title: 'Unsynced header',
meta: { wp_pattern_sync_status: 'unsynced' },
status: 'publish',
content: `<!-- wp:heading -->\n<h2>Unsynced header</h2>\n<!-- /wp:heading -->`,
wp_pattern_category: [],
} ),
requestUtils.createBlock( {
title: 'Unsynced footer',
meta: { wp_pattern_sync_status: 'unsynced' },
status: 'publish',
content: `<!-- wp:paragraph -->\n<p>Unsynced footer</p>\n<!-- /wp:paragraph -->`,
wp_pattern_category: [],
} ),
requestUtils.createBlock( {
title: 'Synced footer',
status: 'publish',
content: `<!-- wp:paragraph -->\n<p>Synced footer</p>\n<!-- /wp:paragraph -->`,
wp_pattern_category: [],
} ),
] );

await admin.visitSiteEditor( { path: '/patterns' } );

await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 3 );

await patterns.content
.getByRole( 'searchbox', { name: 'Search patterns' } )
.fill( 'footer' );
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 2 );
expect(
await patterns.list
.getByRole( 'listitem' )
.getByRole( 'heading' )
.allInnerTexts()
).toEqual(
expect.arrayContaining( [ 'Unsynced footer', 'Synced footer' ] )
);

const searchBox = patterns.content.getByRole( 'searchbox', {
name: 'Search patterns',
} );

await searchBox.fill( 'no match' );
await expect( patterns.content ).toContainText( 'No patterns found.' );

await patterns.content
.getByRole( 'button', { name: 'Reset search' } )
.click();
await expect( searchBox ).toHaveValue( '' );
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 3 );

const syncFilter = patterns.content.getByRole( 'radiogroup', {
name: 'Filter by sync status',
} );
await expect(
syncFilter.getByRole( 'radio', { name: 'All' } )
).toBeChecked();

await syncFilter
.getByRole( 'radio', { name: 'Synced', exact: true } )
.click();
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 1 );
await expect( patterns.list.getByRole( 'listitem' ) ).toContainText(
'Synced footer'
);

await syncFilter.getByRole( 'radio', { name: 'Not synced' } ).click();
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 2 );
expect(
await patterns.list
.getByRole( 'listitem' )
.getByRole( 'heading' )
.allInnerTexts()
).toEqual(
expect.arrayContaining( [ 'Unsynced header', 'Unsynced footer' ] )
);

await searchBox.fill( 'footer' );
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 1 );
await expect( patterns.list.getByRole( 'listitem' ) ).toContainText(
'Unsynced footer'
);

await syncFilter.getByRole( 'radio', { name: 'All' } ).click();
await expect( patterns.list.getByRole( 'listitem' ) ).toHaveCount( 2 );
expect(
await patterns.list
.getByRole( 'listitem' )
.getByRole( 'heading' )
.allInnerTexts()
).toEqual(
expect.arrayContaining( [ 'Unsynced footer', 'Synced footer' ] )
);
} );
} );

class Patterns {
/** @type {import('@playwright/test').Page} */
#page;

constructor( { page } ) {
this.#page = page;

this.content = this.#page.getByRole( 'region', {
name: 'Patterns content',
} );
this.navigation = this.#page.getByRole( 'region', {
name: 'Navigation',
} );
this.list = this.content.getByRole( 'list' );
}
}

0 comments on commit 7d67425

Please sign in to comment.