@wordpress/e2e-test-utils
Version:
End-To-End (E2E) test utils for WordPress.
309 lines (284 loc) • 11.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.closeGlobalBlockInserter = closeGlobalBlockInserter;
exports.insertBlock = insertBlock;
exports.insertBlockDirectoryBlock = insertBlockDirectoryBlock;
exports.insertFromGlobalInserter = insertFromGlobalInserter;
exports.insertPattern = insertPattern;
exports.openGlobalBlockInserter = openGlobalBlockInserter;
exports.searchForBlock = searchForBlock;
exports.searchForBlockDirectoryBlock = searchForBlockDirectoryBlock;
exports.searchForPattern = searchForPattern;
exports.searchForReusableBlock = searchForReusableBlock;
exports.searchGlobalInserter = searchGlobalInserter;
exports.selectGlobalInserterTab = selectGlobalInserterTab;
exports.toggleGlobalBlockInserter = toggleGlobalBlockInserter;
var _puppeteerCore = require("puppeteer-core");
var _pressKeyWithModifier = require("./press-key-with-modifier");
var _canvas = require("./canvas");
/**
* External dependencies
*/
/**
* Internal dependencies
*/
// This selector is written to support the current and old inserter markup
// because the performance tests need to be able to run across versions.
const INSERTER_SEARCH_SELECTOR = '.block-editor-inserter__search input,.block-editor-inserter__search-input,input.block-editor-inserter__search';
/**
* Opens the global inserter.
*/
async function openGlobalBlockInserter() {
if (!(await isGlobalInserterOpen())) {
await toggleGlobalBlockInserter();
// Waiting here is necessary because sometimes the inserter takes more
// time to render than Puppeteer takes to complete the 'click' action.
await page.waitForSelector('.block-editor-inserter__menu');
}
}
/**
* Closes the global inserter.
*/
async function closeGlobalBlockInserter() {
if (await isGlobalInserterOpen()) {
await toggleGlobalBlockInserter();
}
}
/**
* Checks if the global inserter is open.
*
* @return {Promise<boolean>} Whether the inserter is open or not.
*/
async function isGlobalInserterOpen() {
return await page.evaluate(() => {
// "Add block" selector is required to make sure performance comparison
// doesn't fail on older branches where we still had "Add block" as
// label.
return !!document.querySelector('.edit-post-header [aria-label="Add block"].is-pressed,' + '.edit-site-header-edit-mode [aria-label="Add block"].is-pressed,' + '.edit-post-header [aria-label="Block Inserter"].is-pressed,' + '.edit-site-header [aria-label="Block Inserter"].is-pressed,' + '.edit-widgets-header [aria-label="Block Inserter"].is-pressed,' + '.edit-widgets-header [aria-label="Add block"].is-pressed,' + '.edit-site-header-edit-mode__inserter-toggle.is-pressed,' + '.editor-header [aria-label="Block Inserter"].is-pressed');
});
}
/**
* Toggles the global inserter.
*/
async function toggleGlobalBlockInserter() {
// "Add block" selector is required to make sure performance comparison
// doesn't fail on older branches where we still had "Add block" as label.
await page.click('.editor-document-tools__inserter-toggle,' + '.edit-post-header [aria-label="Add block"],' + '.edit-site-header [aria-label="Add block"],' + '.edit-post-header [aria-label="Block Inserter"],' + '.edit-site-header [aria-label="Block Inserter"],' + '.edit-widgets-header [aria-label="Add block"],' + '.edit-widgets-header [aria-label="Block Inserter"],' + '.edit-site-header-edit-mode__inserter-toggle');
}
/**
* Selects the global inserter tab/category, unless it's already selected.
*
* @param {string} label The label of the tab to select.
*/
async function selectGlobalInserterTab(label) {
const tabs = await page.$('.block-editor-inserter__tabs');
if (!tabs) {
return; // Do nothing if tabs are unavailable (e.g. for inner blocks).
}
const activeTab = await page.waitForSelector(
// Targeting a class name is necessary here, because there are likely
// two implementations of the `Tabs` component visible to this test, and
// we want to confirm that it's waiting for the correct one.
'.block-editor-inserter__tabs [role="tab"][aria-selected="true"]');
const activeTabLabel = await page.evaluate(el => el.innerText, activeTab);
if (activeTabLabel === label) {
return; // Do nothing if the target tab is already active.
}
let labelSelector;
switch (label) {
case 'Blocks':
case 'Patterns':
case 'Media':
labelSelector = `. = "${label}"`;
break;
case 'Synced patterns':
// Synced patterns tab label is an icon, hence the different selector.
labelSelector = `@aria-label = "${label}"`;
break;
}
const targetTab = await page.waitForXPath(`//div[contains(@class, "block-editor-inserter__tabs")]//button[${labelSelector}]`);
await targetTab.click();
}
/**
* Moves focus to the selected block.
*/
async function focusSelectedBlock() {
// Ideally, there should be a UI way to focus the selected block.
await page.evaluate(() => {
wp.data.dispatch('core/block-editor').selectBlock(wp.data.select('core/block-editor').getSelectedBlockClientId(), 0);
});
}
/**
* Retrieves the document container by css class and checks to make sure the
* document's active element is within it.
*/
async function waitForInserterCloseAndContentFocus() {
await (0, _canvas.canvas)().waitForFunction(() => document.activeElement.closest('.block-editor-block-list__layout') !== null);
}
/**
* Searches for an entity matching given category and term via the global
* inserter. If nothing is found, null will be returned.
*
* Available categories: Blocks, Patterns, Reusable and Block Directory.
*
* @param {string} category The category to search within.
* @param {string} searchTerm The term to search the inserter for.
* @return {Promise<ElementHandle|null>} The handle of the element to be
* inserted or null if nothing was found.
*/
async function searchGlobalInserter(category, searchTerm) {
await openGlobalBlockInserter();
await page.waitForSelector(INSERTER_SEARCH_SELECTOR);
await page.focus(INSERTER_SEARCH_SELECTOR);
await (0, _pressKeyWithModifier.pressKeyWithModifier)('primary', 'a');
await page.keyboard.type(searchTerm);
// Wait for the default block list to disappear to prevent its items from
// being considered as search results. This is needed since we're debouncing
// search request.
await page.waitForSelector('.block-editor-inserter__block-list', {
hidden: true
});
let waitForInsertElement;
let waitForNoResults;
switch (category) {
case 'Blocks':
case 'Patterns':
case 'Synced patterns':
{
waitForInsertElement = async () => {
return await page.waitForXPath(`//*[@role='option' and contains(., '${searchTerm}')]`);
};
waitForNoResults = async () => {
await page.waitForSelector('.block-editor-inserter__no-results');
return null;
};
break;
}
case 'Block Directory':
{
waitForInsertElement = async () => {
// Return the first item from the Block Directory search results.
return await page.waitForSelector('.block-directory-downloadable-blocks-list button:first-child');
};
waitForNoResults = async () => {
// Use a soft timeout if Block Directory doesn't return anything
// within 5 seconds, as there's no "empty results" element being
// rendered when nothing is found.
return await new Promise(resolve => setTimeout(() => resolve(null), 5000));
};
}
}
return await Promise.race([waitForInsertElement(), waitForNoResults()]);
}
/**
* Inserts an entity matching given category and term via the global inserter.
* If the entity is not instantly available in the open inserter, a search will
* be performed. If the search returns no results, an error will be thrown.
*
* Available categories: Blocks, Patterns, Synced patterns and Block Directory.
*
* @param {string} category The category to insert from.
* @param {string} searchTerm The term by which to find the entity to insert.
*/
async function insertFromGlobalInserter(category, searchTerm) {
await openGlobalBlockInserter();
await selectGlobalInserterTab(category);
let insertButton;
if (['Blocks', 'Synced patterns'].includes(category)) {
// If it's a block, see if it's insertable without searching...
try {
insertButton = (await page.$x(`//*[@role='option' and contains(., '${searchTerm}')]`))[0];
} catch (error) {
// noop
}
}
// ...and if not, perform a global search.
if (!insertButton) {
insertButton = await searchGlobalInserter(category, searchTerm);
}
// Throw an error if nothing was found.
if (!insertButton) {
throw new Error(`Couldn't find "${searchTerm}" in the ${category} category.`);
}
// Insert found entity.
await insertButton.click();
// Extra wait for the reusable block to be ready.
if (category === 'Synced patterns') {
await (0, _canvas.canvas)().waitForSelector('.block-library-block__reusable-block-container');
}
// Extra wait for the Block Directory block to be ready.
if (category === 'Block Directory') {
await page.waitForSelector('.block-directory-downloadable-blocks-list button:first-child:not(.is-busy)');
}
await focusSelectedBlock();
await waitForInserterCloseAndContentFocus();
}
/**
* Searches for a block via the global inserter.
*
* @param {string} searchTerm The term to search the inserter for.
* @return {Promise<ElementHandle|null>} The handle of block to be
* inserted or null if nothing was found.
*/
async function searchForBlock(searchTerm) {
return await searchGlobalInserter('Blocks', searchTerm);
}
/**
* Searches for a pattern via the global inserter.
*
* @param {string} searchTerm The term to search the inserter for.
* @return {Promise<ElementHandle|null>} The handle of the pattern to be
* inserted or null if nothing was found.
*/
async function searchForPattern(searchTerm) {
return await searchGlobalInserter('Patterns', searchTerm);
}
/**
* Searches for a reusable block via the global inserter.
*
* @param {string} searchTerm The term to search the inserter for.
* @return {Promise<ElementHandle|null>} The handle of the reusable block to be
* inserted or null if nothing was found.
*/
async function searchForReusableBlock(searchTerm) {
return await searchGlobalInserter('Reusable', searchTerm);
}
/**
* Searches for a Block Directory block via the global inserter.
*
* @param {string} searchTerm The term to search the inserter for.
* @return {Promise<ElementHandle|null>} The handle of the Block Directory block
* to be inserted or null if nothing was found.
*/
async function searchForBlockDirectoryBlock(searchTerm) {
return await searchGlobalInserter('Block Directory', searchTerm);
}
/**
* Inserts a block matching a given search term via the global inserter.
*
* @param {string} searchTerm The term by which to find the block to insert.
*/
async function insertBlock(searchTerm) {
await insertFromGlobalInserter('Blocks', searchTerm);
}
/**
* Inserts a pattern matching a given search term via the global inserter.
*
* @param {string} searchTerm The term by which to find the pattern to insert.
*/
async function insertPattern(searchTerm) {
await insertFromGlobalInserter('Patterns', searchTerm);
}
/**
* Inserts a Block Directory block matching a given search term via the global
* inserter.
*
* @param {string} searchTerm The term by which to find the Block Directory
* block to insert.
*/
async function insertBlockDirectoryBlock(searchTerm) {
return await insertFromGlobalInserter('Block Directory', searchTerm);
}
//# sourceMappingURL=inserter.js.map