Product Collection: add e2e tests with all product elements included (#45623)

* Prepare test cases

* Shorten out the test description

* Add first test in a post with dummy expect

* Verify all content is displayed

* Add the test in Product Archive and Home Page

* Add changelog

* Add tag to Beanie product

* Switch to Beanie which is on sale to verify on sale badge

* Add comments to explain the expects

* Adjust the expected content

* Switch to lower case in expect

* Switch from woocommerce/product-summary to core/post-excerpt

* Adjust products.sh

* Improve method waiting for products to show so it;'s deterministic

* Refresh locators in template

* Remove unnecessary check

* Eslint disable: expects are extracted to function so disable eslint compaining there's no expect

* Adjust other test after amending products setup

* Change the verify happening in a wrong place

* Tests adjustments

* Revert Blog Home template before performing a test

* Fix other tests
This commit is contained in:
Karol Manijak 2024-03-19 08:59:58 +01:00 committed by GitHub
parent 72f5db9b6d
commit 6cb52c00c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 190 additions and 18 deletions

View File

@ -22,12 +22,13 @@ image3=$(wp post list --post_type=attachment --field=ID --name="hoodie-2.jpg" --
wp post meta update $hoodie_product_id _product_image_gallery "$image1,$image2,$image3"
# Create a tag, so we can add tests for tag-related blocks and templates.
beanie_product_id=$(wp post list --post_type=product --field=ID --name="Beanie" --format=ids)
tag_id=$(wp wc product_tag create --name="Recommended" --slug="recommended" --description="Curated products selected by our experts" --porcelain --user=1)
wp wc product update $hoodie_product_id --tags="[ { \"id\": $tag_id } ]" --user=1
wp wc product update $beanie_product_id --tags="[ { \"id\": $tag_id } ]" --user=1
# This is a non-hacky work around to set up the cross sells product.
cap_product_id=$(wp post list --post_type=product --field=ID --name="Cap" --format=ids)
beanie_product_id=$(wp post list --post_type=product --field=ID --name="Beanie" --format=ids)
wp post meta update $beanie_product_id _crosssell_ids "$cap_product_id"
# Set a product out of stock

View File

@ -2,7 +2,7 @@
* External dependencies
*/
import { test as base, expect } from '@woocommerce/e2e-playwright-utils';
import type { Request } from '@playwright/test';
import type { Request, Locator } from '@playwright/test';
/**
* Internal dependencies
@ -47,6 +47,108 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.addToCartButtons ).toHaveCount( 9 );
} );
test.describe( 'Renders correctly with all Product Elements', async () => {
const insertProductElements = async (
pageObject: ProductCollectionPage
) => {
// By default there are inner blocks:
// - woocommerce/product-image
// - core/post-title
// - woocommerce/product-price
// - woocommerce/product-button
// We're adding remaining ones
const productElements = [
{ name: 'woocommerce/product-rating', attributes: {} },
{ name: 'woocommerce/product-sku', attributes: {} },
{ name: 'woocommerce/product-stock-indicator', attributes: {} },
{ name: 'woocommerce/product-sale-badge', attributes: {} },
{
name: 'core/post-excerpt',
attributes: {
__woocommerceNamespace:
'woocommerce/product-collection/product-summary',
},
},
{
name: 'core/post-terms',
attributes: { term: 'product_tag' },
},
{
name: 'core/post-terms',
attributes: { term: 'product_cat' },
},
];
await Promise.all(
productElements.map( async ( productElement ) => {
await pageObject.insertBlockInProductCollection(
productElement
);
} )
);
};
const verifyProductContent = async ( product: Locator ) => {
await expect( product ).toContainText( 'Beanie' ); // core/post-title
await expect( product ).toContainText(
'$20.00 Original price was: $20.00.$18.00Current price is: $18.00.'
); // woocommerce/product-price
await expect( product ).toContainText( 'woo-beanie' ); // woocommerce/product-sku
await expect( product ).toContainText( 'In stock' ); // woocommerce/product-stock-indicator
await expect( product ).toContainText(
'This is a simple product.'
); // core/post-excerpt
await expect( product ).toContainText( 'Accessories' ); // core/post-terms - product_cat
await expect( product ).toContainText( 'Recommended' ); // core/post-terms - product_tag
await expect( product ).toContainText( 'SaleProduct on sale' ); // woocommerce/product-sale-badge
await expect( product ).toContainText( 'Add to cart' ); // woocommerce/product-button
};
// Expects are collected in verifyProductContent function
// eslint-disable-next-line playwright/expect-expect
test( 'In a post', async ( { pageObject } ) => {
await pageObject.createNewPostAndInsertBlock();
await insertProductElements( pageObject );
await pageObject.publishAndGoToFrontend();
const product = pageObject.products.nth( 1 );
await verifyProductContent( product );
} );
// Expects are collected in verifyProductContent function
// eslint-disable-next-line playwright/expect-expect
test( 'In a Product Archive (Product Catalog)', async ( {
pageObject,
editor,
} ) => {
await pageObject.replaceProductsWithProductCollectionInTemplate(
'woocommerce/woocommerce//archive-product'
);
await insertProductElements( pageObject );
await editor.saveSiteEditorEntities();
await pageObject.goToProductCatalogFrontend();
const product = pageObject.products.nth( 1 );
await verifyProductContent( product );
} );
// Expects are collected in verifyProductContent function
// eslint-disable-next-line playwright/expect-expect
test( 'On a Home Page', async ( { pageObject, editor } ) => {
await pageObject.goToHomePageAndInsertCollection();
await insertProductElements( pageObject );
await editor.saveSiteEditorEntities();
await pageObject.goToHomePageFrontend();
const product = pageObject.products.nth( 1 );
await verifyProductContent( product );
} );
} );
test.describe( 'Product Collection Sidebar Settings', () => {
test.beforeEach( async ( { pageObject } ) => {
await pageObject.createNewPostAndInsertBlock();
@ -200,10 +302,16 @@ test.describe( 'Product Collection', () => {
await pageObject.setFilterComboboxValue( filterName, [
'Recommended',
] );
await expect( pageObject.productTitles ).toHaveText( [ 'Hoodie' ] );
await expect( pageObject.productTitles ).toHaveText( [
'Beanie',
'Hoodie',
] );
await pageObject.publishAndGoToFrontend();
await expect( pageObject.productTitles ).toHaveText( [ 'Hoodie' ] );
await expect( pageObject.productTitles ).toHaveText( [
'Beanie',
'Hoodie',
] );
} );
test( 'Products can be filtered based on product attributes like color, size etc.', async ( {
@ -316,7 +424,7 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.products ).toHaveCount( 4 );
} );
test.describe( 'Sync with current template (former "Inherit query from template")', () => {
test.describe( 'Sync with current template', () => {
test( 'should not be visible on posts', async ( {
pageObject,
} ) => {
@ -333,8 +441,10 @@ test.describe( 'Product Collection', () => {
test( 'should work as expected in Product Catalog template', async ( {
pageObject,
editor,
} ) => {
await pageObject.goToProductCatalogAndInsertCollection();
await editor.openDocumentSettingsSidebar();
const sidebarSettings =
await pageObject.locateSidebarSettings();
@ -385,10 +495,12 @@ test.describe( 'Product Collection', () => {
test( 'is enabled by default in 1st Product Collection and disabled in 2nd+', async ( {
pageObject,
editor,
} ) => {
// First Product Catalog
// Option should be visible & ENABLED by default
await pageObject.goToProductCatalogAndInsertCollection();
await editor.openDocumentSettingsSidebar();
const sidebarSettings =
await pageObject.locateSidebarSettings();
@ -638,7 +750,7 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.products ).toHaveCount( 4 );
} );
test( "Product Catalog Collection can be added in post and doesn't inherit query from template", async ( {
test( "Product Catalog Collection can be added in post and doesn't sync query with template", async ( {
pageObject,
} ) => {
await pageObject.createNewPostAndInsertBlock( 'productCatalog' );
@ -656,12 +768,14 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.products ).toHaveCount( 9 );
} );
test( 'Product Catalog Collection can be added in product archive and inherits query from template', async ( {
test( 'Product Catalog Collection can be added in product archive and syncs query with template', async ( {
pageObject,
editor,
} ) => {
await pageObject.goToProductCatalogAndInsertCollection(
'productCatalog'
);
await editor.openDocumentSettingsSidebar();
const sidebarSettings = await pageObject.locateSidebarSettings();
const input = sidebarSettings.locator(

View File

@ -5,6 +5,11 @@ import { Locator, Page } from '@playwright/test';
import { TemplateApiUtils, EditorUtils } from '@woocommerce/e2e-utils';
import { Editor, Admin } from '@wordpress/e2e-test-utils-playwright';
/**
* Internal dependencies
*/
import { BLOCK_THEME_SLUG } from '../../utils/constants';
export const SELECTORS = {
productTemplate: '.wc-block-product-template',
product: '.wc-block-product-template .wc-block-product',
@ -127,6 +132,7 @@ class ProductCollectionPage {
async createNewPostAndInsertBlock( collection?: Collections ) {
await this.admin.createNewPost( { legacyCanvas: true } );
await this.editorUtils.closeWelcomeGuideModal();
await this.insertProductCollection();
await this.chooseCollectionInPost( collection );
await this.refreshLocators( 'editor' );
@ -184,34 +190,55 @@ class ProductCollectionPage {
this.BLOCK_SLUG
);
await this.chooseCollectionInTemplate( collection );
await this.refreshLocators( 'editor' );
await this.editor.saveSiteEditorEntities();
}
async goToProductCatalogFrontend() {
await this.page.goto( `/shop` );
await this.refreshLocators( 'frontend' );
}
async goToHomePageFrontend() {
await this.page.goto( `/` );
await this.refreshLocators( 'frontend' );
}
async insertProductCollection() {
await this.editor.insertBlock( { name: this.BLOCK_SLUG } );
}
async goToProductCatalogAndInsertCollection( collection?: Collections ) {
await this.templateApiUtils.revertTemplate(
'woocommerce/woocommerce//archive-product'
);
async goToTemplateAndInsertCollection(
template: string,
collection?: Collections
) {
await this.templateApiUtils.revertTemplate( template );
await this.admin.visitSiteEditor( {
postId: 'woocommerce/woocommerce//archive-product',
postId: template,
postType: 'wp_template',
} );
await this.editorUtils.waitForSiteEditorFinishLoading();
await this.editor.canvas.click( 'body' );
await this.insertProductCollection();
await this.chooseCollectionInTemplate( collection );
await this.editor.openDocumentSettingsSidebar();
await this.refreshLocators( 'editor' );
await this.editor.saveSiteEditorEntities();
}
async goToHomePageAndInsertCollection( collection?: Collections ) {
await this.goToTemplateAndInsertCollection(
`${ BLOCK_THEME_SLUG }//home`,
collection
);
}
async goToProductCatalogAndInsertCollection( collection?: Collections ) {
await this.goToTemplateAndInsertCollection(
'woocommerce/woocommerce//archive-product',
collection
);
}
async searchProducts( phrase: string ) {
await this.page
.getByLabel( SELECTORS.productSearchLabel )
@ -516,6 +543,25 @@ class ProductCollectionPage {
await this.page.setViewportSize( { width, height } );
}
async insertBlockInProductCollection( block: {
name: string;
attributes: object;
} ) {
await this.waitForProductsToLoad();
const productTemplate = await this.editorUtils.getBlockByName(
'woocommerce/product-template'
);
const productTemplateId =
( await productTemplate.getAttribute( 'data-block' ) ) ?? '';
await this.editor.selectBlocks( productTemplate );
await this.editorUtils.insertBlock(
block,
undefined,
productTemplateId
);
}
async insertProductCollectionInSingleProductBlock() {
this.insertSingleProductBlock();
@ -618,10 +664,17 @@ class ProductCollectionPage {
}
private async waitForProductsToLoad() {
// Wait for the product blocks to be loaded.
await this.page.waitForSelector( 'wc-block-product-template__spinner', {
state: 'detached',
} );
const loaderInTemplate = this.page
.frameLocator( 'iframe[name="editor-canvas"]' )
.getByLabel( 'Block: Product Template' )
.locator( 'circle' );
const loaderInPost = this.page
.getByLabel( 'Block: Product Template' )
.locator( 'circle' );
await Promise.all( [
loaderInTemplate.waitFor( { state: 'hidden', timeout: 100000 } ),
loaderInPost.waitFor( { state: 'hidden', timeout: 100000 } ),
] );
}
}

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Product Collection: Add E2E tests confirming all Product Elements are rendered correctly