2022-06-13 09:15:29 +00:00
|
|
|
/* eslint-disable no-unused-vars */
|
2020-02-11 16:13:20 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import {
|
|
|
|
enablePageDialogAccept,
|
|
|
|
isOfflineMode,
|
|
|
|
setBrowserViewport,
|
|
|
|
switchUserToAdmin,
|
|
|
|
switchUserToTest,
|
|
|
|
visitAdminPage,
|
|
|
|
} from '@wordpress/e2e-test-utils';
|
2022-06-13 09:15:29 +00:00
|
|
|
import { setDefaultOptions } from 'expect-puppeteer';
|
2022-07-18 09:38:49 +00:00
|
|
|
import { get } from 'lodash';
|
2023-04-03 13:02:36 +00:00
|
|
|
|
2022-06-13 09:15:29 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { DEFAULT_TIMEOUT } from '../utils';
|
2021-04-22 11:37:27 +00:00
|
|
|
// Set the default test timeout.
|
2022-03-08 14:50:16 +00:00
|
|
|
jest.setTimeout( 120000 );
|
2022-07-18 09:38:49 +00:00
|
|
|
// Retry failed tests at most 2 times in CI.
|
|
|
|
// This enables `flaky-tests-reporter` and `report-flaky-tests` GitHub action
|
|
|
|
// to mark test as flaky and automatically create a tracking issue about it.
|
|
|
|
if ( process.env.CI ) {
|
|
|
|
jest.retryTimes( 2 );
|
|
|
|
}
|
|
|
|
|
2022-06-13 09:15:29 +00:00
|
|
|
setDefaultOptions( { timeout: DEFAULT_TIMEOUT } );
|
2020-02-11 16:13:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of page event tuples of [ eventName, handler ].
|
|
|
|
*
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
|
|
|
const pageEvents = [];
|
|
|
|
/**
|
|
|
|
* Set of console logging types observed to protect against unexpected yet
|
|
|
|
* handled (i.e. not catastrophic) errors or warnings. Each key corresponds
|
|
|
|
* to the Puppeteer ConsoleMessage type, its value the corresponding function
|
|
|
|
* on the console global object.
|
|
|
|
*
|
|
|
|
* @type {Object<string,string>}
|
|
|
|
*/
|
|
|
|
const OBSERVED_CONSOLE_MESSAGE_TYPES = {
|
|
|
|
error: 'error',
|
|
|
|
};
|
|
|
|
async function setupBrowser() {
|
|
|
|
await setBrowserViewport( 'large' );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-05-18 13:09:30 +00:00
|
|
|
* Navigates to WooCommerce's import page and imports sample products.
|
2020-02-11 16:13:20 +00:00
|
|
|
*
|
|
|
|
* @return {Promise} Promise resolving once products have been imported.
|
|
|
|
*/
|
|
|
|
async function importSampleProducts() {
|
|
|
|
await switchUserToAdmin();
|
2020-06-05 14:50:17 +00:00
|
|
|
// Visit `/wp-admin/edit.php?post_type=product` so we can see a list of products and decide if we should import them or not.
|
|
|
|
await visitAdminPage( 'edit.php', 'post_type=product' );
|
|
|
|
const emptyState = await page.evaluate( () =>
|
|
|
|
window.find( 'Ready to start selling something awesome' )
|
2020-02-11 16:13:20 +00:00
|
|
|
);
|
2020-06-05 14:50:17 +00:00
|
|
|
const noProduct = await page.evaluate( () =>
|
|
|
|
window.find( 'No products found' )
|
2020-02-11 16:13:20 +00:00
|
|
|
);
|
2020-06-05 14:50:17 +00:00
|
|
|
if ( emptyState || noProduct ) {
|
|
|
|
// Visit Import Products page.
|
|
|
|
await visitAdminPage(
|
|
|
|
'edit.php',
|
|
|
|
'post_type=product&page=product_importer'
|
|
|
|
);
|
|
|
|
await page.click( 'a.woocommerce-importer-toggle-advanced-options' );
|
|
|
|
await page.focus( '#woocommerce-importer-file-url' );
|
|
|
|
// local path for sample data that is included with woo.
|
|
|
|
await page.keyboard.type(
|
|
|
|
'wp-content/plugins/woocommerce/sample-data/sample_products.csv'
|
|
|
|
);
|
|
|
|
await page.click( '.wc-actions .button-next' );
|
|
|
|
await page.waitForSelector( '.wc-importer-mapping-table' );
|
|
|
|
await page.select(
|
|
|
|
'.wc-importer-mapping-table tr:nth-child(29) select',
|
|
|
|
''
|
|
|
|
);
|
|
|
|
await page.click( '.wc-actions .button-next' );
|
|
|
|
await page.waitForXPath(
|
|
|
|
"//*[@class='woocommerce-importer-done' and contains(., 'Import complete! ')]"
|
|
|
|
);
|
|
|
|
await switchUserToTest();
|
|
|
|
}
|
2020-02-11 16:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds an event listener to the page to handle additions of page event
|
|
|
|
* handlers, to assure that they are removed at test teardown.
|
|
|
|
*/
|
|
|
|
function capturePageEventsForTearDown() {
|
|
|
|
page.on( 'newListener', ( eventName, listener ) => {
|
|
|
|
pageEvents.push( [ eventName, listener ] );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all bound page event handlers.
|
|
|
|
*/
|
|
|
|
function removePageEvents() {
|
|
|
|
pageEvents.forEach( ( [ eventName, handler ] ) => {
|
|
|
|
page.removeListener( eventName, handler );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a page event handler to emit uncaught exception to process if one of
|
|
|
|
* the observed console logging types is encountered.
|
|
|
|
*/
|
|
|
|
function observeConsoleLogging() {
|
|
|
|
page.on( 'console', ( message ) => {
|
|
|
|
const type = message.type();
|
|
|
|
if ( ! OBSERVED_CONSOLE_MESSAGE_TYPES.hasOwnProperty( type ) ) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-18 09:38:49 +00:00
|
|
|
|
|
|
|
let text = message.text();
|
|
|
|
|
|
|
|
// An exception is made for _blanket_ deprecation warnings: Those
|
|
|
|
// which log regardless of whether a deprecated feature is in use.
|
|
|
|
if ( text.includes( 'This is a global warning' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A chrome advisory warning about SameSite cookies is informational
|
|
|
|
// about future changes, tracked separately for improvement in core.
|
|
|
|
//
|
|
|
|
// See: https://core.trac.wordpress.org/ticket/37000
|
|
|
|
// See: https://www.chromestatus.com/feature/5088147346030592
|
|
|
|
// See: https://www.chromestatus.com/feature/5633521622188032
|
|
|
|
if (
|
|
|
|
text.includes( 'A cookie associated with a cross-site resource' )
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-11 16:13:20 +00:00
|
|
|
// Viewing posts on the front end can result in this error, which
|
|
|
|
// has nothing to do with Gutenberg.
|
|
|
|
if ( text.includes( 'net::ERR_UNKNOWN_URL_SCHEME' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-18 09:38:49 +00:00
|
|
|
|
2020-02-11 16:13:20 +00:00
|
|
|
// Network errors are ignored only if we are intentionally testing
|
|
|
|
// offline mode.
|
|
|
|
if (
|
|
|
|
text.includes( 'net::ERR_INTERNET_DISCONNECTED' ) &&
|
|
|
|
isOfflineMode()
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-18 09:38:49 +00:00
|
|
|
|
|
|
|
// As of WordPress 5.3.2 in Chrome 79, navigating to the block editor
|
|
|
|
// (Posts > Add New) will display a console warning about
|
|
|
|
// non - unique IDs.
|
|
|
|
// See: https://core.trac.wordpress.org/ticket/23165
|
|
|
|
if ( text.includes( 'elements with non-unique id #_wpnonce' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore all JQMIGRATE (jQuery migrate) deprecation warnings.
|
|
|
|
if ( text.includes( 'JQMIGRATE' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-11 16:13:20 +00:00
|
|
|
const logFunction = OBSERVED_CONSOLE_MESSAGE_TYPES[ type ];
|
2022-07-18 09:38:49 +00:00
|
|
|
|
|
|
|
// As of Puppeteer 1.6.1, `message.text()` wrongly returns an object of
|
|
|
|
// type JSHandle for error logging, instead of the expected string.
|
|
|
|
//
|
|
|
|
// See: https://github.com/GoogleChrome/puppeteer/issues/3397
|
|
|
|
//
|
|
|
|
// The recommendation there to asynchronously resolve the error value
|
|
|
|
// upon a console event may be prone to a race condition with the test
|
|
|
|
// completion, leaving a possibility of an error not being surfaced
|
|
|
|
// correctly. Instead, the logic here synchronously inspects the
|
|
|
|
// internal object shape of the JSHandle to find the error text. If it
|
|
|
|
// cannot be found, the default text value is used instead.
|
|
|
|
text = get(
|
|
|
|
message.args(),
|
|
|
|
[ 0, '_remoteObject', 'description' ],
|
|
|
|
text
|
|
|
|
);
|
|
|
|
|
|
|
|
// Disable reason: We intentionally bubble up the console message
|
|
|
|
// which, unless the test explicitly anticipates the logging via
|
|
|
|
// @wordpress/jest-console matchers, will cause the intended test
|
|
|
|
// failure.
|
|
|
|
|
2020-02-11 16:13:20 +00:00
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console[ logFunction ]( text );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Before every test suite run, delete all content created by the test. This ensures
|
|
|
|
// other posts/comments/etc. aren't dirtying tests and tests don't depend on
|
|
|
|
// each other's side-effects.
|
|
|
|
beforeAll( async () => {
|
|
|
|
capturePageEventsForTearDown();
|
|
|
|
enablePageDialogAccept();
|
|
|
|
observeConsoleLogging();
|
|
|
|
await setupBrowser();
|
|
|
|
await importSampleProducts();
|
2022-07-18 09:38:49 +00:00
|
|
|
await page.emulateMediaFeatures( [
|
|
|
|
{ name: 'prefers-reduced-motion', value: 'reduce' },
|
|
|
|
] );
|
2020-02-11 16:13:20 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
afterEach( async () => {
|
|
|
|
await setupBrowser();
|
|
|
|
} );
|
|
|
|
|
|
|
|
afterAll( () => {
|
|
|
|
removePageEvents();
|
|
|
|
} );
|