Skip to content

Commit

Permalink
Fix navigation editor error (#28190)
Browse files Browse the repository at this point in the history
* Fix error with useNavigationBlockEditor, onChange expects options with selectionStart and selectionEnd props

* Add navigation editor test for creating a menu

* Update test to make endpoint mocking realistic

* Update comment

* Wait for block to be added
  • Loading branch information
talldan authored Jan 18, 2021
1 parent b864c5d commit 9f7d9e7
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Navigation editor allows creation of a menu 1`] = `
"<!-- wp:navigation -->
<!-- wp:navigation-link {\\"label\\":\\"Home\\",\\"type\\":\\"page\\",\\"id\\":1,\\"url\\":\\"https://this/is/a/test/page/home\\"} /-->
<!-- wp:navigation-link {\\"label\\":\\"About\\",\\"type\\":\\"page\\",\\"id\\":2,\\"url\\":\\"https://this/is/a/test/page/about\\"} /-->
<!-- wp:navigation-link {\\"label\\":\\"Contact\\",\\"type\\":\\"page\\",\\"id\\":3,\\"url\\":\\"https://this/is/a/test/page/contact\\"} /-->
<!-- /wp:navigation -->"
`;

exports[`Navigation editor displays the first menu from the REST response when at least one menu exists 1`] = `
"<!-- wp:navigation -->
<!-- wp:navigation-link {\\"label\\":\\"Home\\",\\"description\\":\\"\\",\\"rel\\":\\"\\",\\"url\\":\\"http://localhost:8889/\\",\\"title\\":\\"\\",\\"className\\":\\"\\"} /-->
Expand Down
152 changes: 125 additions & 27 deletions packages/e2e-tests/specs/experiments/navigation-editor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ const menusFixture = [
},
];

const pagesFixture = [
{
title: 'Home',
slug: 'home',
},
{
title: 'About',
slug: 'about',
},
{
title: 'Contact',
slug: 'contact',
},
];

// Matching against variations of the same URL encoded and non-encoded
// produces the most reliable mocking.
const REST_MENUS_ROUTES = [
Expand All @@ -39,6 +54,10 @@ const REST_MENU_ITEMS_ROUTES = [
'/__experimental/menu-items',
`rest_route=${ encodeURIComponent( '/__experimental/menu-items' ) }`,
];
const REST_PAGES_ROUTES = [
'/wp/v2/pages',
`rest_route=${ encodeURIComponent( '/wp/v2/pages' ) }`,
];

/**
* Determines if a given URL matches any of a given collection of
Expand All @@ -51,38 +70,57 @@ function matchUrlToRoute( reqUrl, routes ) {
return routes.some( ( route ) => reqUrl.includes( route ) );
}

/**
* Creates mocked REST API responses for calls to menus and menu-items
* endpoints.
* Note: this needs to be within a single call to
* `setUpResponseMocking` as you can only setup response mocking once per test run.
*
* @param {Array} menus menus to provide as mocked responses to menus entity API requests.
* @param {Array} menuItems menu items to provide as mocked responses to menu-items entity API requests.
*/
async function mockAllMenusResponses(
menus = menusFixture,
menuItems = menuItemsFixture
) {
const mappedMenus = menus.length
function getEndpointMocks( matchingRoutes, responsesByMethod ) {
return [ 'GET', 'POST', 'DELETE', 'PUT' ].reduce( ( mocks, restMethod ) => {
if ( responsesByMethod[ restMethod ] ) {
return [
...mocks,
{
match: ( request ) =>
matchUrlToRoute( request.url(), matchingRoutes ) &&
request.method() === restMethod,
onRequestMatch: createJSONResponse(
responsesByMethod[ restMethod ]
),
},
];
}

return mocks;
}, [] );
}

function assignMockMenuIds( menus ) {
return menus.length
? menus.map( ( menu, index ) => ( {
...menu,
id: index + 1,
} ) )
: [];
}

await setUpResponseMocking( [
{
match: ( request ) =>
matchUrlToRoute( request.url(), REST_MENUS_ROUTES ),
onRequestMatch: createJSONResponse( mappedMenus ),
function createMockPages( pages ) {
return pages.map( ( { title, slug }, index ) => ( {
id: index + 1,
type: 'page',
link: `https://this/is/a/test/page/${ slug }`,
title: {
rendered: title,
raw: title,
},
{
match: ( request ) =>
matchUrlToRoute( request.url(), REST_MENU_ITEMS_ROUTES ),
onRequestMatch: createJSONResponse( menuItems ),
},
] );
} ) );
}

function getMenuMocks( responsesByMethod ) {
return getEndpointMocks( REST_MENUS_ROUTES, responsesByMethod );
}

function getMenuItemMocks( responsesByMethod ) {
return getEndpointMocks( REST_MENU_ITEMS_ROUTES, responsesByMethod );
}

function getPagesMocks( responsesByMethod ) {
return getEndpointMocks( REST_PAGES_ROUTES, responsesByMethod );
}

async function visitNavigationEditor() {
Expand All @@ -100,13 +138,73 @@ async function getSerializedBlocks() {

describe( 'Navigation editor', () => {
useExperimentalFeatures( [ '#gutenberg-navigation' ] );

afterEach( async () => {
await setUpResponseMocking( [] );
} );

it( 'allows creation of a menu', async () => {
const pagesResponse = createMockPages( pagesFixture );
const menuResponse = {
id: 4,
description: '',
name: 'Main Menu',
slug: 'main-menu',
meta: [],
auto_add: false,
};

// Initially return nothing from the menu and menuItem endpoints
await setUpResponseMocking( [
...getMenuMocks( { GET: [] } ),
...getMenuItemMocks( { GET: [] } ),
...getPagesMocks( { GET: pagesResponse } ),
] );
await visitNavigationEditor();

// Wait for the header to show that no menus are available.
await page.waitForXPath( '//h2[contains(., "No menus available")]' );

// Prepare the menu endpoint for creating a menu.
await setUpResponseMocking( [
...getMenuMocks( {
GET: [ menuResponse ],
POST: menuResponse,
} ),
...getMenuItemMocks( { GET: [] } ),
...getPagesMocks( { GET: pagesResponse } ),
] );

// Add a new menu.
const [ addNewButton ] = await page.$x(
'//button[contains(., "Add new")]'
);
await addNewButton.click();
await page.keyboard.type( 'Main Menu' );
const [ createMenuButton ] = await page.$x(
'//button[contains(., "Create menu")]'
);
await createMenuButton.click();

// Close the dropdown.
await page.keyboard.press( 'Escape' );

// Select the navigation block and create a block from existing pages.
await page.waitForSelector( 'div[aria-label="Block: Navigation"]' );
await page.click( 'div[aria-label="Block: Navigation"]' );

const [ addAllPagesButton ] = await page.$x(
'//button[contains(., "Add all pages")]'
);
await addAllPagesButton.click();

expect( await getSerializedBlocks() ).toMatchSnapshot();
} );

it( 'displays the first menu from the REST response when at least one menu exists', async () => {
await mockAllMenusResponses();
await setUpResponseMocking( [
...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
...getMenuItemMocks( { GET: menuItemsFixture } ),
] );
await visitNavigationEditor();

// Wait for the header to show the menu name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ export default function useNavigationBlockEditor( post ) {
);

const onChange = useCallback(
async ( updatedBlocks ) => {
await _onChange( updatedBlocks );
async ( ...args ) => {
await _onChange( ...args );
createMissingMenuItems( post );
},
[ blocks, _onChange ]
[ _onChange, post ]
);

return [ blocks, onInput, onChange ];
Expand Down

0 comments on commit 9f7d9e7

Please sign in to comment.