Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f6b4125
Prepare test cases
kmanijak Mar 15, 2024
4266260
Shorten out the test description
kmanijak Mar 15, 2024
9d51f78
Add first test in a post with dummy expect
kmanijak Mar 15, 2024
9d35567
Verify all content is displayed
kmanijak Mar 15, 2024
596ec51
Add the test in Product Archive and Home Page
kmanijak Mar 15, 2024
3ce4376
Add changelog
kmanijak Mar 15, 2024
d94d0a5
Add tag to Beanie product
kmanijak Mar 15, 2024
da88101
Switch to Beanie which is on sale to verify on sale badge
kmanijak Mar 15, 2024
60c6127
Add comments to explain the expects
kmanijak Mar 15, 2024
e158eef
Adjust the expected content
kmanijak Mar 15, 2024
4d4a713
Switch to lower case in expect
kmanijak Mar 15, 2024
7736a84
Switch from woocommerce/product-summary to core/post-excerpt
kmanijak Mar 15, 2024
4e14f92
Adjust products.sh
kmanijak Mar 15, 2024
5bbd445
Improve method waiting for products to show so it;'s deterministic
kmanijak Mar 15, 2024
d007a35
Refresh locators in template
kmanijak Mar 15, 2024
017b0aa
Remove unnecessary check
kmanijak Mar 15, 2024
1afae8b
Eslint disable: expects are extracted to function so disable eslint c…
kmanijak Mar 18, 2024
ba93429
Adjust other test after amending products setup
kmanijak Mar 18, 2024
18d94d4
Merge branch 'trunk' into 45214-product-collection-add-e2e-tests-with…
kmanijak Mar 18, 2024
2462eef
Change the verify happening in a wrong place
kmanijak Mar 18, 2024
cb2af67
Tests adjustments
kmanijak Mar 18, 2024
fc2c6fe
Revert Blog Home template before performing a test
kmanijak Mar 18, 2024
778666d
Fix other tests
kmanijak Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion plugins/woocommerce-blocks/tests/e2e/bin/scripts/products.sh
Expand Up @@ -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
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 ( {
Expand Down Expand Up @@ -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,
} ) => {
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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' );
Expand All @@ -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(
Expand Down
Expand Up @@ -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',
Expand Down Expand Up @@ -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' );
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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 } ),
] );
}
}

Expand Down
@@ -0,0 +1,4 @@
Significance: patch
Type: dev

Product Collection: Add E2E tests confirming all Product Elements are rendered correctly