Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2E Tests: Add Block Hooks Test Coverage #69044

Open
wants to merge 30 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21a9d8c
E2E Tests: Add Block Hooks Test plugin
ockham Feb 3, 2025
983508a
Add basic e2e test
ockham Feb 5, 2025
20e1ff8
WPCS
ockham Feb 5, 2025
68fee6c
More WPCS
ockham Feb 5, 2025
eed4fe0
Change test file suffix to .spec.js
ockham Feb 5, 2025
1b19968
Rename plugin so it can be found
ockham Feb 5, 2025
5d93287
Fix test description
ockham Feb 5, 2025
a1f8bcb
Add custom class name to facilitate locating block
ockham Feb 11, 2025
61a8c8e
Introduce helper
ockham Feb 11, 2025
0fbdcc0
Truncate 'core/' from block class name
ockham Feb 11, 2025
60d9a2d
Add frontend test
ockham Feb 11, 2025
0a941c6
Fix selector
ockham Feb 11, 2025
7983d01
Rewrite editor test
ockham Feb 11, 2025
7a80350
Move hooked block up
ockham Feb 12, 2025
47f6bec
Fix interpolated string var
ockham Feb 12, 2025
14f5059
Add TODO
ockham Feb 12, 2025
89645cc
Add more test coverage
ockham Feb 12, 2025
2b9f127
Add assertions about element order
ockham Feb 12, 2025
dda5ea0
Move consts up one level
ockham Feb 12, 2025
9b2ee0e
Rename test-paragraph class to dummy-paragraph
ockham Feb 13, 2025
2b97811
Add test coverage for 'after' insertion
ockham Feb 13, 2025
dd7a644
Remove now-obsolete assertions
ockham Feb 13, 2025
70bcc01
Update test description
ockham Feb 13, 2025
8eb1de1
Add test coverage for Synced Patterns
ockham Feb 13, 2025
a757d56
Rename var
ockham Feb 13, 2025
acead15
Merge test cases
ockham Feb 13, 2025
92e7ff4
Add basic Navigation block coverage
ockham Feb 13, 2025
fe75a0d
Slightly less basic
ockham Feb 13, 2025
4615325
More Nav block coverage
ockham Feb 13, 2025
495e14a
WPCS
ockham Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions packages/e2e-tests/plugins/block-hooks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Plugin Name: Gutenberg Test Block Hooks
* Plugin URI: https://github.com/WordPress/gutenberg
* Author: Gutenberg Team
*
* @package gutenberg-test-block-hooks
*/

defined( 'ABSPATH' ) || exit;

function gutenberg_test_insert_hooked_blocks( $hooked_blocks, $position, $anchor_block, $context ) {
if ( ! $context instanceof WP_Post ) {
return $hooked_blocks;
}

if (
( 'core/heading' === $anchor_block && 'after' === $position ) ||
( 'core/post-content' === $anchor_block && 'last_child' === $position ) ||
( 'core/block' === $anchor_block && 'last_child' === $position )
) {
$hooked_blocks[] = 'core/paragraph';
}

if ( 'core/navigation' === $anchor_block && 'first_child' === $position ) {
$hooked_blocks[] = 'core/home-link';
}

if ( 'core/navigation-link' === $anchor_block && 'after' === $position ) {
$hooked_blocks[] = 'core/page-list';
}

return $hooked_blocks;
}
add_filter( 'hooked_block_types', 'gutenberg_test_insert_hooked_blocks', 10, 4 );

function gutenberg_test_set_hooked_block_inner_html( $hooked_block, $hooked_block_type, $relative_position, $anchor_block ) {
if (
( 'core/heading' === $anchor_block['blockName'] && 'after' === $relative_position ) ||
( 'core/post-content' === $anchor_block['blockName'] && 'last_child' === $relative_position ) ||
( 'core/block' === $anchor_block['blockName'] && 'last_child' === $relative_position )
) {
$hooked_block['attrs'] = array(
'className' => "hooked-block-{$relative_position}-" . str_replace( 'core/', '', $anchor_block['blockName'] ),
);
$hooked_block['innerContent'] = array(
sprintf(
'<p class="%1$s">This block was inserted by the Block Hooks API in the <code>%2$s</code> position next to the <code>%3$s</code> anchor block.</p>',
$hooked_block['attrs']['className'],
$relative_position,
$anchor_block['blockName']
),
);
}

return $hooked_block;
}
add_filter( 'hooked_block_core/paragraph', 'gutenberg_test_set_hooked_block_inner_html', 10, 4 );

function gutenberg_register_wp_ignored_hooked_blocks_meta() {
register_post_meta(
'post',
'_wp_ignored_hooked_blocks',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function () {
return current_user_can( 'edit_posts' );
},
)
);
}
add_action( 'rest_api_init', 'gutenberg_register_wp_ignored_hooked_blocks_meta' );
292 changes: 292 additions & 0 deletions test/e2e/specs/editor/plugins/block-hooks.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
/**
* WordPress dependencies
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );

const dummyBlockContent = `<!-- wp:heading -->
<h2 class="wp-block-heading">This is a dummy heading</h2>
<!-- /wp:heading -->
<!-- wp:paragraph {"className":"dummy-paragraph"} -->
<p class="dummy-paragraph">This is a dummy paragraph.</p>
<!-- /wp:paragraph -->`;

const getHookedBlockClassName = ( relativePosition, anchorBlock ) =>
`hooked-block-${ relativePosition }-${ anchorBlock.replace(
'core/',
''
) }`;

const getHookedBlockContent = ( relativePosition, anchorBlock ) =>
`This block was inserted by the Block Hooks API in the ${ relativePosition } position next to the ${ anchorBlock } anchor block.`;

test.describe( 'Block Hooks API', () => {
[
{
name: 'Post Content',
postType: 'post',
blockType: 'core/post-content',
createMethod: 'createPost',
},
{
name: 'Synced Pattern',
postType: 'wp_block',
blockType: 'core/block',
createMethod: 'createBlock',
},
].forEach( ( { name, postType, blockType, createMethod } ) => {
test.describe( `Hooked blocks in ${ name }`, () => {
let postObject, containerPost;
test.beforeAll( async ( { requestUtils } ) => {
postObject = await requestUtils[ createMethod ]( {
title: name,
status: 'publish',
content: dummyBlockContent,
} );

await requestUtils.activatePlugin(
'gutenberg-test-block-hooks'
);

if ( postType !== 'post' ) {
// We need a container post to hold our block instance.
containerPost = await requestUtils.createPost( {
title: `Block Hooks in ${ name }`,
status: 'publish',
content: `<!-- wp:${ blockType } {"ref":${ postObject.id }} /-->`,
meta: {
// Prevent Block Hooks from injecting blocks into the container
// post content so they won't distract from the ones injected
// into the block instance.
_wp_ignored_hooked_blocks: '["core/paragraph"]',
},
} );
} else {
containerPost = postObject;
}
} );

test.afterAll( async ( { requestUtils } ) => {
await requestUtils.deactivatePlugin(
'gutenberg-test-block-hooks'
);

await requestUtils.deleteAllPosts();
await requestUtils.deleteAllBlocks();
} );

test( `should insert hooked blocks into ${ name } on frontend`, async ( {
page,
} ) => {
await page.goto( `/?p=${ containerPost.id }` );
await expect(
page.locator( '.entry-content > *' )
).toHaveClass( [
'wp-block-heading',
getHookedBlockClassName( 'after', 'core/heading' ),
'dummy-paragraph',
getHookedBlockClassName( 'last_child', blockType ),
] );
} );

test( `should insert hooked blocks into ${ name } in editor and respect changes made there`, async ( {
admin,
editor,
page,
} ) => {
const expectedHookedBlockAfterHeading = {
name: 'core/paragraph',
attributes: {
className: getHookedBlockClassName(
'after',
'core/heading'
),
},
};

const expectedHookedBlockLastChild = {
name: 'core/paragraph',
attributes: {
className: getHookedBlockClassName(
'last_child',
blockType
),
},
};

await admin.editPost( postObject.id );
await expect
.poll( editor.getBlocks )
.toMatchObject( [
{ name: 'core/heading' },
expectedHookedBlockAfterHeading,
{ name: 'core/paragraph' },
expectedHookedBlockLastChild,
] );

const hookedBlock = editor.canvas.getByText(
getHookedBlockContent( 'last_child', blockType )
);
await editor.selectBlocks( hookedBlock );
await editor.clickBlockToolbarButton( 'Move up' );

// Save updated post.
const saveButton = page
.getByRole( 'region', { name: 'Editor top bar' } )
.getByRole( 'button', { name: 'Save', exact: true } );
await saveButton.click();
await page
.getByRole( 'button', { name: 'Dismiss this notice' } )
.filter( { hasText: 'updated' } )
.waitFor();

// Reload and verify that the new position of the hooked block has been persisted.
await page.reload();
await expect
.poll( editor.getBlocks )
.toMatchObject( [
{ name: 'core/heading' },
expectedHookedBlockAfterHeading,
expectedHookedBlockLastChild,
{ name: 'core/paragraph' },
] );

// Verify that the frontend reflects the changes made in the editor.
await page.goto( `/?p=${ containerPost.id }` );
await expect(
page.locator( '.entry-content > *' )
).toHaveClass( [
'wp-block-heading',
getHookedBlockClassName( 'after', 'core/heading' ),
getHookedBlockClassName( 'last_child', blockType ),
'dummy-paragraph',
] );
} );
} );
} );

test.describe( `Hooked blocks in Navigation Menu`, () => {
let postObject, containerPost;
test.beforeAll( async ( { requestUtils } ) => {
postObject = await requestUtils.createNavigationMenu( {
title: 'Navigation Menu',
status: 'publish',
content:
'<!-- wp:navigation-link {"label":"wordpress.org","url":"https://wordpress.org","kind":"custom"} /-->', // '<!-- wp:page-list /-->',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a code comment with a different block included?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two failing tests might be related to that:

Screenshot 2025-02-14 at 11 36 47

} );

await requestUtils.activatePlugin( 'gutenberg-test-block-hooks' );

// We need a container post to hold our block instance.
containerPost = await requestUtils.createPost( {
title: `Block Hooks in Navigation Menu`,
status: 'publish',
content: `<!-- wp:navigation {"ref":${ postObject.id }} /-->`,
meta: {
// Prevent Block Hooks from injecting blocks into the container
// post content so they won't distract from the ones injected
// into the block instance.
_wp_ignored_hooked_blocks: '["core/paragraph"]',
},
} );
} );

test.afterAll( async ( { requestUtils } ) => {
await requestUtils.deactivatePlugin( 'gutenberg-test-block-hooks' );

await requestUtils.deleteAllPosts();
await requestUtils.deleteAllBlocks();
} );

test( `should insert hooked blocks into Navigation Menu on frontend`, async ( {
page,
} ) => {
await page.goto( `/?p=${ containerPost.id }` );
await expect(
page.locator( '.wp-block-navigation__container > *' )
).toHaveClass( [

Check failure on line 206 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend

1) [chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ "wp-block-navigation-item wp-block-home-link", " wp-block-navigation-item wp-block-navigation-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 204 | await expect( 205 | page.locator( '.wp-block-navigation__container > *' ) > 206 | ).toHaveClass( [ | ^ 207 | 'wp-block-navigation-item wp-block-home-link', 208 | ' wp-block-navigation-item wp-block-navigation-link', 209 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:206:6

Check failure on line 206 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend

1) [chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ "wp-block-navigation-item wp-block-home-link", " wp-block-navigation-item wp-block-navigation-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 204 | await expect( 205 | page.locator( '.wp-block-navigation__container > *' ) > 206 | ).toHaveClass( [ | ^ 207 | 'wp-block-navigation-item wp-block-home-link', 208 | ' wp-block-navigation-item wp-block-navigation-link', 209 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:206:6

Check failure on line 206 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend

1) [chromium] › editor/plugins/block-hooks.spec.js:200:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu on frontend Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ "wp-block-navigation-item wp-block-home-link", " wp-block-navigation-item wp-block-navigation-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 204 | await expect( 205 | page.locator( '.wp-block-navigation__container > *' ) > 206 | ).toHaveClass( [ | ^ 207 | 'wp-block-navigation-item wp-block-home-link', 208 | ' wp-block-navigation-item wp-block-navigation-link', 209 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:206:6
'wp-block-navigation-item wp-block-home-link',
' wp-block-navigation-item wp-block-navigation-link',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this preceding space intentional?

'wp-block-page-list',
] );
} );

test( `should insert hooked blocks into Navigation Menu in editor and respect changes made there`, async ( {
admin,
editor,
page,
} ) => {
await admin.visitSiteEditor( {
postId: postObject.id,
postType: 'wp_navigation',
canvas: 'edit',
} );

// Since the Navigation block is a controlled block, we need
// to specify its client ID when calling `getBlocks`.
let navigationBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Navigation',
} );
let navigationClientId =
await navigationBlock.getAttribute( 'data-block' );

await expect
.poll( () =>
editor.getBlocks( {
clientId: navigationClientId,
} )
)
.toMatchObject( [
{ name: 'core/home-link' },
{ name: 'core/navigation-link' },
{ name: 'core/page-list' },
] );

const hookedBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Home Link',
} );
await editor.selectBlocks( hookedBlock );
await editor.clickBlockToolbarButton( 'Move right' );

// Save updated post.
const saveButton = page
.getByRole( 'region', { name: 'Editor top bar' } )
.getByRole( 'button', { name: 'Save', exact: true } );
await saveButton.click();
await page
.getByRole( 'button', { name: 'Dismiss this notice' } )
.filter( { hasText: 'updated' } )
.waitFor();

// Reload and verify that the new position of the hooked block has been persisted.
await page.reload();

navigationBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Navigation',
} );
navigationClientId =
await navigationBlock.getAttribute( 'data-block' );

await expect
.poll( () =>
editor.getBlocks( {
clientId: navigationClientId,
} )
)
.toMatchObject( [
{ name: 'core/navigation-link' },
{ name: 'core/home-link' },
{ name: 'core/page-list' },
] );

// Verify that the frontend reflects the changes made in the editor.
await page.goto( `/?p=${ containerPost.id }` );
await expect(
page.locator( '.wp-block-navigation__container > *' )
).toHaveClass( [

Check failure on line 285 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there

2) [chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ " wp-block-navigation-item wp-block-navigation-link", "wp-block-navigation-item wp-block-home-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 283 | await expect( 284 | page.locator( '.wp-block-navigation__container > *' ) > 285 | ).toHaveClass( [ | ^ 286 | ' wp-block-navigation-item wp-block-navigation-link', 287 | 'wp-block-navigation-item wp-block-home-link', 288 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:285:6

Check failure on line 285 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there

2) [chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ " wp-block-navigation-item wp-block-navigation-link", "wp-block-navigation-item wp-block-home-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 283 | await expect( 284 | page.locator( '.wp-block-navigation__container > *' ) > 285 | ).toHaveClass( [ | ^ 286 | ' wp-block-navigation-item wp-block-navigation-link', 287 | 'wp-block-navigation-item wp-block-home-link', 288 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:285:6

Check failure on line 285 in test/e2e/specs/editor/plugins/block-hooks.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 2

[chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there

2) [chromium] › editor/plugins/block-hooks.spec.js:213:3 › Block Hooks API › Hooked blocks in Navigation Menu › should insert hooked blocks into Navigation Menu in editor and respect changes made there Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected) Locator: locator('.wp-block-navigation__container > *') - Expected - 1 + Received + 0 Array [ " wp-block-navigation-item wp-block-navigation-link", "wp-block-navigation-item wp-block-home-link", - "wp-block-page-list", ] Call log: - expect.toHaveClass with timeout 5000ms - waiting for locator('.wp-block-navigation__container > *') 9 × locator resolved to 2 elements 283 | await expect( 284 | page.locator( '.wp-block-navigation__container > *' ) > 285 | ).toHaveClass( [ | ^ 286 | ' wp-block-navigation-item wp-block-navigation-link', 287 | 'wp-block-navigation-item wp-block-home-link', 288 | 'wp-block-page-list', at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/block-hooks.spec.js:285:6
' wp-block-navigation-item wp-block-navigation-link',
'wp-block-navigation-item wp-block-home-link',
'wp-block-page-list',
] );
} );
} );
} );
Loading