This commit is contained in:
Luigi Teschio 2022-07-18 11:38:49 +02:00 committed by GitHub
parent 1784b525df
commit 1761da37cd
20 changed files with 694 additions and 660 deletions

View File

@ -0,0 +1,38 @@
name: Report Flaky Tests
on:
workflow_run:
workflows: ['E2E tests']
types:
- completed
jobs:
report-to-issues:
name: Report to GitHub issues
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
# Checkout defaults to using the branch which triggered the event, which
# isn't necessarily `trunk` (e.g. in the case of a merge).
- uses: actions/checkout@v3
with:
repository: WordPress/gutenberg
ref: trunk
- name: Use desired version of NodeJS
uses: actions/setup-node@5b52f097d36d4b0b2f94ed6de710023fbb8b2236 # v3.1.0
with:
node-version-file: '.nvmrc'
cache: npm
- name: Npm install and build
# TODO: We don't have to build the entire project, just the action itself.
run: |
npm ci
npm run build:packages
- name: Report flaky tests
uses: ./packages/report-flaky-tests
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
label: 'type: flaky test'
artifact-name-prefix: flaky-tests-report

View File

@ -7,6 +7,10 @@ on:
jobs:
JSE2EWithGutenberg:
strategy:
fail-fast: false
matrix:
part: [1, 2, 3, 4, 5]
name: JavaScript E2E Tests (WP latest with Gutenberg plugin)
runs-on: ubuntu-latest
steps:
@ -62,19 +66,32 @@ jobs:
WOOCOMMERCE_BLOCKS_PHASE: 3
GUTENBERG_EDITOR_CONTEXT: 'gutenberg'
run: |
chmod -R 767 ./ #needed for permissions issues
node ./bin/wp-env-with-gutenberg.js
npm run wp-env start
npm run wp-env clean all
npm run test:e2e
npm run wp-env:config && npx cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --listTests > ~/.jest-e2e-tests
npx cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --runInBand --runTestsByPath $( awk 'NR % 5 == ${{ matrix.part }} - 1' < ~/.jest-e2e-tests )
- name: Upload artifacts on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: e2e-with-gutenberg-test-report
name: e2e-with-gutenberg-test-report-${{matrix.part}}
path: reports/e2e
- name: Archive flaky tests report
uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2
if: always()
with:
name: flaky-tests-report-${{ matrix.part }}
path: flaky-tests
if-no-files-found: ignore
JSE2ETests:
name: JavaScript E2E Tests (latest)
strategy:
fail-fast: false
matrix:
part: [1, 2, 3, 4, 5]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -131,12 +148,20 @@ jobs:
WOOCOMMERCE_BLOCKS_PHASE: 3
run: |
npm run wp-env start
npm run wp-env clean all
npm run test:e2e
npm run wp-env:config && npx cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --listTests > ~/.jest-e2e-tests
npx cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --runInBand --runTestsByPath $( awk 'NR % 5 == ${{ matrix.part }} - 1' < ~/.jest-e2e-tests )
- name: Upload artifacts on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: e2e-test-report
name: e2e-test-report-${{matrix.part}}
path: reports/e2e
- name: Archive flaky tests report
uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2
if: always()
with:
name: flaky-tests-report-${{ matrix.part }}
path: flaky-tests
if-no-files-found: ignore

File diff suppressed because it is too large Load Diff

View File

@ -68,8 +68,8 @@
"test": "wp-scripts test-unit-js --config tests/js/jest.config.json",
"test:debug": "ndb .",
"test:e2e": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js",
"test:e2e-dev": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --puppeteer-interactive",
"test:e2e-dev-watch": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --watch --puppeteer-interactive",
"test:e2e-dev": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config-dev.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js",
"test:e2e-dev-watch": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config-dev.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --watch",
"test:e2e:update": "npm run wp-env:config && cross-env JEST_PUPPETEER_CONFIG=tests/e2e/config/jest-puppeteer.config.js NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --updateSnapshot",
"test:help": "wp-scripts test-unit-js --help",
"test:performance": "npm run wp-env:config && cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.performance.config.js -- performance",
@ -133,8 +133,8 @@
"@wordpress/data-controls": "2.2.7",
"@wordpress/dependency-extraction-webpack-plugin": "3.2.1",
"@wordpress/dom": "3.2.7",
"@wordpress/e2e-test-utils": "7.4.0",
"@wordpress/e2e-tests": "4.3.0",
"@wordpress/e2e-test-utils": "7.8.0",
"@wordpress/e2e-tests": "4.6.0",
"@wordpress/element": "4.0.4",
"@wordpress/env": "4.5.0",
"@wordpress/html-entities": "3.2.3",
@ -164,7 +164,7 @@
"eslint-plugin-import": "2.25.4",
"eslint-plugin-woocommerce": "file:bin/eslint-plugin-woocommerce",
"eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
"expect-puppeteer": "6.0.2",
"expect-puppeteer": "6.1.0",
"follow-redirects": "1.14.9",
"fs-extra": "9.1.0",
"gh-pages": "3.2.3",
@ -189,7 +189,7 @@
"prettier": "npm:wp-prettier@2.6.2",
"progress-bar-webpack-plugin": "2.1.0",
"promptly": "3.2.0",
"puppeteer": "13.7.0",
"puppeteer": "15.2.0",
"react-docgen": "5.4.0",
"react-test-renderer": "17.0.2",
"request-promise": "4.2.6",

View File

@ -0,0 +1,9 @@
const config = require( './jest-puppeteer.config' );
module.exports = {
...config,
launch: {
...config.launch,
headless: false,
},
};

View File

@ -1,3 +1,7 @@
module.exports = {
exitOnPageError: false,
launch: {
ignoreDefaultArgs: [ '--disable-extensions' ],
args: [ '--no-sandbox', '--disable-dev-shm-usage' ],
},
};

View File

@ -3,7 +3,6 @@ module.exports = {
rootDir: '../../../',
// Automatically clear mock calls and instances between every test
clearMocks: true,
moduleNameMapper: {
'@woocommerce/blocks-test-utils': '<rootDir>/tests/utils',
},
@ -14,7 +13,8 @@ module.exports = {
'jest-html-reporters',
{ publicPath: './reports/e2e', filename: 'index.html' },
],
],
process.env.CI && '@wordpress/e2e-tests/config/flaky-tests-reporter.js',
].filter( Boolean ),
testEnvironment: '<rootDir>/tests/e2e/config/environment.js',
testRunner: 'jest-circus/runner',
@ -30,7 +30,6 @@ module.exports = {
'<rootDir>/tests/e2e/config/jest.setup.js',
'expect-puppeteer',
],
testPathIgnorePatterns: [ '<rootDir>/tests/e2e/specs/performance' ],
transformIgnorePatterns: [ 'node_modules/(?!(woocommerce)/)' ],
};

View File

@ -11,12 +11,20 @@ import {
visitAdminPage,
} from '@wordpress/e2e-test-utils';
import { setDefaultOptions } from 'expect-puppeteer';
import { get } from 'lodash';
/**
* Internal dependencies
*/
import { DEFAULT_TIMEOUT } from '../utils';
// Set the default test timeout.
jest.setTimeout( 120000 );
// 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 );
}
setDefaultOptions( { timeout: DEFAULT_TIMEOUT } );
/**
@ -110,12 +118,33 @@ function observeConsoleLogging() {
if ( ! OBSERVED_CONSOLE_MESSAGE_TYPES.hasOwnProperty( type ) ) {
return;
}
const text = message.text();
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;
}
// 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;
}
// Network errors are ignored only if we are intentionally testing
// offline mode.
if (
@ -124,10 +153,44 @@ function observeConsoleLogging() {
) {
return;
}
// 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;
}
const logFunction = OBSERVED_CONSOLE_MESSAGE_TYPES[ type ];
// Disable reason: We intentionally bubble up console error messages
// for debugging reasons. If you need to test explicitly the logging,
// use @wordpress/jest-console
// 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.
// eslint-disable-next-line no-console
console[ logFunction ]( text );
} );
@ -142,6 +205,9 @@ beforeAll( async () => {
observeConsoleLogging();
await setupBrowser();
await importSampleProducts();
await page.emulateMediaFeatures( [
{ name: 'prefers-reduced-motion', value: 'reduce' },
] );
} );
afterEach( async () => {

View File

@ -49,6 +49,7 @@ module.exports = async ( globalConfig ) => {
enablePaymentGateways(),
setupPageSettings(),
] ).catch( console.log );
const [ taxes, coupons, categories, tags, shippingZones, attributes ] =
results;
// Create products after categories.

View File

@ -14,7 +14,7 @@ exports[`Store Editing Templates Product Search Results block template should co
"<!-- wp:template-part {\\"slug\\":\\"header\\",\\"theme\\":\\"emptytheme\\",\\"tagName\\":\\"header\\"} /-->
<!-- wp:group {\\"layout\\":{\\"inherit\\":true}} -->
<div class=\\"wp-block-group\\"><!-- wp:woocommerce/legacy-template {\\"template\\":\\"archive-product\\"} /--></div>
<div class=\\"wp-block-group\\"><!-- wp:woocommerce/legacy-template {\\"template\\":\\"product-search-results\\"} /--></div>
<!-- /wp:group -->
<!-- wp:template-part {\\"slug\\":\\"footer\\",\\"theme\\":\\"emptytheme\\",\\"tagName\\":\\"footer\\"} /-->"

View File

@ -92,6 +92,14 @@ const BLOCK_DATA = {
},
name: 'woocommerce/legacy-template',
},
'product-search-results': {
attributes: {
title: 'WooCommerce Product Search Results Block',
template: 'product-search-results',
placeholder: 'archive-product',
},
name: 'woocommerce/legacy-template',
},
};
const SELECTORS = {
@ -515,7 +523,7 @@ describe( 'Store Editing Templates', () => {
);
expect( classicBlock.attributes.template ).toBe(
BLOCK_DATA[ 'archive-product' ].attributes.template
BLOCK_DATA[ 'product-search-results' ].attributes.template
);
expect( await getCurrentSiteEditorContent() ).toMatchSnapshot();
} );

View File

@ -53,7 +53,7 @@ const block = {
const FILTER_STOCK_STATUS_TITLE = 'Stock Status';
const FILTER_STOCK_STATUS_PROPERTY = 'In stock';
const FILTER_CAPACITY_TITLE = 'Capacity';
const FILTER_CAPACITY_TITLE = 'Capacity:';
const FILTER_CAPACITY_PROPERTY = '128gb';
const { selectors } = block;
@ -68,10 +68,25 @@ const insertBlocks = async () => {
const configurateFilterProductsByAttributeBlock = async (
pageOrCanvas: Page | Frame
) => {
await pageOrCanvas.click( selectors.editor.firstAttributeInTheList );
await pageOrCanvas.$eval(
selectors.editor.firstAttributeInTheList,
( el ) => ( el as HTMLElement ).click()
);
await pageOrCanvas.click( selectors.editor.doneButton );
};
const getActiveFilterTypeText = () =>
page.$eval(
selectors.frontend.activeFilterType,
( el ) => ( el as HTMLElement ).innerText
);
const getActiveFilterNameText = () =>
page.$eval(
selectors.frontend.activeFilterName,
( el ) => ( el as HTMLElement ).childNodes[ 0 ].textContent
);
describe( 'Shopper → Active Filters Block', () => {
describe( 'With All Products block', () => {
beforeAll( async () => {
@ -115,11 +130,10 @@ describe( 'Shopper → Active Filters Block', () => {
text: FILTER_CAPACITY_PROPERTY,
} );
await expect( page ).toMatchElement(
selectors.frontend.activeFilterType,
{
text: FILTER_CAPACITY_TITLE,
}
const activeFilterType = await getActiveFilterTypeText();
expect( activeFilterType ).toBe(
FILTER_CAPACITY_TITLE.toUpperCase()
);
await waitForAllProductsBlockLoaded();
@ -130,12 +144,9 @@ describe( 'Shopper → Active Filters Block', () => {
await expect( page ).toMatch( FILTER_STOCK_STATUS_TITLE );
await expect( page ).toMatchElement(
selectors.frontend.activeFilterName,
{
text: FILTER_STOCK_STATUS_PROPERTY,
}
);
const activeFilterNameText = await getActiveFilterNameText();
expect( activeFilterNameText ).toBe( FILTER_STOCK_STATUS_PROPERTY );
await waitForAllProductsBlockLoaded();
@ -189,13 +200,6 @@ describe( 'Shopper → Active Filters Block', () => {
text: FILTER_CAPACITY_PROPERTY,
} );
await expect( page ).toMatchElement(
selectors.frontend.removeAllFiltersButton,
{
text: 'Clear All',
}
);
await page.click( selectors.frontend.removeAllFiltersButton );
await waitForAllProductsBlockLoaded();
@ -252,13 +256,11 @@ describe( 'Shopper → Active Filters Block', () => {
await page.waitForSelector( block.class );
await expect( page ).toMatchElement(
selectors.frontend.activeFilterType,
{
text: FILTER_CAPACITY_TITLE,
}
);
const activeFilterType = await getActiveFilterTypeText();
expect( activeFilterType ).toBe(
FILTER_CAPACITY_TITLE.toUpperCase()
);
await page.waitForSelector( selectors.frontend.stockFilterBlock );
await expect( page ).toClick( 'label', {
@ -269,19 +271,8 @@ describe( 'Shopper → Active Filters Block', () => {
await page.waitForSelector( block.class );
await expect( page ).toMatchElement(
selectors.frontend.activeFilterType,
{
text: 'Stock Status',
}
);
await expect( page ).toMatchElement(
selectors.frontend.activeFilterName,
{
text: FILTER_STOCK_STATUS_PROPERTY,
}
);
const activeFilterNameText = await getActiveFilterNameText();
expect( activeFilterNameText ).toBe( FILTER_STOCK_STATUS_PROPERTY );
const products = await page.$$(
selectors.frontend.classicProductsList
@ -314,13 +305,6 @@ describe( 'Shopper → Active Filters Block', () => {
await page.waitForSelector( block.class );
await expect( page ).toMatchElement(
selectors.frontend.activeFilterType,
{
text: FILTER_CAPACITY_TITLE,
}
);
await clickLink( selectors.frontend.removeFilterButton );
const products = await page.$$(
@ -347,13 +331,6 @@ describe( 'Shopper → Active Filters Block', () => {
await page.waitForSelector( block.class );
await expect( page ).toMatchElement(
selectors.frontend.removeAllFiltersButton,
{
text: 'Clear All',
}
);
await clickLink( selectors.frontend.removeAllFiltersButton );
const products = await page.$$(

View File

@ -6,10 +6,7 @@ import {
selectBlockByName,
saveOrPublish,
} from '@woocommerce/blocks-test-utils';
import {
setCheckbox,
openDocumentSettingsSidebar,
} from '@woocommerce/e2e-utils';
import { setCheckbox } from '@woocommerce/e2e-utils';
import { visitAdminPage } from '@wordpress/e2e-test-utils';
/**
@ -17,6 +14,7 @@ import { visitAdminPage } from '@wordpress/e2e-test-utils';
*/
import { shopper, merchant, clickLink } from '../../../../utils';
import { SIMPLE_PHYSICAL_PRODUCT_NAME } from '.../../../../utils/constants';
import { openBlockEditorSettings } from '../../../utils';
const block = {
name: 'Checkout',
@ -44,11 +42,11 @@ describe( 'Shopper → Checkout → Account', () => {
await setCheckbox( '#woocommerce_enable_guest_checkout' );
await clickLink( 'button[name="save"]' );
await visitBlockPage( `${ block.name } Block` );
await openDocumentSettingsSidebar();
await selectBlockByName( block.slug );
await selectBlockByName(
'woocommerce/checkout-contact-information-block'
);
await openBlockEditorSettings( { isFSEEditor: false } );
//Enable shoppers to sign up at checkout option.
// eslint-disable-next-line jest/no-standalone-expect
await expect( page ).toClick( 'label', {

View File

@ -21,10 +21,15 @@ describe( 'Shopper → Cart', () => {
it( 'User can view empty cart message', async () => {
await shopper.block.goToCart();
await page.waitForSelector( '.wc-block-cart__empty-cart__title' );
// Verify cart is empty'
await expect( page ).toMatchElement( 'h2', {
text: 'Your cart is currently empty!',
} );
await expect( page ).toMatchElement(
'.wc-block-cart__empty-cart__title',
{
text: 'Your cart is currently empty!',
}
);
} );
it( 'User can remove a product from cart', async () => {

View File

@ -146,9 +146,7 @@ describe( `${ block.name } Block`, () => {
expect( isRefreshed ).not.toBeCalled();
await Promise.all( [
page.waitForNavigation( {
waitUntil: 'networkidle0',
} ),
page.waitForNavigation(),
page.click( selectors.frontend.filter ),
] );
@ -173,7 +171,7 @@ describe( `${ block.name } Block`, () => {
} );
await selectBlockByName( block.slug );
await openBlockEditorSettings();
await openBlockEditorSettings( { isFSEEditor: true } );
const [ filterButtonToggle ] = await page.$x(
block.selectors.editor.filterButtonToggle
);

View File

@ -52,13 +52,12 @@ const goToShopPage = () =>
const setMaxPrice = async () => {
await page.waitForSelector( selectors.frontend.priceMaxAmount );
await page.focus( selectors.frontend.priceMaxAmount );
await page.click( selectors.frontend.priceMaxAmount, {
clickCount: 3,
} );
await page.keyboard.type( '1.99' );
await page.$eval( selectors.frontend.priceMaxAmount, ( el ) =>
( el as HTMLElement ).blur()
await page.$eval(
selectors.frontend.priceMaxAmount,
( el ) => ( ( el as HTMLInputElement ).value = '' )
);
await page.keyboard.type( '1.99' );
await page.keyboard.press( 'Tab' );
};
describe( `${ block.name } Block`, () => {
@ -91,7 +90,6 @@ describe( `${ block.name } Block`, () => {
const isRefreshed = jest.fn( () => void 0 );
page.on( 'load', isRefreshed );
await setMaxPrice();
await page.waitForNetworkIdle();
await waitForAllProductsBlockLoaded();
await page.waitForSelector( selectors.frontend.productsList );
@ -121,6 +119,10 @@ describe( `${ block.name } Block`, () => {
await goToShopPage();
} );
beforeEach( async () => {
await goToShopPage();
} );
afterAll( async () => {
await deleteAllTemplates( 'wp_template' );
await deleteAllTemplates( 'wp_template_part' );
@ -145,12 +147,7 @@ describe( `${ block.name } Block`, () => {
await expect( page ).toMatch( block.foundProduct );
expect( isRefreshed ).not.toBeCalled();
await Promise.all( [
setMaxPrice(),
page.waitForNavigation( {
waitUntil: 'networkidle0',
} ),
] );
await Promise.all( [ page.waitForNavigation(), setMaxPrice() ] );
await page.waitForSelector(
selectors.frontend.classicProductsList
@ -177,7 +174,7 @@ describe( `${ block.name } Block`, () => {
} );
await selectBlockByName( block.slug );
await openBlockEditorSettings();
await openBlockEditorSettings( { isFSEEditor: true } );
await page.waitForXPath(
block.selectors.editor.filterButtonToggle
);

View File

@ -34,7 +34,7 @@ const block = {
frontend: {
productsList: '.wc-block-grid__products > li',
classicProductsList: '.products.columns-3 > li',
filter: 'label[for=outofstock]',
filter: 'input[id=outofstock]',
submitButton: '.wc-block-components-filter-submit-button',
},
},
@ -104,6 +104,10 @@ describe( `${ block.name } Block`, () => {
await goToShopPage();
} );
beforeEach( async () => {
await goToShopPage();
} );
afterAll( async () => {
await deleteAllTemplates( 'wp_template' );
await deleteAllTemplates( 'wp_template_part' );
@ -126,8 +130,13 @@ describe( `${ block.name } Block`, () => {
} );
expect( isRefreshed ).not.toBeCalled();
await page.click( selectors.frontend.filter );
await page.waitForNetworkIdle();
await page.waitForSelector( selectors.frontend.filter );
await Promise.all( [
page.waitForNavigation(),
page.click( selectors.frontend.filter ),
] );
const products = await page.$$(
selectors.frontend.classicProductsList
@ -150,10 +159,11 @@ describe( `${ block.name } Block`, () => {
await waitForCanvas();
await selectBlockByName( block.slug );
await openBlockEditorSettings();
await openBlockEditorSettings( { isFSEEditor: true } );
await page.waitForXPath(
block.selectors.editor.filterButtonToggle
);
const [ filterButtonToggle ] = await page.$x(
selectors.editor.filterButtonToggle
);

View File

@ -4,7 +4,6 @@
import { Coupon, HTTPClientFactory } from '@woocommerce/api';
import config from 'config';
import {
activateTheme,
disableSiteEditorWelcomeGuide,
openGlobalBlockInserter,
switchUserToAdmin,
@ -19,8 +18,9 @@ import fs from 'fs';
/**
* Internal dependencies
*/
import { elementExists, getElementData, getTextContent } from './page-utils';
import { elementExists, getTextContent } from './page-utils';
import { PERFORMANCE_REPORT_FILENAME } from '../utils/constants';
import { cli } from '../utils';
/**
* @typedef {import('@types/puppeteer').ElementHandle} ElementHandle
@ -64,7 +64,7 @@ const SELECTORS = {
savePrompt: '.entities-saved-states__text-prompt',
},
allProductsBlock: {
productsList: '.wc-block-grid__products > li.is-loading',
productsList: '.wc-block-grid__products > li > div:not(.is-loading)',
},
};
@ -134,6 +134,9 @@ export const closeModalIfExists = async () => {
};
export const openWidgetsEditorBlockInserter = async () => {
await page.waitForSelector(
'.edit-widgets-header [aria-label="Add block"],.edit-widgets-header [aria-label="Toggle block inserter"]'
);
await page.click(
'.edit-widgets-header [aria-label="Add block"],.edit-widgets-header [aria-label="Toggle block inserter"]'
);
@ -247,10 +250,6 @@ export async function saveTemplate() {
await page.waitForSelector( savePrompt );
await page.click( confirmSave );
await page.waitForSelector( `${ saveButton }[aria-disabled="true"]` );
await page.waitForResponse( ( res ) => {
// Will match both templates and template_parts endpoints.
return res.url().includes( '/wp/v2/template' );
} );
}
/**
@ -329,22 +328,18 @@ export async function filterCurrentBlocks( predicate ) {
* @param {string} themeSlug The theme the test suite should use
*/
export function useTheme( themeSlug ) {
let previousTheme;
beforeAll( async () => {
await switchUserToAdmin();
await visitAdminPage( 'themes.php' );
previousTheme = await getElementData(
SELECTORS.themesPage.currentTheme,
'slug'
await cli(
`npm run wp-env run tests-cli wp theme activate ${ themeSlug }`
);
await activateTheme( themeSlug );
await switchUserToAdmin();
} );
afterAll( async () => {
await activateTheme( previousTheme );
await cli(
`npm run wp-env run tests-cli wp theme activate storefront`
);
await switchUserToAdmin();
} );
}
@ -436,27 +431,30 @@ export const createCoupon = async ( coupon ) => {
/**
* Open the block editor settings menu.
*
* @param {Object} [root0]
* @param {boolean} [root0.isFSEEditor] Amount to be applied. Defaults to 5.
*/
export const openBlockEditorSettings = async () => {
const buttonSelector =
'.edit-site-header__actions button[aria-label="Settings"]';
const isSideBarAlreadyOpened = await page.$(
'.interface-interface-skeleton__sidebar'
);
export const openBlockEditorSettings = async ( { isFSEEditor = false } ) => {
const buttonSelector = isFSEEditor
? '.edit-site-header__actions button[aria-label="Settings"]'
: '.edit-post-header__settings button[aria-label="Settings"]';
const isPressed = `${ buttonSelector }.is-pressed`;
const isSideBarAlreadyOpened = await page.$( isPressed );
if ( isSideBarAlreadyOpened === null ) {
await page.click( buttonSelector );
// @ts-ignore
await page.$eval( buttonSelector, ( el ) => el.click() );
}
};
/**
* Wait for all Products Block is loaded completely: when the skeleton disappears, and the products are visible.
* Wait for all Products Block is loaded completely: when the skeleton disappears, and the products are visible
*/
export const waitForAllProductsBlockLoaded = async () => {
await page.waitForSelector( SELECTORS.allProductsBlock.productsList, {
hidden: true,
} );
await page.waitForNetworkIdle();
await page.waitForSelector( SELECTORS.allProductsBlock.productsList );
await page.waitForTimeout( 5000 );
};

View File

@ -34,10 +34,6 @@ export const shopper = {
await page.goto( url, {
waitUntil: 'networkidle0',
} );
await expect( page ).toMatchElement( 'h1', {
text: blockName,
} );
},
goToCart: async () => {
@ -212,6 +208,7 @@ export const shopper = {
// prettier-ignore
fillBillingDetails: async ( customerBillingDetails ) => {
await page.waitForSelector("#billing-fields");
const companyInputField = await page.$( '#billing-company' );
if ( companyInputField ) {
@ -474,4 +471,23 @@ export const shopper = {
page.click( 'button[name="login"]' ),
] );
},
addToCartFromShopPage: async ( productIdOrTitle ) => {
if ( Number.isInteger( productIdOrTitle ) ) {
const addToCart = `a[data-product_id="${ productIdOrTitle }"]`;
await page.click( addToCart );
await expect( page ).toMatchElement( addToCart + '.added' );
} else {
const addToCartXPath =
`//li[contains(@class, "type-product") and a/h2[contains(text(), "${ productIdOrTitle }")]]` +
'//a[contains(@class, "add_to_cart_button") and contains(@class, "ajax_add_to_cart")';
const [ addToCartButton ] = await page.$x( addToCartXPath + ']' );
await addToCartButton.click();
await page.waitForXPath(
addToCartXPath + ' and contains(@class, "added")]'
);
}
},
};

View File

@ -22,7 +22,9 @@ import { clickLink } from '.';
* @param {string} link the page or post you want to visit.
*/
async function visitPage( link ) {
await page.goto( link );
await page.goto( link, {
waitUntil: 'load',
} );
await page.waitForSelector( '.edit-post-layout' );
const isWelcomeGuideActive = await page.evaluate( () =>
wp.data.select( 'core/edit-post' ).isFeatureActive( 'welcomeGuide' )