Standardize linting: e2e js packages (#32794)

This commit is contained in:
Paul Sealock 2022-05-05 15:02:50 +12:00 committed by GitHub
parent 4915c389ef
commit 489ebf2cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 3844 additions and 2957 deletions

View File

@ -0,0 +1,3 @@
module.exports = {
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
};

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Standardize linting: Ensure e2e packages are lintable

View File

@ -1,4 +1,4 @@
const { customerBilling, customerShipping } = require('./shared'); const { customerBilling, customerShipping } = require( './shared' );
/** /**
* A basic order. * A basic order.
@ -53,10 +53,10 @@ const couponLines = {
/** /**
* Builds an example order request. * Builds an example order request.
* *
* @returns {Object} Sample Order payload. * @return {Object} Sample Order payload.
*/ */
const getOrderExample = () => { const getOrderExample = () => {
let orderExample = { const orderExample = {
id: 0, id: 0,
payment_method: 'cod', payment_method: 'cod',
payment_method_title: 'Cash on Delivery', payment_method_title: 'Cash on Delivery',
@ -73,7 +73,7 @@ const getOrderExample = () => {
coupon_lines: [ couponLines ], coupon_lines: [ couponLines ],
}; };
return orderExample; return orderExample;
} };
module.exports = { module.exports = {
order, order,

View File

@ -6,40 +6,53 @@ const {
postRequest, postRequest,
putRequest, putRequest,
deleteRequest, deleteRequest,
} = require('../utils/request'); } = require( '../utils/request' );
const getProducts = ( params = {} ) => getRequest( 'products', params ); const getProducts = ( params = {} ) => getRequest( 'products', params );
const createProduct = ( data ) => postRequest( 'products', data ); const createProduct = ( data ) => postRequest( 'products', data );
const createProductVariations = ( parentId, variations ) => postRequest( const createProductVariations = ( parentId, variations ) =>
`products/${ parentId }/variations/batch`, postRequest( `products/${ parentId }/variations/batch`, {
{
create: variations, create: variations,
} } );
) const createProducts = ( products ) =>
const createProducts = ( products ) => postRequest( 'products/batch', { create: products } ); postRequest( 'products/batch', { create: products } );
const createProductCategory = ( data ) => postRequest( 'products/categories', data ); const createProductCategory = ( data ) =>
const createProductAttribute = ( name ) => postRequest( 'products/attributes', { name } ); postRequest( 'products/categories', data );
const createProductAttributeTerms = ( parentId, termNames ) => postRequest( const createProductAttribute = ( name ) =>
`products/attributes/${ parentId }/terms/batch`, postRequest( 'products/attributes', { name } );
{ const createProductAttributeTerms = ( parentId, termNames ) =>
create: termNames.map( name => ( { name } ) ) postRequest( `products/attributes/${ parentId }/terms/batch`, {
} create: termNames.map( ( name ) => ( { name } ) ),
); } );
const createProductReview = ( productId, review ) => postRequest( 'products/reviews', { const createProductReview = ( productId, review ) =>
product_id: productId, postRequest( 'products/reviews', {
...review, product_id: productId,
} ); ...review,
const updateProductReview = ( reviewId, data = {} ) => putRequest( `products/reviews/${ reviewId }`, data ); } );
const updateProductReview = ( reviewId, data = {} ) =>
putRequest( `products/reviews/${ reviewId }`, data );
const createProductTag = ( name ) => postRequest( 'products/tags', { name } ); const createProductTag = ( name ) => postRequest( 'products/tags', { name } );
const createShippingClass = ( name ) => postRequest( 'products/shipping_classes', { name } ); const createShippingClass = ( name ) =>
postRequest( 'products/shipping_classes', { name } );
const createTaxClass = ( name ) => postRequest( 'taxes/classes', { name } ); const createTaxClass = ( name ) => postRequest( 'taxes/classes', { name } );
const createSampleCategories = async () => { const createSampleCategories = async () => {
const { body: clothing } = await createProductCategory( { name: 'Clothing' } ); const { body: clothing } = await createProductCategory( {
const { body: accessories } = await createProductCategory( { name: 'Accessories', parent: clothing.id } ); name: 'Clothing',
const { body: hoodies } = await createProductCategory( { name: 'Hoodies', parent: clothing.id } ); } );
const { body: tshirts } = await createProductCategory( { name: 'Tshirts', parent: clothing.id } ); const { body: accessories } = await createProductCategory( {
name: 'Accessories',
parent: clothing.id,
} );
const { body: hoodies } = await createProductCategory( {
name: 'Hoodies',
parent: clothing.id,
} );
const { body: tshirts } = await createProductCategory( {
name: 'Tshirts',
parent: clothing.id,
} );
const { body: decor } = await createProductCategory( { name: 'Decor' } ); const { body: decor } = await createProductCategory( { name: 'Decor' } );
const { body: music } = await createProductCategory( { name: 'Music' } ); const { body: music } = await createProductCategory( { name: 'Music' } );
@ -56,8 +69,18 @@ const createSampleCategories = async () => {
const createSampleAttributes = async () => { const createSampleAttributes = async () => {
const { body: color } = await createProductAttribute( 'Color' ); const { body: color } = await createProductAttribute( 'Color' );
const { body: size } = await createProductAttribute( 'Size' ); const { body: size } = await createProductAttribute( 'Size' );
const { body: colors } = await createProductAttributeTerms( color.id, [ 'Blue', 'Gray', 'Green', 'Red', 'Yellow' ] ); const { body: colors } = await createProductAttributeTerms( color.id, [
const { body: sizes } = await createProductAttributeTerms( size.id, [ 'Large', 'Medium', 'Small' ] ); 'Blue',
'Gray',
'Green',
'Red',
'Yellow',
] );
const { body: sizes } = await createProductAttributeTerms( size.id, [
'Large',
'Medium',
'Small',
] );
return { return {
color, color,
@ -73,7 +96,7 @@ const createSampleTags = async () => {
return { return {
cool, cool,
}; };
} };
const createSampleShippingClasses = async () => { const createSampleShippingClasses = async () => {
const { body: freight } = await createShippingClass( 'Freight' ); const { body: freight } = await createShippingClass( 'Freight' );
@ -81,7 +104,7 @@ const createSampleShippingClasses = async () => {
return { return {
freight, freight,
}; };
} };
const createSampleTaxClasses = async () => { const createSampleTaxClasses = async () => {
const { body: reducedRate } = await createTaxClass( 'Reduced Rate' ); const { body: reducedRate } = await createTaxClass( 'Reduced Rate' );
@ -89,12 +112,13 @@ const createSampleTaxClasses = async () => {
return { return {
reducedRate, reducedRate,
}; };
} };
const createSampleSimpleProducts = async ( categories, attributes, tags ) => { const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
const description = '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' const description =
+ 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. ' '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' +
+ 'Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n'; 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. ' +
'Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const { body: simpleProducts } = await createProducts( [ const { body: simpleProducts } = await createProducts( [
{ {
@ -151,15 +175,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Red' ] options: [ 'Red' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 62, 63, 61, 60 ], related_ids: [ 62, 63, 61, 60 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'T-Shirt with Logo', name: 'T-Shirt with Logo',
@ -215,15 +239,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Gray' ] options: [ 'Gray' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 59, 67, 66, 56 ], related_ids: [ 59, 67, 66, 56 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Single', name: 'Single',
@ -249,8 +273,9 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
{ {
id: '2579cf07-8b08-4c25-888a-b6258dd1f035', id: '2579cf07-8b08-4c25-888a-b6258dd1f035',
name: 'Single', name: 'Single',
file: 'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/single.jpg' file:
} 'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/single.jpg',
},
], ],
download_limit: 1, download_limit: 1,
download_expiry: 1, download_expiry: 1,
@ -285,7 +310,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 68 ], related_ids: [ 68 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Album', name: 'Album',
@ -311,13 +336,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
{ {
id: 'cc10249f-1de2-44d4-93d3-9f88ae629f76', id: 'cc10249f-1de2-44d4-93d3-9f88ae629f76',
name: 'Single 1', name: 'Single 1',
file: 'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/single.jpg' file:
'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/single.jpg',
}, },
{ {
id: 'aea8ef69-ccdc-4d83-8e21-3c395ebb9411', id: 'aea8ef69-ccdc-4d83-8e21-3c395ebb9411',
name: 'Single 2', name: 'Single 2',
file: 'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/album.jpg' file:
} 'https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/album.jpg',
},
], ],
download_limit: 1, download_limit: 1,
download_expiry: 1, download_expiry: 1,
@ -352,7 +379,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 69 ], related_ids: [ 69 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Polo', name: 'Polo',
@ -408,15 +435,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Blue' ] options: [ 'Blue' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 59, 56, 66, 76 ], related_ids: [ 59, 56, 66, 76 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Long Sleeve Tee', name: 'Long Sleeve Tee',
@ -472,15 +499,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Green' ] options: [ 'Green' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 59, 56, 76, 67 ], related_ids: [ 59, 56, 76, 67 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Hoodie with Zipper', name: 'Hoodie with Zipper',
@ -536,7 +563,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 57, 58 ], related_ids: [ 57, 58 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Hoodie with Pocket', name: 'Hoodie with Pocket',
@ -592,15 +619,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Gray' ] options: [ 'Gray' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 65, 57, 58 ], related_ids: [ 65, 57, 58 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Sunglasses', name: 'Sunglasses',
@ -656,7 +683,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 60, 62, 77, 61 ], related_ids: [ 60, 62, 77, 61 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Cap', name: 'Cap',
@ -712,15 +739,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Yellow' ] options: [ 'Yellow' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 60, 77, 61, 63 ], related_ids: [ 60, 77, 61, 63 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Belt', name: 'Belt',
@ -776,7 +803,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 63, 77, 62, 60 ], related_ids: [ 63, 77, 62, 60 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'Beanie', name: 'Beanie',
@ -832,15 +859,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Red' ] options: [ 'Red' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 63, 62, 61, 77 ], related_ids: [ 63, 62, 61, 77 ],
stock_status: 'instock' stock_status: 'instock',
}, },
{ {
name: 'T-Shirt', name: 'T-Shirt',
@ -896,15 +923,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Gray' ] options: [ 'Gray' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 67, 76, 56, 66 ], related_ids: [ 67, 76, 56, 66 ],
stock_status: 'onbackorder' stock_status: 'onbackorder',
}, },
{ {
name: 'Hoodie with Logo', name: 'Hoodie with Logo',
@ -960,16 +987,16 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: false, variation: false,
options: [ 'Blue' ] options: [ 'Blue' ],
} },
], ],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [ 57, 65 ], related_ids: [ 57, 65 ],
stock_status: 'instock' stock_status: 'instock',
} },
] ); ] );
return simpleProducts.create; return simpleProducts.create;
@ -985,9 +1012,9 @@ const createSampleExternalProducts = async ( categories ) => {
featured: false, featured: false,
catalog_visibility: 'visible', catalog_visibility: 'visible',
description: description:
'<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' +
+ 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' +
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n', 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n',
short_description: '<p>This is an external product.</p>\n', short_description: '<p>This is an external product.</p>\n',
sku: 'wp-pennant', sku: 'wp-pennant',
price: '11.05', price: '11.05',
@ -1003,7 +1030,8 @@ const createSampleExternalProducts = async ( categories ) => {
downloads: [], downloads: [],
download_limit: 0, download_limit: 0,
download_expiry: 0, download_expiry: 0,
external_url: 'https://mercantile.wordpress.org/product/wordpress-pennant/', external_url:
'https://mercantile.wordpress.org/product/wordpress-pennant/',
button_text: 'Buy on the WordPress swag store!', button_text: 'Buy on the WordPress swag store!',
tax_status: 'taxable', tax_status: 'taxable',
tax_class: '', tax_class: '',
@ -1034,7 +1062,7 @@ const createSampleExternalProducts = async ( categories ) => {
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
related_ids: [], related_ids: [],
stock_status: 'instock' stock_status: 'instock',
}, },
] ); ] );
@ -1056,9 +1084,9 @@ const createSampleGroupedProduct = async ( categories ) => {
featured: false, featured: false,
catalog_visibility: 'visible', catalog_visibility: 'visible',
description: description:
'<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' +
+ 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' +
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n', 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n',
short_description: '<p>This is a grouped product.</p>\n', short_description: '<p>This is a grouped product.</p>\n',
sku: 'logo-collection', sku: 'logo-collection',
price: '18', price: '18',
@ -1102,10 +1130,10 @@ const createSampleGroupedProduct = async ( categories ) => {
attributes: [], attributes: [],
default_attributes: [], default_attributes: [],
variations: [], variations: [],
grouped_products: logoProducts.map( p => p.id ), grouped_products: logoProducts.map( ( p ) => p.id ),
menu_order: 0, menu_order: 0,
related_ids: [], related_ids: [],
stock_status: 'instock' stock_status: 'instock',
}, },
] ); ] );
@ -1113,9 +1141,10 @@ const createSampleGroupedProduct = async ( categories ) => {
}; };
const createSampleVariableProducts = async ( categories, attributes ) => { const createSampleVariableProducts = async ( categories, attributes ) => {
const description = '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' const description =
+ 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' '<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ' +
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n'; 'Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. ' +
'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const { body: hoodie } = await createProduct( { const { body: hoodie } = await createProduct( {
name: 'Hoodie', name: 'Hoodie',
date_created_gmt: '2021-09-18T15:50:19', date_created_gmt: '2021-09-18T15:50:19',
@ -1170,7 +1199,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: true, variation: true,
options: [ 'Blue', 'Green', 'Red' ] options: [ 'Blue', 'Green', 'Red' ],
}, },
{ {
id: 0, id: 0,
@ -1178,165 +1207,168 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 1, position: 1,
visible: true, visible: true,
variation: true, variation: true,
options: [ 'Yes', 'No' ] options: [ 'Yes', 'No' ],
} },
], ],
default_attributes: [], default_attributes: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
stock_status: 'instock' stock_status: 'instock',
} ); } );
const variationDescription = const variationDescription =
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sagittis orci ac odio dictum tincidunt. ' '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sagittis orci ac odio dictum tincidunt. ' +
+ 'Donec ut metus leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. ' 'Donec ut metus leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. ' +
+ 'Sed luctus, dui eu sagittis sodales, nulla nibh sagittis augue, vel porttitor diam enim non metus. ' 'Sed luctus, dui eu sagittis sodales, nulla nibh sagittis augue, vel porttitor diam enim non metus. ' +
+ 'Vestibulum aliquam augue neque. Phasellus tincidunt odio eget ullamcorper efficitur. ' 'Vestibulum aliquam augue neque. Phasellus tincidunt odio eget ullamcorper efficitur. ' +
+ 'Cras placerat ut turpis pellentesque vulputate. Nam sed consequat tortor. Curabitur finibus sapien dolor. ' 'Cras placerat ut turpis pellentesque vulputate. Nam sed consequat tortor. Curabitur finibus sapien dolor. ' +
+ 'Ut eleifend tellus nec erat pulvinar dignissim. Nam non arcu purus. Vivamus et massa massa.</p>\n'; 'Ut eleifend tellus nec erat pulvinar dignissim. Nam non arcu purus. Vivamus et massa massa.</p>\n';
const { body: hoodieVariations } = await createProductVariations( hoodie.id, [ const { body: hoodieVariations } = await createProductVariations(
{ hoodie.id,
date_created_gmt: '2021-09-19T15:50:20', [
description: variationDescription, {
sku: 'woo-hoodie-blue-logo', date_created_gmt: '2021-09-19T15:50:20',
price: '45', description: variationDescription,
regular_price: '45', sku: 'woo-hoodie-blue-logo',
sale_price: '', price: '45',
date_on_sale_from_gmt: null, regular_price: '45',
date_on_sale_to_gmt: null, sale_price: '',
on_sale: false, date_on_sale_from_gmt: null,
status: 'publish', date_on_sale_to_gmt: null,
purchasable: true, on_sale: false,
virtual: false, status: 'publish',
downloadable: false, purchasable: true,
downloads: [], virtual: false,
download_limit: 0, downloadable: false,
download_expiry: 0, downloads: [],
tax_status: 'taxable', download_limit: 0,
tax_class: '', download_expiry: 0,
manage_stock: false, tax_status: 'taxable',
stock_quantity: null, tax_class: '',
stock_status: 'instock', manage_stock: false,
backorders: 'no', stock_quantity: null,
backorders_allowed: false, stock_status: 'instock',
backordered: false, backorders: 'no',
low_stock_amount: null, backorders_allowed: false,
weight: '1.5', backordered: false,
dimensions: { length: '10', width: '8', height: '3' }, low_stock_amount: null,
shipping_class: '', weight: '1.5',
attributes: [ dimensions: { length: '10', width: '8', height: '3' },
{ id: attributes.color.id, option: 'Blue' }, shipping_class: '',
{ id: 0, name: 'Logo', option: 'Yes' } attributes: [
], { id: attributes.color.id, option: 'Blue' },
menu_order: 0 { id: 0, name: 'Logo', option: 'Yes' },
}, ],
{ menu_order: 0,
date_created_gmt: '2021-09-20T15:50:20', },
description: variationDescription, {
sku: 'woo-hoodie-blue', date_created_gmt: '2021-09-20T15:50:20',
price: '45', description: variationDescription,
regular_price: '45', sku: 'woo-hoodie-blue',
sale_price: '', price: '45',
date_on_sale_from_gmt: null, regular_price: '45',
date_on_sale_to_gmt: null, sale_price: '',
on_sale: false, date_on_sale_from_gmt: null,
status: 'publish', date_on_sale_to_gmt: null,
purchasable: true, on_sale: false,
virtual: false, status: 'publish',
downloadable: false, purchasable: true,
downloads: [], virtual: false,
download_limit: 0, downloadable: false,
download_expiry: 0, downloads: [],
tax_status: 'taxable', download_limit: 0,
tax_class: '', download_expiry: 0,
manage_stock: false, tax_status: 'taxable',
stock_quantity: null, tax_class: '',
stock_status: 'instock', manage_stock: false,
backorders: 'no', stock_quantity: null,
backorders_allowed: false, stock_status: 'instock',
backordered: false, backorders: 'no',
low_stock_amount: null, backorders_allowed: false,
weight: '1.5', backordered: false,
dimensions: { length: '10', width: '8', height: '3' }, low_stock_amount: null,
shipping_class: '', weight: '1.5',
attributes: [ dimensions: { length: '10', width: '8', height: '3' },
{ id: attributes.color.id, option: 'Blue' }, shipping_class: '',
{ id: 0, name: 'Logo', option: 'No' } attributes: [
], { id: attributes.color.id, option: 'Blue' },
menu_order: 3 { id: 0, name: 'Logo', option: 'No' },
}, ],
{ menu_order: 3,
date_created_gmt: '2021-09-21T15:50:20', },
description: variationDescription, {
sku: 'woo-hoodie-green', date_created_gmt: '2021-09-21T15:50:20',
price: '45', description: variationDescription,
regular_price: '45', sku: 'woo-hoodie-green',
sale_price: '', price: '45',
date_on_sale_from_gmt: null, regular_price: '45',
date_on_sale_to_gmt: null, sale_price: '',
on_sale: false, date_on_sale_from_gmt: null,
status: 'publish', date_on_sale_to_gmt: null,
purchasable: true, on_sale: false,
virtual: false, status: 'publish',
downloadable: false, purchasable: true,
downloads: [], virtual: false,
download_limit: 0, downloadable: false,
download_expiry: 0, downloads: [],
tax_status: 'taxable', download_limit: 0,
tax_class: '', download_expiry: 0,
manage_stock: false, tax_status: 'taxable',
stock_quantity: null, tax_class: '',
stock_status: 'instock', manage_stock: false,
backorders: 'no', stock_quantity: null,
backorders_allowed: false, stock_status: 'instock',
backordered: false, backorders: 'no',
low_stock_amount: null, backorders_allowed: false,
weight: '1.5', backordered: false,
dimensions: { length: '10', width: '8', height: '3' }, low_stock_amount: null,
shipping_class: '', weight: '1.5',
attributes: [ dimensions: { length: '10', width: '8', height: '3' },
{ id: attributes.color.id, option: 'Green' }, shipping_class: '',
{ id: 0, name: 'Logo', option: 'No' } attributes: [
], { id: attributes.color.id, option: 'Green' },
menu_order: 2 { id: 0, name: 'Logo', option: 'No' },
}, ],
{ menu_order: 2,
date_created_gmt: '2021-09-22T15:50:19', },
description: variationDescription, {
sku: 'woo-hoodie-red', date_created_gmt: '2021-09-22T15:50:19',
price: '42', description: variationDescription,
regular_price: '45', sku: 'woo-hoodie-red',
sale_price: '42', price: '42',
date_on_sale_from_gmt: null, regular_price: '45',
date_on_sale_to_gmt: null, sale_price: '42',
on_sale: true, date_on_sale_from_gmt: null,
status: 'publish', date_on_sale_to_gmt: null,
purchasable: true, on_sale: true,
virtual: false, status: 'publish',
downloadable: false, purchasable: true,
downloads: [], virtual: false,
download_limit: 0, downloadable: false,
download_expiry: 0, downloads: [],
tax_status: 'taxable', download_limit: 0,
tax_class: '', download_expiry: 0,
manage_stock: false, tax_status: 'taxable',
stock_quantity: null, tax_class: '',
stock_status: 'instock', manage_stock: false,
backorders: 'no', stock_quantity: null,
backorders_allowed: false, stock_status: 'instock',
backordered: false, backorders: 'no',
low_stock_amount: null, backorders_allowed: false,
weight: '1.5', backordered: false,
dimensions: { length: '10', width: '8', height: '3' }, low_stock_amount: null,
shipping_class: '', weight: '1.5',
attributes: [ dimensions: { length: '10', width: '8', height: '3' },
{ id: attributes.color.id, option: 'Red' }, shipping_class: '',
{ id: 0, name: 'Logo', option: 'No' } attributes: [
], { id: attributes.color.id, option: 'Red' },
menu_order: 1 { id: 0, name: 'Logo', option: 'No' },
} ],
] ); menu_order: 1,
},
]
);
const { body: vneck } = await createProduct( { const { body: vneck } = await createProduct( {
name: 'V-Neck T-Shirt', name: 'V-Neck T-Shirt',
@ -1392,20 +1424,20 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 0, position: 0,
visible: true, visible: true,
variation: true, variation: true,
options: [ 'Blue', 'Green', 'Red' ] options: [ 'Blue', 'Green', 'Red' ],
}, },
{ {
id: attributes.size.id, id: attributes.size.id,
position: 1, position: 1,
visible: true, visible: true,
variation: true, variation: true,
options: [ 'Large', 'Medium', 'Small' ] options: [ 'Large', 'Medium', 'Small' ],
} },
], ],
default_attributes: [], default_attributes: [],
grouped_products: [], grouped_products: [],
menu_order: 0, menu_order: 0,
stock_status: 'instock' stock_status: 'instock',
} ); } );
const { body: vneckVariations } = await createProductVariations( vneck.id, [ const { body: vneckVariations } = await createProductVariations( vneck.id, [
@ -1439,7 +1471,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' }, dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '', shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Blue' } ], attributes: [ { id: attributes.color.id, option: 'Blue' } ],
menu_order: 0 menu_order: 0,
}, },
{ {
date_created_gmt: '2021-09-25T15:50:19', date_created_gmt: '2021-09-25T15:50:19',
@ -1471,7 +1503,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' }, dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '', shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Green' } ], attributes: [ { id: attributes.color.id, option: 'Green' } ],
menu_order: 0 menu_order: 0,
}, },
{ {
date_created_gmt: '2021-09-26T15:50:19', date_created_gmt: '2021-09-26T15:50:19',
@ -1503,8 +1535,8 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' }, dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '', shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Red' } ], attributes: [ { id: attributes.color.id, option: 'Red' } ],
menu_order: 0 menu_order: 0,
} },
] ); ] );
return { return {
@ -1530,15 +1562,15 @@ const createSampleHierarchicalProducts = async () => {
return { return {
parent, parent,
child, child,
} };
}; };
const createSampleProductReviews = async ( simpleProducts ) => { const createSampleProductReviews = async ( simpleProducts ) => {
const cap = simpleProducts.find( p => p.name === 'Cap' ); const cap = simpleProducts.find( ( p ) => p.name === 'Cap' );
const shirt = simpleProducts.find( p => p.name === 'T-Shirt' ); const shirt = simpleProducts.find( ( p ) => p.name === 'T-Shirt' );
const sunglasses = simpleProducts.find( p => p.name === 'Sunglasses' ); const sunglasses = simpleProducts.find( ( p ) => p.name === 'Sunglasses' );
let { body: review1 } = await createProductReview( cap.id, { const { body: review1 } = await createProductReview( cap.id, {
rating: 3, rating: 3,
review: 'Decent cap.', review: 'Decent cap.',
reviewer: 'John Doe', reviewer: 'John Doe',
@ -1549,7 +1581,7 @@ const createSampleProductReviews = async ( simpleProducts ) => {
// See: https://github.com/woocommerce/woocommerce/issues/29906. // See: https://github.com/woocommerce/woocommerce/issues/29906.
await updateProductReview( review1.id ); await updateProductReview( review1.id );
let { body: review2 } = await createProductReview( shirt.id, { const { body: review2 } = await createProductReview( shirt.id, {
rating: 5, rating: 5,
review: 'The BEST shirt ever!!', review: 'The BEST shirt ever!!',
reviewer: 'Shannon Smith', reviewer: 'Shannon Smith',
@ -1557,7 +1589,7 @@ const createSampleProductReviews = async ( simpleProducts ) => {
} ); } );
await updateProductReview( review2.id ); await updateProductReview( review2.id );
let { body: review3 } = await createProductReview( sunglasses.id, { const { body: review3 } = await createProductReview( sunglasses.id, {
rating: 1, rating: 1,
review: 'These are way too expensive.', review: 'These are way too expensive.',
reviewer: 'Tim Frugalman', reviewer: 'Tim Frugalman',
@ -1569,9 +1601,11 @@ const createSampleProductReviews = async ( simpleProducts ) => {
}; };
const createSampleProductOrders = async ( simpleProducts ) => { const createSampleProductOrders = async ( simpleProducts ) => {
const single = simpleProducts.find( p => p.name === 'Single' ); const single = simpleProducts.find( ( p ) => p.name === 'Single' );
const beanie = simpleProducts.find( p => p.name === 'Beanie with Logo' ); const beanie = simpleProducts.find(
const shirt = simpleProducts.find( p => p.name === 'T-Shirt' ); ( p ) => p.name === 'Beanie with Logo'
);
const shirt = simpleProducts.find( ( p ) => p.name === 'T-Shirt' );
const { body: order } = await postRequest( 'orders', { const { body: order } = await postRequest( 'orders', {
set_paid: true, set_paid: true,
@ -1602,10 +1636,17 @@ const createSampleData = async () => {
const shippingClasses = await createSampleShippingClasses(); const shippingClasses = await createSampleShippingClasses();
const taxClasses = await createSampleTaxClasses(); const taxClasses = await createSampleTaxClasses();
const simpleProducts = await createSampleSimpleProducts( categories, attributes, tags ); const simpleProducts = await createSampleSimpleProducts(
categories,
attributes,
tags
);
const externalProducts = await createSampleExternalProducts( categories ); const externalProducts = await createSampleExternalProducts( categories );
const groupedProducts = await createSampleGroupedProduct( categories ); const groupedProducts = await createSampleGroupedProduct( categories );
const variableProducts = await createSampleVariableProducts( categories, attributes ); const variableProducts = await createSampleVariableProducts(
categories,
attributes
);
const hierarchicalProducts = await createSampleHierarchicalProducts(); const hierarchicalProducts = await createSampleHierarchicalProducts();
const reviewIds = await createSampleProductReviews( simpleProducts ); const reviewIds = await createSampleProductReviews( simpleProducts );
@ -1642,19 +1683,15 @@ const deleteSampleData = async ( sampleData ) => {
orders, orders,
} = sampleData; } = sampleData;
const productIds = [].concat( const productIds = []
simpleProducts.map( p => p.id ) .concat( simpleProducts.map( ( p ) => p.id ) )
).concat( .concat( externalProducts.map( ( p ) => p.id ) )
externalProducts.map( p => p.id ) .concat( groupedProducts.map( ( p ) => p.id ) )
).concat( .concat( [ variableProducts.hoodie.id, variableProducts.vneck.id ] )
groupedProducts.map( p => p.id ) .concat( [
).concat( [ hierarchicalProducts.parent.id,
variableProducts.hoodie.id, hierarchicalProducts.child.id,
variableProducts.vneck.id, ] );
] ).concat( [
hierarchicalProducts.parent.id,
hierarchicalProducts.child.id,
] );
orders.forEach( async ( { id } ) => { orders.forEach( async ( { id } ) => {
await deleteRequest( `orders/${ id }`, true ); await deleteRequest( `orders/${ id }`, true );

View File

@ -3,12 +3,12 @@
* *
* Note that by default the update endpoint is limited to 100 objects to be created, updated, or deleted. * Note that by default the update endpoint is limited to 100 objects to be created, updated, or deleted.
* *
* @param {string} action Batch action. Must be one of: create, update, or delete. * @param {string} action Batch action. Must be one of: create, update, or delete.
* @param {Array} resources A list of resource objects. For the delete action, this will be a list of IDs. * @param {Array} resources A list of resource objects. For the delete action, this will be a list of IDs.
* @param {Object} payload The batch payload object. Defaults to an empty object. * @param {Object} payload The batch payload object. Defaults to an empty object.
* @returns {Object} The payload to send to the batch endpoint. * @return {Object} The payload to send to the batch endpoint.
*/ */
const batch = ( action, resources = [], payload = {} ) => { const batch = ( action, resources = [], payload = {} ) => {
if ( ! [ 'create', 'update', 'delete' ].includes( action ) ) { if ( ! [ 'create', 'update', 'delete' ].includes( action ) ) {
return; return;
} }

View File

@ -7,7 +7,7 @@ const errorResponse = {
code: '', code: '',
message: '', message: '',
data: { data: {
status: 400 status: 400,
}, },
}; };

View File

@ -1,9 +1,6 @@
const { customerBilling, customerShipping } = require('./customer'); const { customerBilling, customerShipping } = require( './customer' );
const { const { batch, getBatchPayloadExample } = require( './batch-update' );
batch, const { errorResponse } = require( './error-response' );
getBatchPayloadExample
} = require('./batch-update');
const { errorResponse } = require('./error-response');
module.exports = { module.exports = {
customerBilling, customerBilling,

View File

@ -6,7 +6,9 @@
* - `flat_rate` * - `flat_rate`
* - `local_pickup` * - `local_pickup`
* *
* @returns shipping method object that can serve as a request payload for adding a shipping method to a shipping zone. * @param methodId
* @param cost
* @return shipping method object that can serve as a request payload for adding a shipping method to a shipping zone.
*/ */
const getShippingMethodExample = ( methodId, cost ) => { const getShippingMethodExample = ( methodId, cost ) => {
const shippingMethodExample = { const shippingMethodExample = {
@ -15,7 +17,7 @@ const getShippingMethodExample = ( methodId, cost ) => {
if ( cost !== undefined ) { if ( cost !== undefined ) {
shippingMethodExample.settings = { shippingMethodExample.settings = {
cost: cost, cost,
}; };
} }

View File

@ -13,7 +13,7 @@ const shippingZone = {
/** /**
* Constructs a default shipping zone object. * Constructs a default shipping zone object.
* *
* @returns default shipping zone * @return default shipping zone
*/ */
const getShippingZoneExample = () => { const getShippingZoneExample = () => {
return shippingZone; return shippingZone;

View File

@ -1,8 +1,13 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { getRequest, postRequest, putRequest, deleteRequest } = require('../utils/request'); const {
const { coupon, shared } = require('../data'); getRequest,
postRequest,
putRequest,
deleteRequest,
} = require( '../utils/request' );
const { coupon, shared } = require( '../data' );
/** /**
* WooCommerce Coupon endpoints. * WooCommerce Coupon endpoints.
@ -17,14 +22,15 @@ const couponsApi = {
path: 'coupons', path: 'coupons',
responseCode: 201, responseCode: 201,
payload: coupon, payload: coupon,
coupon: async ( couponDetails ) => postRequest( 'coupons', couponDetails ), coupon: async ( couponDetails ) =>
postRequest( 'coupons', couponDetails ),
}, },
retrieve: { retrieve: {
name: 'Retrieve a coupon', name: 'Retrieve a coupon',
method: 'GET', method: 'GET',
path: 'coupons/<id>', path: 'coupons/<id>',
responseCode: 200, responseCode: 200,
coupon: async ( couponId ) => getRequest( `coupons/${couponId}` ), coupon: async ( couponId ) => getRequest( `coupons/${ couponId }` ),
}, },
listAll: { listAll: {
name: 'List all coupons', name: 'List all coupons',
@ -39,7 +45,8 @@ const couponsApi = {
path: 'coupons/<id>', path: 'coupons/<id>',
responseCode: 200, responseCode: 200,
payload: coupon, payload: coupon,
coupon: async ( couponId, couponDetails ) => putRequest( `coupons/${couponId}`, couponDetails ), coupon: async ( couponId, couponDetails ) =>
putRequest( `coupons/${ couponId }`, couponDetails ),
}, },
delete: { delete: {
name: 'Delete a coupon', name: 'Delete a coupon',
@ -47,9 +54,10 @@ const couponsApi = {
path: 'coupons/<id>', path: 'coupons/<id>',
responseCode: 200, responseCode: 200,
payload: { payload: {
force: false force: false,
}, },
coupon: async ( couponId, deletePermanently ) => deleteRequest( `coupons/${couponId}`, deletePermanently ), coupon: async ( couponId, deletePermanently ) =>
deleteRequest( `coupons/${ couponId }`, deletePermanently ),
}, },
batch: { batch: {
name: 'Batch update coupons', name: 'Batch update coupons',
@ -57,8 +65,9 @@ const couponsApi = {
path: 'coupons/batch', path: 'coupons/batch',
responseCode: 200, responseCode: 200,
payload: shared.getBatchPayloadExample( coupon ), payload: shared.getBatchPayloadExample( coupon ),
coupons: async ( batchUpdatePayload ) => postRequest( `coupons/batch`, batchUpdatePayload ), coupons: async ( batchUpdatePayload ) =>
postRequest( `coupons/batch`, batchUpdatePayload ),
}, },
}; };
module.exports = { couponsApi }; module.exports = { couponsApi };

View File

@ -1,7 +1,12 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { getRequest, postRequest, putRequest, deleteRequest } = require('../utils/request'); const {
getRequest,
postRequest,
putRequest,
deleteRequest,
} = require( '../utils/request' );
/** /**
* WooCommerce Products endpoints. * WooCommerce Products endpoints.
@ -15,28 +20,31 @@ const productsApi = {
method: 'POST', method: 'POST',
path: 'products', path: 'products',
responseCode: 201, responseCode: 201,
product: async ( productDetails ) => postRequest( 'products', productDetails ), product: async ( productDetails ) =>
postRequest( 'products', productDetails ),
}, },
retrieve: { retrieve: {
name: 'Retrieve a product', name: 'Retrieve a product',
method: 'GET', method: 'GET',
path: 'products/<id>', path: 'products/<id>',
responseCode: 200, responseCode: 200,
product: async ( productId ) => getRequest( `products/${productId}` ), product: async ( productId ) => getRequest( `products/${ productId }` ),
}, },
listAll: { listAll: {
name: 'List all products', name: 'List all products',
method: 'GET', method: 'GET',
path: 'products', path: 'products',
responseCode: 200, responseCode: 200,
products: async ( productsQuery = {} ) => getRequest( 'products', productsQuery ), products: async ( productsQuery = {} ) =>
getRequest( 'products', productsQuery ),
}, },
update: { update: {
name: 'Update a product', name: 'Update a product',
method: 'PUT', method: 'PUT',
path: 'products/<id>', path: 'products/<id>',
responseCode: 200, responseCode: 200,
product: async ( productId, productDetails ) => putRequest( `products/${productId}`, productDetails ), product: async ( productId, productDetails ) =>
putRequest( `products/${ productId }`, productDetails ),
}, },
delete: { delete: {
name: 'Delete a product', name: 'Delete a product',
@ -44,16 +52,18 @@ const productsApi = {
path: 'products/<id>', path: 'products/<id>',
responseCode: 200, responseCode: 200,
payload: { payload: {
force: false force: false,
}, },
product: async ( productId, deletePermanently ) => deleteRequest( `products/${productId}`, deletePermanently ), product: async ( productId, deletePermanently ) =>
deleteRequest( `products/${ productId }`, deletePermanently ),
}, },
batch: { batch: {
name: 'Batch update products', name: 'Batch update products',
method: 'POST', method: 'POST',
path: 'products/batch', path: 'products/batch',
responseCode: 200, responseCode: 200,
products: async ( batchUpdatePayload ) => postRequest( `products/batch`, batchUpdatePayload ), products: async ( batchUpdatePayload ) =>
postRequest( `products/batch`, batchUpdatePayload ),
}, },
}; };

View File

@ -55,5 +55,5 @@ const refundsApi = {
}; };
module.exports = { module.exports = {
refundsApi: refundsApi, refundsApi,
}; };

View File

@ -9,7 +9,9 @@
"test:api": "jest --group=api", "test:api": "jest --group=api",
"test:hello": "jest --group=hello", "test:hello": "jest --group=hello",
"make:collection": "node utils/api-collection/build-collection.js", "make:collection": "node utils/api-collection/build-collection.js",
"report": "allure generate --clean && allure serve" "report": "allure generate --clean && allure serve",
"lint": "eslint data endpoints tests utils --ext=js,ts,tsx",
"lint:fix": "eslint data endpoints tests utils --ext=js,ts,tsx --fix"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,6 +31,10 @@
"postman-collection": "^4.1.0", "postman-collection": "^4.1.0",
"supertest": "^6.1.4" "supertest": "^6.1.4"
}, },
"devDependencies": {
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0"
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@ -1,4 +1,4 @@
const { getRequest } = require('../../utils/request'); const { getRequest } = require( '../../utils/request' );
/** /**
* Tests to verify connection to the API. * Tests to verify connection to the API.
@ -6,16 +6,14 @@ const { getRequest } = require('../../utils/request');
* @group hello * @group hello
* *
*/ */
describe('Test API connectivity', () => { describe( 'Test API connectivity', () => {
it( 'can access a non-authenticated endpoint', async () => {
it('can access a non-authenticated endpoint', async () => {
const result = await getRequest( '' ); const result = await getRequest( '' );
expect( result.statusCode ).toEqual( 200 ); expect( result.statusCode ).toEqual( 200 );
}); } );
it('can access an authenticated endpoint', async () => { it( 'can access an authenticated endpoint', async () => {
const result = await getRequest( 'system_status' ); const result = await getRequest( 'system_status' );
expect( result.statusCode ).toEqual( 200 ); expect( result.statusCode ).toEqual( 200 );
}); } );
} );
});

View File

@ -341,39 +341,36 @@ describe( 'Orders API tests', () => {
}; };
const verifyOrderPrecision = ( order, dp ) => { const verifyOrderPrecision = ( order, dp ) => {
expectPrecisionToMatch( order[ 'discount_total' ], dp ); expectPrecisionToMatch( order.discount_total, dp );
expectPrecisionToMatch( order[ 'discount_tax' ], dp ); expectPrecisionToMatch( order.discount_tax, dp );
expectPrecisionToMatch( order[ 'shipping_total' ], dp ); expectPrecisionToMatch( order.shipping_total, dp );
expectPrecisionToMatch( order[ 'shipping_tax' ], dp ); expectPrecisionToMatch( order.shipping_tax, dp );
expectPrecisionToMatch( order[ 'cart_tax' ], dp ); expectPrecisionToMatch( order.cart_tax, dp );
expectPrecisionToMatch( order[ 'total' ], dp ); expectPrecisionToMatch( order.total, dp );
expectPrecisionToMatch( order[ 'total_tax' ], dp ); expectPrecisionToMatch( order.total_tax, dp );
order[ 'line_items' ].forEach( ( lineItem ) => { order.line_items.forEach( ( lineItem ) => {
expectPrecisionToMatch( lineItem[ 'total' ], dp ); expectPrecisionToMatch( lineItem.total, dp );
expectPrecisionToMatch( lineItem[ 'total_tax' ], dp ); expectPrecisionToMatch( lineItem.total_tax, dp );
} ); } );
order[ 'tax_lines' ].forEach( ( taxLine ) => { order.tax_lines.forEach( ( taxLine ) => {
expectPrecisionToMatch( taxLine[ 'tax_total' ], dp ); expectPrecisionToMatch( taxLine.tax_total, dp );
expectPrecisionToMatch( expectPrecisionToMatch( taxLine.shipping_tax_total, dp );
taxLine[ 'shipping_tax_total' ],
dp
);
} ); } );
order[ 'shipping_lines' ].forEach( ( shippingLine ) => { order.shipping_lines.forEach( ( shippingLine ) => {
expectPrecisionToMatch( shippingLine[ 'total' ], dp ); expectPrecisionToMatch( shippingLine.total, dp );
expectPrecisionToMatch( shippingLine[ 'total_tax' ], dp ); expectPrecisionToMatch( shippingLine.total_tax, dp );
} ); } );
order[ 'fee_lines' ].forEach( ( feeLine ) => { order.fee_lines.forEach( ( feeLine ) => {
expectPrecisionToMatch( feeLine[ 'total' ], dp ); expectPrecisionToMatch( feeLine.total, dp );
expectPrecisionToMatch( feeLine[ 'total_tax' ], dp ); expectPrecisionToMatch( feeLine.total_tax, dp );
} ); } );
order[ 'refunds' ].forEach( ( refund ) => { order.refunds.forEach( ( refund ) => {
expectPrecisionToMatch( refund[ 'total' ], dp ); expectPrecisionToMatch( refund.total, dp );
} ); } );
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,7 @@
const fs = require('fs'); const fs = require( 'fs' );
const { Collection, ItemGroup, Item } = require('postman-collection'); const { Collection, ItemGroup, Item } = require( 'postman-collection' );
require('dotenv').config(); require( 'dotenv' ).config();
const { const { BASE_URL, USER_KEY, USER_SECRET, USE_INDEX_PERMALINKS } = process.env;
BASE_URL,
USER_KEY,
USER_SECRET,
USE_INDEX_PERMALINKS
} = process.env;
/** /**
* Build a Postman collection using the API testing objects. * Build a Postman collection using the API testing objects.
@ -17,10 +12,10 @@ const {
// Set up our empty collection // Set up our empty collection
if ( typeof USER_KEY === 'undefined' ) { if ( typeof USER_KEY === 'undefined' ) {
console.log('No USER_KEY was defined.'); console.log( 'No USER_KEY was defined.' );
} }
if ( typeof USER_SECRET === 'undefined' ) { if ( typeof USER_SECRET === 'undefined' ) {
console.log('No USER_SECRET was defined.'); console.log( 'No USER_SECRET was defined.' );
} }
const postmanCollection = new Collection( { const postmanCollection = new Collection( {
@ -30,74 +25,72 @@ const postmanCollection = new Collection( {
{ {
key: 'username', key: 'username',
value: USER_KEY, value: USER_KEY,
type: 'string' type: 'string',
}, },
{ {
key: 'password', key: 'password',
value: USER_SECRET, value: USER_SECRET,
type: 'string' type: 'string',
}, },
] ],
}, },
info: { info: {
name: 'WooCommerce API - v3' name: 'WooCommerce API - v3',
}, },
} ); } );
// Get the API url // Get the API url
if ( typeof BASE_URL === 'undefined' ) { if ( typeof BASE_URL === 'undefined' ) {
console.log('No BASE_URL was defined.'); console.log( 'No BASE_URL was defined.' );
} }
// Update the API path if the `USE_INDEX_PERMALINKS` flag is set // Update the API path if the `USE_INDEX_PERMALINKS` flag is set
const useIndexPermalinks = ( USE_INDEX_PERMALINKS === 'true' ); const useIndexPermalinks = USE_INDEX_PERMALINKS === 'true';
let apiPath = `${BASE_URL}/?rest_route=/wc/v3`; let apiPath = `${ BASE_URL }/?rest_route=/wc/v3`;
if ( useIndexPermalinks ) { if ( useIndexPermalinks ) {
apiPath = `${BASE_URL}/wp-json/wc/v3`; apiPath = `${ BASE_URL }/wp-json/wc/v3`;
} }
// Set this here for use in `request.js` // Set this here for use in `request.js`
global.API_PATH = `${apiPath}/`; global.API_PATH = `${ apiPath }/`;
// Add the API path has a collection variable // Add the API path has a collection variable
postmanCollection.variables.add({ postmanCollection.variables.add( {
id: 'apiBaseUrl', id: 'apiBaseUrl',
value: apiPath, value: apiPath,
type: 'string', type: 'string',
}); } );
// Get the API request data // Get the API request data
const resources = require('../../endpoints'); const resources = require( '../../endpoints' );
resourceKeys = Object.keys( resources ); resourceKeys = Object.keys( resources );
// Add the requests to folders in the collection // Add the requests to folders in the collection
for ( const key in resources ) { for ( const key in resources ) {
const folder = new ItemGroup( { const folder = new ItemGroup( {
name: resources[key].name, name: resources[ key ].name,
items: [] items: [],
} ); } );
for ( const endpoint in resources[key] ) { for ( const endpoint in resources[ key ] ) {
const api = resources[ key ][ endpoint ];
let api = resources[key][endpoint];
// If there is no name defined, continue // If there is no name defined, continue
if ( !api.name ) { if ( ! api.name ) {
continue; continue;
} }
const request = new Item( { const request = new Item( {
name: api.name, name: api.name,
request: { request: {
url: `{{apiBaseUrl}}/${api.path}`, url: `{{apiBaseUrl}}/${ api.path }`,
method: api.method, method: api.method,
body: { body: {
mode: 'raw', mode: 'raw',
raw: JSON.stringify( api.payload ), raw: JSON.stringify( api.payload ),
options: { options: {
raw: { language: 'json' } raw: { language: 'json' },
} },
}, },
}, },
} ); } );
folder.items.add( request ); folder.items.add( request );
@ -110,9 +103,13 @@ for ( const key in resources ) {
const collectionJSON = postmanCollection.toJSON(); const collectionJSON = postmanCollection.toJSON();
// Create a colleciton.json file. It can be imported to postman // Create a colleciton.json file. It can be imported to postman
fs.writeFile('./collection.json', JSON.stringify( collectionJSON ), ( err ) => { fs.writeFile(
if ( err ) { './collection.json',
console.log( err ); JSON.stringify( collectionJSON ),
( err ) => {
if ( err ) {
console.log( err );
}
console.log( 'File saved!' );
} }
console.log('File saved!'); );
});

View File

@ -1,13 +1,13 @@
require('dotenv').config(); require( 'dotenv' ).config();
const { USER_KEY, USER_SECRET } = process.env; const { USER_KEY, USER_SECRET } = process.env;
const request = require('supertest')( API_PATH ); const request = require( 'supertest' )( API_PATH );
/** /**
* Make a GET request. * Make a GET request.
* *
* @param {string} requestPath The path of the request. * @param {string} requestPath The path of the request.
* @param {object} queryString Optional. An object of one or more `key: value` query string parameters. * @param {Object} queryString Optional. An object of one or more `key: value` query string parameters.
* @returns {Response} * @return {Response}
*/ */
const getRequest = async ( requestPath, queryString = {} ) => { const getRequest = async ( requestPath, queryString = {} ) => {
const response = await request const response = await request
@ -22,8 +22,8 @@ const getRequest = async ( requestPath, queryString = {} ) => {
* Make a POST request. * Make a POST request.
* *
* @param {string} requestPath The path of the request. * @param {string} requestPath The path of the request.
* @param {object} requestBody The body of the request to submit. * @param {Object} requestBody The body of the request to submit.
* @returns {Response} * @return {Response}
*/ */
const postRequest = async ( requestPath, requestBody ) => { const postRequest = async ( requestPath, requestBody ) => {
const response = await request const response = await request
@ -38,8 +38,8 @@ const postRequest = async ( requestPath, requestBody ) => {
* Make a PUT request. * Make a PUT request.
* *
* @param {string} requestPath The path of the request. * @param {string} requestPath The path of the request.
* @param {object} requestBody The body of the request to submit. * @param {Object} requestBody The body of the request to submit.
* @returns {Request} * @return {Request}
*/ */
const putRequest = async ( requestPath, requestBody ) => { const putRequest = async ( requestPath, requestBody ) => {
const response = await request const response = await request
@ -53,12 +53,12 @@ const putRequest = async ( requestPath, requestBody ) => {
/** /**
* Make a DELETE request, optionally deleting the resource permanently. * Make a DELETE request, optionally deleting the resource permanently.
* *
* @param {string} requestPath The path of the request. * @param {string} requestPath The path of the request.
* @param {boolean} deletePermanently Flag to permanently delete the resource. * @param {boolean} deletePermanently Flag to permanently delete the resource.
* @returns {Response} * @return {Response}
*/ */
const deleteRequest = async ( requestPath, deletePermanently = false ) => { const deleteRequest = async ( requestPath, deletePermanently = false ) => {
const requestBody = deletePermanently ? { force: true } : {} const requestBody = deletePermanently ? { force: true } : {};
const response = await request const response = await request
.delete( requestPath ) .delete( requestPath )
.set( 'Accept', 'application/json' ) .set( 'Accept', 'application/json' )
@ -67,4 +67,4 @@ const deleteRequest = async ( requestPath, deletePermanently = false ) => {
return response; return response;
}; };
module.exports = { getRequest, postRequest, putRequest, deleteRequest } module.exports = { getRequest, postRequest, putRequest, deleteRequest };

View File

@ -1,4 +1,3 @@
module.exports = { module.exports = {
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ], extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
root: true,
}; };

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Standardize linting: Ensure e2e packages are lintable

View File

@ -1,26 +1,37 @@
{ {
"name": "@woocommerce/e2e-builds", "name": "@woocommerce/e2e-builds",
"version": "0.1.0", "version": "0.1.0",
"description": "Utility build files for e2e packages", "description": "Utility build files for e2e packages",
"private": "true", "private": "true",
"main": "build.js", "main": "build.js",
"bin": { "bin": {
"e2e-builds": "./build.js" "e2e-builds": "./build.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/woocommerce/woocommerce.git" "url": "git+https://github.com/woocommerce/woocommerce.git"
}, },
"license": "GPL-3.0+", "license": "GPL-3.0+",
"bugs": { "bugs": {
"url": "https://github.com/woocommerce/woocommerce/issues" "url": "https://github.com/woocommerce/woocommerce/issues"
}, },
"homepage": "https://github.com/woocommerce/woocommerce#readme", "homepage": "https://github.com/woocommerce/woocommerce#readme",
"devDependencies": { "scripts": {
"@babel/core": "7.12.9", "lint": "eslint build.js",
"chalk": "^4.1.2", "lint:fix": "eslint build.js --fix"
"glob": "^7.2.0", },
"mkdirp": "^1.0.4", "devDependencies": {
"lodash": "^4.17.21" "@babel/core": "7.12.9",
"@woocommerce/eslint-plugin": "workspace:*",
"chalk": "^4.1.2",
"eslint": "^8.12.0",
"glob": "^7.2.0",
"mkdirp": "^1.0.4",
"lodash": "^4.17.21"
},
"lint-staged": {
"*.(t|j)s?(x)": [
"pnpm lint:fix"
]
} }
} }

View File

@ -0,0 +1,3 @@
module.exports = {
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
};

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Standardize linting: Ensure e2e packages are lintable

View File

@ -24,18 +24,20 @@
"config": "3.3.3" "config": "3.3.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "7.12.8", "@babel/cli": "7.12.8",
"@babel/core": "7.12.9", "@babel/core": "7.12.9",
"@babel/plugin-proposal-async-generator-functions": "^7.16.4", "@babel/plugin-proposal-async-generator-functions": "^7.16.4",
"@babel/plugin-proposal-object-rest-spread": "^7.16.0", "@babel/plugin-proposal-object-rest-spread": "^7.16.0",
"@babel/plugin-transform-react-jsx": "^7.16.0", "@babel/plugin-transform-react-jsx": "^7.16.0",
"@babel/plugin-transform-runtime": "^7.16.4", "@babel/plugin-transform-runtime": "^7.16.4",
"@babel/polyfill": "7.12.1", "@babel/polyfill": "7.12.1",
"@babel/preset-env": "7.12.7", "@babel/preset-env": "7.12.7",
"@woocommerce/e2e-builds": "workspace:*", "@woocommerce/e2e-builds": "workspace:*",
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3", "@woocommerce/eslint-plugin": "workspace:*",
"@wordpress/babel-preset-default": "3.0.2", "@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/browserslist-config": "^4.1.0" "@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0",
"eslint": "^8.12.0"
}, },
"peerDependencies": { "peerDependencies": {
"@woocommerce/api": "^0.2.0", "@woocommerce/api": "^0.2.0",
@ -46,14 +48,16 @@
}, },
"scripts": { "scripts": {
"postinstall": "composer install", "postinstall": "composer install",
"prepack": "pnpm run build", "prepare": "pnpm run build",
"clean": "rm -rf ./build ./build-module", "clean": "rm -rf ./build ./build-module",
"compile": "e2e-builds", "compile": "e2e-builds",
"build": "./bin/build.sh && pnpm run clean && pnpm run compile" "build": "./bin/build.sh && pnpm run clean && pnpm run compile",
"lint": "eslint src --ext=js,ts,tsx",
"lint:fix": "eslint src --ext=js,ts,tsx --fix"
}, },
"lint-staged": { "lint-staged": {
"*.(t|j)s?(x)": [ "*.(t|j)s?(x)": [
"eslint --fix" "pnpm lint:fix"
] ]
} }
} }

View File

@ -6,35 +6,35 @@ const { merchant } = require( '@woocommerce/e2e-utils' );
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
import deprecated from '@wordpress/deprecated'; import deprecated from '@wordpress/deprecated';
const runActivationTest = () => { const runActivationTest = () => {
describe('Store owner can login and make sure WooCommerce is activated', () => { describe( 'Store owner can login and make sure WooCommerce is activated', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can make sure WooCommerce is activated. If not, activate it', async () => { it( 'can make sure WooCommerce is activated. If not, activate it', async () => {
deprecated( 'runActivationTest', { deprecated( 'runActivationTest', {
alternative: '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`', alternative:
}); '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`',
} );
const slug = 'woocommerce'; const slug = 'woocommerce';
await merchant.openPlugins(); await merchant.openPlugins();
const disableLink = await page.$(`tr[data-slug="${slug}"] .deactivate a`); const disableLink = await page.$(
if (disableLink) { `tr[data-slug="${ slug }"] .deactivate a`
);
if ( disableLink ) {
return; return;
} }
await page.click(`tr[data-slug="${slug}"] .activate a`); await page.click( `tr[data-slug="${ slug }"] .activate a` );
await page.waitForSelector(`tr[data-slug="${slug}"] .deactivate a`); await page.waitForSelector(
}); `tr[data-slug="${ slug }"] .deactivate a`
);
}); } );
} );
}; };
module.exports = runActivationTest; module.exports = runActivationTest;

View File

@ -14,84 +14,91 @@ const {
*/ */
const config = require( 'config' ); const config = require( 'config' );
import deprecated from '@wordpress/deprecated'; import deprecated from '@wordpress/deprecated';
const { const { it, describe } = require( '@jest/globals' );
it,
describe,
} = require( '@jest/globals' );
const shippingZoneNameUS = config.get( 'addresses.customer.shipping.country' ); const shippingZoneNameUS = config.get( 'addresses.customer.shipping.country' );
const runOnboardingFlowTest = () => { const runOnboardingFlowTest = () => {
describe('Store owner can go through store Onboarding', () => { describe( 'Store owner can go through store Onboarding', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
if ( IS_RETEST_MODE ) { if ( IS_RETEST_MODE ) {
it('can reset onboarding to default settings', async () => { it( 'can reset onboarding to default settings', async () => {
await withRestApi.resetOnboarding(); await withRestApi.resetOnboarding();
}); } );
it('can reset shipping zones to default settings', async () => { it( 'can reset shipping zones to default settings', async () => {
await withRestApi.deleteAllShippingZones(); await withRestApi.deleteAllShippingZones();
}); } );
it('can reset shipping classes', async () => { it( 'can reset shipping classes', async () => {
await withRestApi.deleteAllShippingClasses(); await withRestApi.deleteAllShippingClasses();
}) } );
it('can reset to default settings', async () => { it( 'can reset to default settings', async () => {
await withRestApi.resetSettingsGroupToDefault('general'); await withRestApi.resetSettingsGroupToDefault( 'general' );
await withRestApi.resetSettingsGroupToDefault('products'); await withRestApi.resetSettingsGroupToDefault( 'products' );
await withRestApi.resetSettingsGroupToDefault('tax'); await withRestApi.resetSettingsGroupToDefault( 'tax' );
}); } );
} }
it('can start and complete onboarding when visiting the site for the first time.', async () => { it( 'can start and complete onboarding when visiting the site for the first time.', async () => {
deprecated( 'runOnboardingFlowTest', { deprecated( 'runOnboardingFlowTest', {
alternative: '@woocommerce/admin-e2e-tests `testAdminOnboardingWizard()`', alternative:
}); '@woocommerce/admin-e2e-tests `testAdminOnboardingWizard()`',
} );
await completeOnboardingWizard(); await completeOnboardingWizard();
}); } );
}); } );
}; };
const runTaskListTest = () => { const runTaskListTest = () => {
describe('Store owner can go through setup Task List', () => { describe( 'Store owner can go through setup Task List', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can setup shipping', async () => { it( 'can setup shipping', async () => {
deprecated( 'runTaskListTest', { deprecated( 'runTaskListTest', {
alternative: '@woocommerce/admin-e2e-tests `testAdminHomescreenTasklist()`', alternative:
}); '@woocommerce/admin-e2e-tests `testAdminHomescreenTasklist()`',
await page.evaluate(() => { } );
document.querySelector('.woocommerce-list__item-title').scrollIntoView(); await page.evaluate( () => {
}); document
.querySelector( '.woocommerce-list__item-title' )
.scrollIntoView();
} );
// Query for all tasks on the list // Query for all tasks on the list
const taskListItems = await page.$$('.woocommerce-list__item-title'); const taskListItems = await page.$$(
expect(taskListItems.length).toBeInRange( 5, 6 ); '.woocommerce-list__item-title'
);
expect( taskListItems.length ).toBeInRange( 5, 6 );
// Work around for https://github.com/woocommerce/woocommerce-admin/issues/6761 // Work around for https://github.com/woocommerce/woocommerce-admin/issues/6761
if ( taskListItems.length == 6 ) { if ( taskListItems.length == 6 ) {
// Click on "Set up shipping" task to move to the next step // Click on "Set up shipping" task to move to the next step
const [ setupTaskListItem ] = await page.$x( '//div[contains(text(),"Set up shipping")]' ); const [ setupTaskListItem ] = await page.$x(
'//div[contains(text(),"Set up shipping")]'
);
await setupTaskListItem.click(); await setupTaskListItem.click();
// Wait for "Proceed" button to become active // Wait for "Proceed" button to become active
await page.waitForSelector('button.is-primary:not(:disabled)'); await page.waitForSelector(
await page.waitFor(3000); 'button.is-primary:not(:disabled)'
);
await page.waitFor( 3000 );
// Click on "Proceed" button to save shipping settings // Click on "Proceed" button to save shipping settings
await page.click('button.is-primary'); await page.click( 'button.is-primary' );
await page.waitFor(3000); await page.waitFor( 3000 );
} else { } else {
await merchant.openNewShipping(); await merchant.openNewShipping();
await addShippingZoneAndMethod(shippingZoneNameUS); await addShippingZoneAndMethod( shippingZoneNameUS );
} }
}); } );
}); } );
}; };
module.exports = { module.exports = {

View File

@ -2,10 +2,7 @@
* External dependencies * External dependencies
*/ */
const { HTTPClientFactory } = require( '@woocommerce/api' ); const { HTTPClientFactory } = require( '@woocommerce/api' );
const { const { it, describe } = require( '@jest/globals' );
it,
describe,
} = require( '@jest/globals' );
import deprecated from '@wordpress/deprecated'; import deprecated from '@wordpress/deprecated';
/** /**
@ -17,64 +14,85 @@ const {
setCheckbox, setCheckbox,
settingsPageSaveChanges, settingsPageSaveChanges,
verifyCheckboxIsSet, verifyCheckboxIsSet,
verifyValueOfInputField verifyValueOfInputField,
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const { const {
getTestConfig, getTestConfig,
waitAndClick waitAndClick,
} = require( '@woocommerce/e2e-environment' ); } = require( '@woocommerce/e2e-environment' );
const runInitialStoreSettingsTest = () => { const runInitialStoreSettingsTest = () => {
describe('Store owner can finish initial store setup', () => { describe( 'Store owner can finish initial store setup', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can enable tax rates and calculations', async () => { it( 'can enable tax rates and calculations', async () => {
deprecated( 'runInitialStoreSettingsTest', { deprecated( 'runInitialStoreSettingsTest', {
alternative: '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`', alternative:
}); '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`',
} );
// Go to general settings page // Go to general settings page
await merchant.openSettings('general'); await merchant.openSettings( 'general' );
// Make sure the general tab is active // Make sure the general tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'General'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'General',
} );
// Enable tax rates and calculations // Enable tax rates and calculations
await setCheckbox('#woocommerce_calc_taxes'); await setCheckbox( '#woocommerce_calc_taxes' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
verifyCheckboxIsSet('#woocommerce_calc_taxes'), text: 'Your settings have been saved.',
]); } ),
}); verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
} );
it('can configure permalink settings', async () => { it( 'can configure permalink settings', async () => {
// Go to Permalink Settings page // Go to Permalink Settings page
await merchant.openPermalinkSettings(); await merchant.openPermalinkSettings();
// Select "Post name" option in common settings section // Select "Post name" option in common settings section
await page.click('input[value="/%postname%/"]', {text: ' Post name'}); await page.click( 'input[value="/%postname%/"]', {
text: ' Post name',
} );
// Select "Custom base" in product permalinks section // Select "Custom base" in product permalinks section
await waitAndClick( page, '#woocommerce_custom_selection' ); await waitAndClick( page, '#woocommerce_custom_selection' );
// Fill custom base slug to use // Fill custom base slug to use
await expect(page).toFill('#woocommerce_permalink_structure', '/product/'); await expect( page ).toFill(
'#woocommerce_permalink_structure',
'/product/'
);
await permalinkSettingsPageSaveChanges(); await permalinkSettingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#setting-error-settings_updated', {text: 'Permalink structure updated.'}), expect( page ).toMatchElement(
verifyValueOfInputField('#permalink_structure', '/%postname%/'), '#setting-error-settings_updated',
verifyValueOfInputField('#woocommerce_permalink_structure', '/product/'), {
]); text: 'Permalink structure updated.',
}); }
),
verifyValueOfInputField(
'#permalink_structure',
'/%postname%/'
),
verifyValueOfInputField(
'#woocommerce_permalink_structure',
'/product/'
),
] );
} );
it( 'can use api with pretty permalinks', async () => { it( 'can use api with pretty permalinks', async () => {
const testConfig = getTestConfig(); const testConfig = getTestConfig();
@ -86,8 +104,8 @@ const runInitialStoreSettingsTest = () => {
const response = await client.get( '/wc/v3/products' ); const response = await client.get( '/wc/v3/products' );
expect( response.status ).toBe( 200 ); expect( response.status ).toBe( 200 );
}); } );
}); } );
}; };
module.exports = runInitialStoreSettingsTest; module.exports = runInitialStoreSettingsTest;

View File

@ -2,59 +2,64 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { HTTPClientFactory, VariableProduct, ProductVariation } = require( '@woocommerce/api' ); const {
HTTPClientFactory,
VariableProduct,
ProductVariation,
} = require( '@woocommerce/api' );
/** /**
* External dependencies * External dependencies
*/ */
const config = require( 'config' ); const config = require( 'config' );
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
/** /**
* Create a variable product and retrieve via the API. * Create a variable product and retrieve via the API.
*/ */
const runVariableProductAPITest = () => { const runVariableProductAPITest = () => {
describe('REST API > Variable Product', () => { describe( 'REST API > Variable Product', () => {
let client; let client;
let defaultVariableProduct; let defaultVariableProduct;
let defaultVariations; let defaultVariations;
let baseVariableProduct; let baseVariableProduct;
let product; let product;
let variations = []; const variations = [];
let productRepository; let productRepository;
let variationRepository; let variationRepository;
beforeAll(async () => { beforeAll( async () => {
defaultVariableProduct = config.get('products.variable'); defaultVariableProduct = config.get( 'products.variable' );
defaultVariations = config.get('products.variations'); defaultVariations = config.get( 'products.variations' );
const admin = config.get('users.admin'); const admin = config.get( 'users.admin' );
const url = config.get('url'); const url = config.get( 'url' );
client = HTTPClientFactory.build(url) client = HTTPClientFactory.build( url )
.withBasicAuth(admin.username, admin.password) .withBasicAuth( admin.username, admin.password )
.withIndexPermalinks() .withIndexPermalinks()
.create(); .create();
}); } );
it('can create a variable product', async () => { it( 'can create a variable product', async () => {
productRepository = VariableProduct.restRepository(client); productRepository = VariableProduct.restRepository( client );
// Check properties of product in the create product response. // Check properties of product in the create product response.
product = await productRepository.create(defaultVariableProduct); product = await productRepository.create( defaultVariableProduct );
expect(product).toEqual(expect.objectContaining(defaultVariableProduct)); expect( product ).toEqual(
}); expect.objectContaining( defaultVariableProduct )
);
} );
it('can add variations', async () => { it( 'can add variations', async () => {
variationRepository = ProductVariation.restRepository(client); variationRepository = ProductVariation.restRepository( client );
for (let v = 0; v < defaultVariations.length; v++) { for ( let v = 0; v < defaultVariations.length; v++ ) {
const variation = await variationRepository.create(product.id, defaultVariations[v]); const variation = await variationRepository.create(
product.id,
defaultVariations[ v ]
);
// Test that variation id is a number. // Test that variation id is a number.
expect(variation.id).toBeGreaterThan(0); expect( variation.id ).toBeGreaterThan( 0 );
variations.push(variation.id); variations.push( variation.id );
} }
baseVariableProduct = { baseVariableProduct = {
@ -62,31 +67,36 @@ const runVariableProductAPITest = () => {
...defaultVariableProduct, ...defaultVariableProduct,
variations, variations,
}; };
}); } );
it('can retrieve a transformed variable product', async () => { it( 'can retrieve a transformed variable product', async () => {
// Read product via the repository. // Read product via the repository.
const transformed = await productRepository.read(product.id); const transformed = await productRepository.read( product.id );
expect(transformed).toEqual(expect.objectContaining(baseVariableProduct)); expect( transformed ).toEqual(
}); expect.objectContaining( baseVariableProduct )
);
} );
it('can retrieve transformed product variations', async () => { it( 'can retrieve transformed product variations', async () => {
// Read variations via the repository. // Read variations via the repository.
const transformed = await variationRepository.list(product.id); const transformed = await variationRepository.list( product.id );
expect(transformed).toHaveLength(defaultVariations.length); expect( transformed ).toHaveLength( defaultVariations.length );
}); } );
it('can delete a variation', async () => { it( 'can delete a variation', async () => {
const variationId = baseVariableProduct.variations.pop(); const variationId = baseVariableProduct.variations.pop();
const status = variationRepository.delete(product.id, variationId); const status = variationRepository.delete(
expect(status).toBeTruthy(); product.id,
}); variationId
);
expect( status ).toBeTruthy();
} );
it('can delete a variable product', async () => { it( 'can delete a variable product', async () => {
const status = productRepository.delete(product.id); const status = productRepository.delete( product.id );
expect(status).toBeTruthy(); expect( status ).toBeTruthy();
}); } );
}); } );
} };
module.exports = runVariableProductAPITest; module.exports = runVariableProductAPITest;

View File

@ -5,14 +5,17 @@
// Setup and onboarding tests // Setup and onboarding tests
const runActivationTest = require( './activate-and-setup/activate.test' ); const runActivationTest = require( './activate-and-setup/activate.test' );
const { runOnboardingFlowTest, runTaskListTest } = require( './activate-and-setup/onboarding-tasklist.test' ); const {
runOnboardingFlowTest,
runTaskListTest,
} = require( './activate-and-setup/onboarding-tasklist.test' );
const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' ); const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' );
// Shopper tests // Shopper tests
const runProductBrowseSearchSortTest = require( './shopper/front-end-product-browse-search-sort.test' ); const runProductBrowseSearchSortTest = require( './shopper/front-end-product-browse-search-sort.test' );
const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test'); const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test' );
const runCartPageTest = require( './shopper/front-end-cart.test' ); const runCartPageTest = require( './shopper/front-end-cart.test' );
const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test'); const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test' );
const runCheckoutPageTest = require( './shopper/front-end-checkout.test' ); const runCheckoutPageTest = require( './shopper/front-end-checkout.test' );
const runMyAccountPageTest = require( './shopper/front-end-my-account.test' ); const runMyAccountPageTest = require( './shopper/front-end-my-account.test' );
const runMyAccountPayOrderTest = require( './shopper/front-end-my-account-pay-order.test' ); const runMyAccountPayOrderTest = require( './shopper/front-end-my-account-pay-order.test' );
@ -26,12 +29,15 @@ const runCartRedirectionTest = require( './shopper/front-end-cart-redirection.te
const runOrderEmailReceivingTest = require( './shopper/front-end-order-email-receiving.test' ); const runOrderEmailReceivingTest = require( './shopper/front-end-order-email-receiving.test' );
// Merchant tests // Merchant tests
const runAddNewShippingZoneTest = require ( './merchant/wp-admin-settings-shipping-zones.test' ); const runAddNewShippingZoneTest = require( './merchant/wp-admin-settings-shipping-zones.test' );
const runAddShippingClassesTest = require('./merchant/wp-admin-settings-shipping-classes.test') const runAddShippingClassesTest = require( './merchant/wp-admin-settings-shipping-classes.test' );
const runCreateCouponTest = require( './merchant/wp-admin-coupon-new.test' ); const runCreateCouponTest = require( './merchant/wp-admin-coupon-new.test' );
const runCreateOrderTest = require( './merchant/wp-admin-order-new.test' ); const runCreateOrderTest = require( './merchant/wp-admin-order-new.test' );
const runEditOrderTest = require( './merchant/wp-admin-order-edit.test' ); const runEditOrderTest = require( './merchant/wp-admin-order-edit.test' );
const { runAddSimpleProductTest, runAddVariableProductTest } = require( './merchant/wp-admin-product-new.test' ); const {
runAddSimpleProductTest,
runAddVariableProductTest,
} = require( './merchant/wp-admin-product-new.test' );
const runUpdateGeneralSettingsTest = require( './merchant/wp-admin-settings-general.test' ); const runUpdateGeneralSettingsTest = require( './merchant/wp-admin-settings-general.test' );
const runProductSettingsTest = require( './merchant/wp-admin-settings-product.test' ); const runProductSettingsTest = require( './merchant/wp-admin-settings-product.test' );
const runTaxSettingsTest = require( './merchant/wp-admin-settings-tax.test' ); const runTaxSettingsTest = require( './merchant/wp-admin-settings-tax.test' );
@ -105,7 +111,7 @@ const runMerchantTests = () => {
runAnalyticsPageLoadsTest(); runAnalyticsPageLoadsTest();
runInitiateWccomConnectionTest(); runInitiateWccomConnectionTest();
runAdminPageLoadTests(); runAdminPageLoadTests();
} };
const runApiTests = () => { const runApiTests = () => {
runExternalProductAPITest(); runExternalProductAPITest();
@ -114,7 +120,7 @@ const runApiTests = () => {
runCouponApiTest(); runCouponApiTest();
runOrderApiTest(); runOrderApiTest();
runTelemetryAPITest(); runTelemetryAPITest();
} };
module.exports = { module.exports = {
runActivationTest, runActivationTest,

View File

@ -2,7 +2,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
merchant, merchant,
waitForSelectorWithoutThrow, waitForSelectorWithoutThrow,
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
@ -10,67 +10,67 @@
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
import deprecated from '@wordpress/deprecated'; import deprecated from '@wordpress/deprecated';
/** /**
* Quick check for page title and no data message. * Quick check for page title and no data message.
* *
* @param pageTitle Page title in H1. * @param pageTitle Page title in H1.
* @param element Defaults to '.d3-chart__empty-message' * @param element Defaults to '.d3-chart__empty-message'
* @param elementText Defaults to 'No data for the selected date range' * @param elementText Defaults to 'No data for the selected date range'
*/ */
const checkHeadingAndElement = async ( const checkHeadingAndElement = async (
pageTitle, element = '.d3-chart__empty-message', elementText = 'No data for the selected date range') => { pageTitle,
await expect(page).toMatchElement('h1', {text: pageTitle}); element = '.d3-chart__empty-message',
elementText = 'No data for the selected date range'
) => {
await expect( page ).toMatchElement( 'h1', { text: pageTitle } );
// Depending on order of tests the chart may not be empty. // Depending on order of tests the chart may not be empty.
const found = await waitForSelectorWithoutThrow( element ); const found = await waitForSelectorWithoutThrow( element );
if ( found ) { if ( found ) {
await expect(page).toMatchElement(element, {text: elementText}); await expect( page ).toMatchElement( element, { text: elementText } );
} else { } else {
await expect(page).toMatchElement( '.woocommerce-chart' ); await expect( page ).toMatchElement( '.woocommerce-chart' );
} }
}; };
// Analytics pages that we'll test against // Analytics pages that we'll test against
const pages = [ const pages = [
['Overview'], [ 'Overview' ],
['Products'], [ 'Products' ],
['Revenue'], [ 'Revenue' ],
['Orders'], [ 'Orders' ],
['Variations'], [ 'Variations' ],
['Categories'], [ 'Categories' ],
['Coupons'], [ 'Coupons' ],
['Taxes'], [ 'Taxes' ],
['Downloads'], [ 'Downloads' ],
['Stock', '.components-button > span', 'Product / Variation'], [ 'Stock', '.components-button > span', 'Product / Variation' ],
['Settings', 'h2', 'Analytics Settings'] [ 'Settings', 'h2', 'Analytics Settings' ],
]; ];
const runAnalyticsPageLoadsTest = () => { const runAnalyticsPageLoadsTest = () => {
describe('Analytics > Opening Top Level Pages', () => { describe( 'Analytics > Opening Top Level Pages', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
deprecated( 'runAnalyticsPageLoadsTest', { deprecated( 'runAnalyticsPageLoadsTest', {
alternative: '@woocommerce/admin-e2e-tests `testAdminAnalyticsPages()`', alternative:
}); '@woocommerce/admin-e2e-tests `testAdminAnalyticsPages()`',
} );
it.each(pages)( it.each( pages )(
'can see %s page properly', 'can see %s page properly',
async (pageTitle, element, elementText) => { async ( pageTitle, element, elementText ) => {
// Go to the desired page and verify it // Go to the desired page and verify it
await merchant.openAnalyticsPage(pageTitle.toLowerCase()); await merchant.openAnalyticsPage( pageTitle.toLowerCase() );
await checkHeadingAndElement(pageTitle, element, elementText); await checkHeadingAndElement( pageTitle, element, elementText );
} }
); );
}); } );
} };
module.exports = runAnalyticsPageLoadsTest; module.exports = runAnalyticsPageLoadsTest;

View File

@ -11,48 +11,53 @@ const {
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const runCreateCouponTest = () => { const runCreateCouponTest = () => {
describe('Add New Coupon Page', () => { describe( 'Add New Coupon Page', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can create new coupon', async () => { it( 'can create new coupon', async () => {
// Go to "add coupon" page // Go to "add coupon" page
await merchant.openNewCoupon(); await merchant.openNewCoupon();
// Make sure we're on the add coupon page // Make sure we're on the add coupon page
await expect(page.title()).resolves.toMatch('Add new coupon'); await expect( page.title() ).resolves.toMatch( 'Add new coupon' );
// Fill in coupon code and description // Fill in coupon code and description
await expect(page).toFill('#title', 'code-' + new Date().getTime().toString()); await expect( page ).toFill(
await expect(page).toFill('#woocommerce-coupon-description', 'test coupon'); '#title',
'code-' + new Date().getTime().toString()
);
await expect( page ).toFill(
'#woocommerce-coupon-description',
'test coupon'
);
// Set general coupon data // Set general coupon data
await clickTab('General'); await clickTab( 'General' );
await expect(page).toSelect('#discount_type', 'Fixed cart discount'); await expect( page ).toSelect(
await expect(page).toFill('#coupon_amount', '100'); '#discount_type',
'Fixed cart discount'
);
await expect( page ).toFill( '#coupon_amount', '100' );
// Publish coupon, verify that it was published. // Publish coupon, verify that it was published.
const adminEdit = new AdminEdit(); const adminEdit = new AdminEdit();
await adminEdit.verifyPublish( await adminEdit.verifyPublish(
'#publish', '#publish',
'.notice', '.notice',
'Coupon updated.', 'Coupon updated.'
); );
// Delete the coupon // Delete the coupon
const couponId = await adminEdit.getId(); const couponId = await adminEdit.getId();
if ( couponId ) { if ( couponId ) {
await withRestApi.deleteCoupon( couponId ); await withRestApi.deleteCoupon( couponId );
} }
}); } );
}); } );
} };
module.exports = runCreateCouponTest; module.exports = runCreateCouponTest;

View File

@ -1,46 +1,44 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const { merchant } = require( '@woocommerce/e2e-utils' );
merchant,
} = require( '@woocommerce/e2e-utils' );
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const runInitiateWccomConnectionTest = () => { const runInitiateWccomConnectionTest = () => {
describe('Merchant > Initiate WCCOM Connection', () => { describe( 'Merchant > Initiate WCCOM Connection', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it.skip('can initiate WCCOM connection', async () => { it.skip( 'can initiate WCCOM connection', async () => {
await merchant.openExtensions(); await merchant.openExtensions();
// Click on a tab to choose WooCommerce Subscriptions extension // Click on a tab to choose WooCommerce Subscriptions extension
await Promise.all( [ await Promise.all( [
expect( page ).toClick( 'a.nav-tab', { text: "WooCommerce.com Subscriptions" } ), expect( page ).toClick( 'a.nav-tab', {
text: 'WooCommerce.com Subscriptions',
} ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] ); ] );
// Click on Connect button to initiate a WCCOM connection // Click on Connect button to initiate a WCCOM connection
await Promise.all([ await Promise.all( [
expect(page).toClick('.button-helper-connect'), expect( page ).toClick( '.button-helper-connect' ),
page.waitForNavigation({waitUntil: 'networkidle0'}), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify that you see a login page for connecting WCCOM account // Verify that you see a login page for connecting WCCOM account
await expect(page).toMatchElement('div.login'); await expect( page ).toMatchElement( 'div.login' );
await expect(page).toMatchElement('input#usernameOrEmail'); await expect( page ).toMatchElement( 'input#usernameOrEmail' );
await expect(page).toMatchElement('button.button', {text: "Continue"}); await expect( page ).toMatchElement( 'button.button', {
}); text: 'Continue',
}); } );
} } );
} );
};
module.exports = runInitiateWccomConnectionTest; module.exports = runInitiateWccomConnectionTest;

View File

@ -12,55 +12,79 @@ const {
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
const discountedPrice = simpleProductPrice - 5.00; ? config.get( 'products.simple.price' )
: '9.99';
const discountedPrice = simpleProductPrice - 5.0;
const couponDialogMessage = 'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.'; const couponDialogMessage =
'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.';
let couponCode; let couponCode;
let orderId; let orderId;
let productId; let productId;
const runOrderApplyCouponTest = () => { const runOrderApplyCouponTest = () => {
describe('WooCommerce Orders > Apply coupon', () => { describe( 'WooCommerce Orders > Apply coupon', () => {
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
couponCode = await createCoupon(); couponCode = await createCoupon();
orderId = await createOrder( { productId, status: 'pending' } ); orderId = await createOrder( { productId, status: 'pending' } );
await merchant.login(); await merchant.login();
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
await page.removeAllListeners('dialog'); await page.removeAllListeners( 'dialog' );
// Make sure the simple product price is greater than the coupon amount // Make sure the simple product price is greater than the coupon amount
await expect(Number(simpleProductPrice)).toBeGreaterThan(5.00); await expect( Number( simpleProductPrice ) ).toBeGreaterThan( 5.0 );
} ); } );
it('can apply a coupon', async () => { it( 'can apply a coupon', async () => {
await page.waitForSelector('button.add-coupon'); await page.waitForSelector( 'button.add-coupon' );
const couponDialog = await expect(page).toDisplayDialog(async () => { const couponDialog = await expect( page ).toDisplayDialog(
await evalAndClick('button.add-coupon'); async () => {
}); await evalAndClick( 'button.add-coupon' );
expect(couponDialog.message()).toMatch(couponDialogMessage); }
);
expect( couponDialog.message() ).toMatch( couponDialogMessage );
// Accept the dialog with the coupon code // Accept the dialog with the coupon code
await couponDialog.accept(couponCode); await couponDialog.accept( couponCode );
await uiUnblocked(); await uiUnblocked();
// Verify the coupon list is showing // Verify the coupon list is showing
await page.waitForSelector('.wc-used-coupons'); await page.waitForSelector( '.wc-used-coupons' );
await expect(page).toMatchElement('.wc_coupon_list', { text: 'Coupon(s)' }); await expect( page ).toMatchElement( '.wc_coupon_list', {
await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); text: 'Coupon(s)',
} );
await expect( page ).toMatchElement(
'.wc_coupon_list li.code.editable',
{
text: couponCode.toLowerCase(),
}
);
// Check that the coupon has been applied // Check that the coupon has been applied
await expect(page).toMatchElement('.wc-order-item-discount', { text: '5.00' }); await expect( page ).toMatchElement( '.wc-order-item-discount', {
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: discountedPrice }); text: '5.00',
}); } );
await expect(
page
).toMatchElement(
'.line_cost > .view > .woocommerce-Price-amount',
{ text: discountedPrice }
);
} );
it('can remove a coupon', async () => { it( 'can remove a coupon', async () => {
// Make sure we have a coupon on the page to use // Make sure we have a coupon on the page to use
await page.waitForSelector('.wc-used-coupons'); await page.waitForSelector( '.wc-used-coupons' );
await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); await expect( page ).toMatchElement(
'.wc_coupon_list li.code.editable',
{
text: couponCode.toLowerCase(),
}
);
await evalAndClick( 'a.remove-coupon' ); await evalAndClick( 'a.remove-coupon' );
await uiUnblocked(); await uiUnblocked();
@ -68,16 +92,32 @@ const runOrderApplyCouponTest = () => {
await utils.waitForTimeout( 2000 ); // to avoid flakyness await utils.waitForTimeout( 2000 ); // to avoid flakyness
// Verify the coupon pricing has been removed // Verify the coupon pricing has been removed
await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); await expect( page ).not.toMatchElement(
await expect(page).not.toMatchElement('.wc-order-item-discount', { text: '5.00' }); '.wc_coupon_list li.code.editable',
await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: discountedPrice }); {
text: couponCode.toLowerCase(),
}
);
await expect( page ).not.toMatchElement(
'.wc-order-item-discount',
{ text: '5.00' }
);
await expect(
page
).not.toMatchElement(
'.line-cost .view .woocommerce-Price-amount',
{ text: discountedPrice }
);
// Verify the original price is the order total // Verify the original price is the order total
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: simpleProductPrice }); await expect(
}); page
).toMatchElement(
}); '.line_cost > .view > .woocommerce-Price-amount',
{ text: simpleProductPrice }
);
} );
} );
}; };
module.exports = runOrderApplyCouponTest; module.exports = runOrderApplyCouponTest;

View File

@ -3,72 +3,99 @@ const { createSimpleProduct } = require( '@woocommerce/e2e-utils' );
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const { merchant, createOrder } = require( '@woocommerce/e2e-utils' );
merchant,
createOrder,
} = require( '@woocommerce/e2e-utils' );
// TODO create a function for the logic below getConfigSimpleProduct(), see: https://github.com/woocommerce/woocommerce/issues/29072 // TODO create a function for the logic below getConfigSimpleProduct(), see: https://github.com/woocommerce/woocommerce/issues/29072
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const simpleProductPrice = config.has( 'products.simple.price' ) ? config.get( 'products.simple.price' ) : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const runMerchantOrdersCustomerPaymentPage = () => { const runMerchantOrdersCustomerPaymentPage = () => {
let orderId; let orderId;
let productId; let productId;
describe('WooCommerce Merchant Flow: Orders > Customer Payment Page', () => { describe( 'WooCommerce Merchant Flow: Orders > Customer Payment Page', () => {
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
orderId = await createOrder( { productId } ); orderId = await createOrder( { productId } );
await merchant.login(); await merchant.login();
}); } );
it('should show the customer payment page link on a pending payment order', async () => { it( 'should show the customer payment page link on a pending payment order', async () => {
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
// Verify the order is still pending payment // Verify the order is still pending payment
await expect( page ).toMatchElement( '#order_status', { text: 'Pending payment' } ); await expect( page ).toMatchElement( '#order_status', {
text: 'Pending payment',
} );
// Verify the customer payment page link is displayed // Verify the customer payment page link is displayed
await expect(page).toMatchElement( 'label[for=order_status] > a' , { text: 'Customer payment page →' }); await expect( page ).toMatchElement(
}); 'label[for=order_status] > a',
{ text: 'Customer payment page →' }
);
} );
it( 'should load the customer payment page', async () => {
it('should load the customer payment page', async () => {
// Verify the customer payment page link is displayed // Verify the customer payment page link is displayed
await expect(page).toMatchElement( 'label[for=order_status] > a' , { text: 'Customer payment page →' }); await expect( page ).toMatchElement(
'label[for=order_status] > a',
{ text: 'Customer payment page →' }
);
// Visit the page // Visit the page
await Promise.all([ await Promise.all( [
expect(page).toClick( 'label[for=order_status] > a' ), expect( page ).toClick( 'label[for=order_status] > a' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify we landed on the customer payment page // Verify we landed on the customer payment page
await expect(page).toMatchElement( 'h1.entry-title' , { text: 'Pay for order' }); await expect( page ).toMatchElement( 'h1.entry-title', {
await expect(page).toMatchElement( 'td.product-name' , { text: simpleProductName }); text: 'Pay for order',
await expect(page).toMatchElement( 'span.woocommerce-Price-amount.amount' , { text: simpleProductPrice }); } );
}); await expect( page ).toMatchElement( 'td.product-name', {
text: simpleProductName,
} );
await expect( page ).toMatchElement(
'span.woocommerce-Price-amount.amount',
{
text: simpleProductPrice,
}
);
} );
it('can pay for the order through the customer payment page', async () => { it( 'can pay for the order through the customer payment page', async () => {
// Make sure we're still on the customer payment page // Make sure we're still on the customer payment page
await expect(page).toMatchElement( 'h1.entry-title' , { text: 'Pay for order' }); await expect( page ).toMatchElement( 'h1.entry-title', {
text: 'Pay for order',
} );
// Pay for the order // Pay for the order
await Promise.all([ await Promise.all( [
expect(page).toClick( '#place_order'), expect( page ).toClick( '#place_order' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify we landed on the order received page // Verify we landed on the order received page
await expect(page).toMatchElement( 'h1.entry-title', { text: 'Order received' }); await expect( page ).toMatchElement( 'h1.entry-title', {
await expect(page).toMatchElement( 'li.woocommerce-order-overview__order.order' , { text: orderId.toString() }); text: 'Order received',
await expect(page).toMatchElement( 'span.woocommerce-Price-amount.amount' , { text: simpleProductPrice }); } );
}); await expect( page ).toMatchElement(
'li.woocommerce-order-overview__order.order',
}); {
text: orderId.toString(),
}
);
await expect( page ).toMatchElement(
'span.woocommerce-Price-amount.amount',
{
text: simpleProductPrice,
}
);
} );
} );
}; };
module.exports = runMerchantOrdersCustomerPaymentPage; module.exports = runMerchantOrdersCustomerPaymentPage;

View File

@ -15,63 +15,71 @@ let orderId;
const orderStatus = { const orderStatus = {
processing: 'processing', processing: 'processing',
completed: 'completed' completed: 'completed',
}; };
const runEditOrderTest = () => { const runEditOrderTest = () => {
describe('WooCommerce Orders > Edit order', () => { describe( 'WooCommerce Orders > Edit order', () => {
beforeAll(async () => { beforeAll( async () => {
orderId = await createOrder( { status: orderStatus.processing } ); orderId = await createOrder( { status: orderStatus.processing } );
await merchant.login(); await merchant.login();
}); } );
afterAll( async () => { afterAll( async () => {
await withRestApi.deleteOrder( orderId ); await withRestApi.deleteOrder( orderId );
}); } );
it('can view single order', async () => { it( 'can view single order', async () => {
// Go to "orders" page // Go to "orders" page
await merchant.openAllOrdersView(); await merchant.openAllOrdersView();
// Make sure we're on the orders page // Make sure we're on the orders page
await expect(page.title()).resolves.toMatch('Orders'); await expect( page.title() ).resolves.toMatch( 'Orders' );
//Open order we created //Open order we created
await merchant.goToOrder(orderId); await merchant.goToOrder( orderId );
// Make sure we're on the order details page // Make sure we're on the order details page
await expect(page.title()).resolves.toMatch('Edit order'); await expect( page.title() ).resolves.toMatch( 'Edit order' );
}); } );
it('can update order status', async () => { it( 'can update order status', async () => {
//Open order we created //Open order we created
await merchant.goToOrder(orderId); await merchant.goToOrder( orderId );
// Make sure we're still on the order details page // Make sure we're still on the order details page
await expect(page.title()).resolves.toMatch('Edit order'); await expect( page.title() ).resolves.toMatch( 'Edit order' );
// Update order status to `Completed` // Update order status to `Completed`
await merchant.updateOrderStatus(orderId, 'Completed'); await merchant.updateOrderStatus( orderId, 'Completed' );
// Verify order status changed note added // Verify order status changed note added
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Completed' } ); await expect( page ).toMatchElement(
'#select2-order_status-container',
{
text: 'Completed',
}
);
await expect( page ).toMatchElement( await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content', '#woocommerce-order-notes .note_content',
{ {
text: 'Order status changed from Processing to Completed.', text: 'Order status changed from Processing to Completed.',
} }
); );
}); } );
it('can update order details', async () => { it( 'can update order details', async () => {
//Open order we created //Open order we created
await merchant.goToOrder(orderId); await merchant.goToOrder( orderId );
// Make sure we're still on the order details page // Make sure we're still on the order details page
await expect(page.title()).resolves.toMatch('Edit order'); await expect( page.title() ).resolves.toMatch( 'Edit order' );
// Update order details // Update order details
await expect(page).toFill('input[name=order_date]', '2018-12-14'); await expect( page ).toFill(
'input[name=order_date]',
'2018-12-14'
);
// Wait for auto save // Wait for auto save
await utils.waitForTimeout( 2000 ); await utils.waitForTimeout( 2000 );
@ -80,10 +88,15 @@ const runEditOrderTest = () => {
await orderPageSaveChanges(); await orderPageSaveChanges();
// Verify // Verify
await expect( page ).toMatchElement( '#message', { text: 'Order updated.' } ); await expect( page ).toMatchElement( '#message', {
await verifyValueOfInputField( 'input[name=order_date]' , '2018-12-14' ); text: 'Order updated.',
}); } );
}); await verifyValueOfInputField(
'input[name=order_date]',
'2018-12-14'
);
} );
} );
describe( 'WooCommerce Orders > Edit order > Downloadable product permissions', () => { describe( 'WooCommerce Orders > Edit order > Downloadable product permissions', () => {
const productName = 'TDP 001'; const productName = 'TDP 001';
@ -97,12 +110,12 @@ const runEditOrderTest = () => {
await merchant.login(); await merchant.login();
} ); } );
beforeEach(async () => { beforeEach( async () => {
productId = await createSimpleDownloadableProduct( productName ); productId = await createSimpleDownloadableProduct( productName );
orderId = await createOrder( { orderId = await createOrder( {
productId, productId,
customerBilling , customerBilling,
status: orderStatus.processing status: orderStatus.processing,
} ); } );
} ); } );
@ -115,7 +128,7 @@ const runEditOrderTest = () => {
// Create order without product // Create order without product
const newOrderId = await createOrder( { const newOrderId = await createOrder( {
customerBilling, customerBilling,
status: orderStatus.processing status: orderStatus.processing,
} ); } );
// Open order we created // Open order we created
@ -125,7 +138,7 @@ const runEditOrderTest = () => {
await merchant.addDownloadableProductPermission( productName ); await merchant.addDownloadableProductPermission( productName );
// Verify new downloadable product permission details // Verify new downloadable product permission details
await merchant.verifyDownloadableProductPermission( productName ) await merchant.verifyDownloadableProductPermission( productName );
// Remove order // Remove order
await withRestApi.deleteOrder( newOrderId ); await withRestApi.deleteOrder( newOrderId );
@ -134,7 +147,9 @@ const runEditOrderTest = () => {
it( 'can add downloadable product permissions to order with product', async () => { it( 'can add downloadable product permissions to order with product', async () => {
// Create new downloadable product // Create new downloadable product
const newProductName = 'TDP 002'; const newProductName = 'TDP 002';
const newProductId = await createSimpleDownloadableProduct( newProductName ); const newProductId = await createSimpleDownloadableProduct(
newProductName
);
// Open order we created // Open order we created
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
@ -143,7 +158,9 @@ const runEditOrderTest = () => {
await merchant.addDownloadableProductPermission( newProductName ); await merchant.addDownloadableProductPermission( newProductName );
// Verify new downloadable product permission details // Verify new downloadable product permission details
await merchant.verifyDownloadableProductPermission( newProductName ) await merchant.verifyDownloadableProductPermission(
newProductName
);
// Remove product // Remove product
await withRestApi.deleteProduct( newProductId ); await withRestApi.deleteProduct( newProductId );
@ -180,21 +197,28 @@ const runEditOrderTest = () => {
await merchant.revokeDownloadableProductPermission( productName ); await merchant.revokeDownloadableProductPermission( productName );
// Verify // Verify
await expect( page ).not.toMatchElement( 'div.order_download_permissions', { await expect( page ).not.toMatchElement(
text: productName 'div.order_download_permissions',
} ); {
text: productName,
}
);
} ); } );
it( 'should not allow downloading a product if download attempts are exceeded', async () => { it( 'should not allow downloading a product if download attempts are exceeded', async () => {
// Define expected download error reason // Define expected download error reason
const expectedReason = 'Sorry, you have reached your download limit for this file'; const expectedReason =
'Sorry, you have reached your download limit for this file';
// Create order with product without any available download attempt // Create order with product without any available download attempt
const newProductId = await createSimpleDownloadableProduct( productName, 0 ); const newProductId = await createSimpleDownloadableProduct(
productName,
0
);
const newOrderId = await createOrder( { const newOrderId = await createOrder( {
productId: newProductId, productId: newProductId,
customerBilling, customerBilling,
status: orderStatus.processing status: orderStatus.processing,
} ); } );
// Open order we created // Open order we created
@ -204,7 +228,10 @@ const runEditOrderTest = () => {
const downloadPage = await merchant.openDownloadLink(); const downloadPage = await merchant.openDownloadLink();
// Verify file download cannot start // Verify file download cannot start
await merchant.verifyCannotDownloadFromBecause( downloadPage, expectedReason ); await merchant.verifyCannotDownloadFromBecause(
downloadPage,
expectedReason
);
// Remove data // Remove data
await withRestApi.deleteOrder( newOrderId ); await withRestApi.deleteOrder( newOrderId );
@ -220,15 +247,21 @@ const runEditOrderTest = () => {
// Update permission so that the expiration date has already passed // Update permission so that the expiration date has already passed
// Note: Seems this operation can't be performed through the API // Note: Seems this operation can't be performed through the API
await merchant.updateDownloadableProductPermission( productName, '2018-12-14' ); await merchant.updateDownloadableProductPermission(
productName,
'2018-12-14'
);
// Open download page // Open download page
const downloadPage = await merchant.openDownloadLink(); const downloadPage = await merchant.openDownloadLink();
// Verify file download cannot start // Verify file download cannot start
await merchant.verifyCannotDownloadFromBecause( downloadPage, expectedReason ); await merchant.verifyCannotDownloadFromBecause(
downloadPage,
expectedReason
);
} ); } );
} ); } );
} };
module.exports = runEditOrderTest; module.exports = runEditOrderTest;

View File

@ -11,14 +11,15 @@ const {
const config = require( 'config' ); const config = require( 'config' );
const customerEmail = config.get( 'addresses.customer.billing.email' ); const customerEmail = config.get( 'addresses.customer.billing.email' );
const adminEmail = config.has( 'users.admin.email' ) ? config.get( 'users.admin.email' ) : 'admin@woocommercecoree2etestsuite.com'; const adminEmail = config.has( 'users.admin.email' )
? config.get( 'users.admin.email' )
: 'admin@woocommercecoree2etestsuite.com';
const storeName = 'WooCommerce Core E2E Test Suite'; const storeName = 'WooCommerce Core E2E Test Suite';
let orderId; let orderId;
const runMerchantOrderEmailsTest = () => { const runMerchantOrderEmailsTest = () => {
describe( 'Merchant > Order Action emails received', () => {
describe('Merchant > Order Action emails received', () => {
beforeAll( async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
@ -29,7 +30,9 @@ const runMerchantOrderEmailsTest = () => {
await Promise.all( [ await Promise.all( [
// Select the billing email address field and add the customer billing email from the config // Select the billing email address field and add the customer billing email from the config
await page.click( 'div.order_data_column:nth-child(2) > h3:nth-child(1) > a:nth-child(1)' ), await page.click(
'div.order_data_column:nth-child(2) > h3:nth-child(1) > a:nth-child(1)'
),
await expect( page ).toFill( '#_billing_email', customerEmail ), await expect( page ).toFill( '#_billing_email', customerEmail ),
await clickUpdateOrder( 'Order updated.' ), await clickUpdateOrder( 'Order updated.' ),
] ); ] );
@ -41,31 +44,42 @@ const runMerchantOrderEmailsTest = () => {
} ); } );
// New order emails are sent automatically when we create the simple order above, so let's verify we get these emails // New order emails are sent automatically when we create the simple order above, so let's verify we get these emails
it('can receive new order email', async () => { it( 'can receive new order email', async () => {
await merchant.openEmailLog(); await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } ); await expect( page ).toMatchElement( '.column-receiver', {
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } ); text: adminEmail,
} );
await expect( page ).toMatchElement( '.column-subject', {
text: `[${ storeName }]: New order #${ orderId }`,
} );
} ); } );
it('can resend new order notification', async () => { it( 'can resend new order notification', async () => {
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
await selectOrderAction( 'send_order_details_admin' ); await selectOrderAction( 'send_order_details_admin' );
await merchant.openEmailLog(); await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } ); await expect( page ).toMatchElement( '.column-receiver', {
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } ); text: adminEmail,
} );
await expect( page ).toMatchElement( '.column-subject', {
text: `[${ storeName }]: New order #${ orderId }`,
} );
} ); } );
it('can email invoice/order details to customer', async () => { it( 'can email invoice/order details to customer', async () => {
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
await selectOrderAction( 'send_order_details' ); await selectOrderAction( 'send_order_details' );
await merchant.openEmailLog(); await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } ); await expect( page ).toMatchElement( '.column-receiver', {
await expect( page ).toMatchElement( '.column-subject', { text: `Invoice for order #${orderId} on ${storeName}` } ); text: customerEmail,
} );
await expect( page ).toMatchElement( '.column-subject', {
text: `Invoice for order #${ orderId } on ${ storeName }`,
} );
} ); } );
} ); } );
} };
module.exports = runMerchantOrderEmailsTest; module.exports = runMerchantOrderEmailsTest;

View File

@ -14,7 +14,7 @@ const {
GroupedProduct, GroupedProduct,
SimpleProduct, SimpleProduct,
ProductVariation, ProductVariation,
ExternalProduct ExternalProduct,
} = require( '@woocommerce/api' ); } = require( '@woocommerce/api' );
const taxClasses = [ const taxClasses = [
@ -33,31 +33,35 @@ const taxRates = [
{ {
name: 'Tax Rate Simple', name: 'Tax Rate Simple',
rate: '10.0000', rate: '10.0000',
class: 'tax-class-simple' class: 'tax-class-simple',
}, },
{ {
name: 'Tax Rate Variable', name: 'Tax Rate Variable',
rate: '20.0000', rate: '20.0000',
class: 'tax-class-variable' class: 'tax-class-variable',
}, },
{ {
name: 'Tax Rate External', name: 'Tax Rate External',
rate: '30.0000', rate: '30.0000',
class: 'tax-class-external' class: 'tax-class-external',
} },
]; ];
const taxTotals = ['$10.00', '$40.00', '$240.00']; const taxTotals = [ '$10.00', '$40.00', '$240.00' ];
const initProducts = async () => { const initProducts = async () => {
const apiUrl = config.get('url'); const apiUrl = config.get( 'url' );
const adminUsername = config.get('users.admin.username'); const adminUsername = config.get( 'users.admin.username' );
const adminPassword = config.get('users.admin.password'); const adminPassword = config.get( 'users.admin.password' );
const httpClient = HTTPClientFactory.build(apiUrl) const httpClient = HTTPClientFactory.build( apiUrl )
.withBasicAuth(adminUsername, adminPassword) .withBasicAuth( adminUsername, adminPassword )
.create(); .create();
await withRestApi.updateSettingOption( 'general', 'woocommerce_calc_taxes', { value: 'yes' } ); await withRestApi.updateSettingOption(
'general',
'woocommerce_calc_taxes',
{ value: 'yes' }
);
await withRestApi.addTaxClasses( taxClasses ); await withRestApi.addTaxClasses( taxClasses );
await withRestApi.addTaxRates( taxRates ); await withRestApi.addTaxRates( taxRates );
@ -67,7 +71,7 @@ const initProducts = async () => {
const simpleProduct = { const simpleProduct = {
name: 'Simple Product 273722', name: 'Simple Product 273722',
regularPrice: '100', regularPrice: '100',
taxClass: 'Tax Class Simple' taxClass: 'Tax Class Simple',
}; };
return await repo.create( simpleProduct ); return await repo.create( simpleProduct );
}; };
@ -79,65 +83,65 @@ const initProducts = async () => {
attributes: [ attributes: [
{ {
name: 'Size', name: 'Size',
option: 'Small' option: 'Small',
}, },
{ {
name: 'Colour', name: 'Colour',
option: 'Yellow' option: 'Yellow',
} },
], ],
taxClass: 'Tax Class Variable' taxClass: 'Tax Class Variable',
}, },
{ {
regularPrice: '300', regularPrice: '300',
attributes: [ attributes: [
{ {
name: 'Size', name: 'Size',
option: 'Medium' option: 'Medium',
}, },
{ {
name: 'Colour', name: 'Colour',
option: 'Magenta' option: 'Magenta',
} },
], ],
taxClass: 'Tax Class Variable' taxClass: 'Tax Class Variable',
} },
]; ];
const variableProductData = { const variableProductData = {
name: 'Variable Product 024611', name: 'Variable Product 024611',
type: 'variable', type: 'variable',
taxClass: 'Tax Class Variable' taxClass: 'Tax Class Variable',
}; };
const variationRepo = ProductVariation.restRepository(httpClient); const variationRepo = ProductVariation.restRepository( httpClient );
const productRepo = VariableProduct.restRepository(httpClient); const productRepo = VariableProduct.restRepository( httpClient );
const variableProduct = await productRepo.create(variableProductData); const variableProduct = await productRepo.create( variableProductData );
for (const v of variations) { for ( const v of variations ) {
await variationRepo.create(variableProduct.id, v); await variationRepo.create( variableProduct.id, v );
} }
return variableProduct; return variableProduct;
}; };
const initGroupedProduct = async () => { const initGroupedProduct = async () => {
const groupedRepo = GroupedProduct.restRepository(httpClient); const groupedRepo = GroupedProduct.restRepository( httpClient );
const defaultGroupedData = config.get('products.grouped'); const defaultGroupedData = config.get( 'products.grouped' );
const groupedProductData = { const groupedProductData = {
...defaultGroupedData, ...defaultGroupedData,
name: 'Grouped Product 858012' name: 'Grouped Product 858012',
}; };
return await groupedRepo.create(groupedProductData); return await groupedRepo.create( groupedProductData );
}; };
const initExternalProduct = async () => { const initExternalProduct = async () => {
const repo = ExternalProduct.restRepository(httpClient); const repo = ExternalProduct.restRepository( httpClient );
const defaultProps = config.get('products.external'); const defaultProps = config.get( 'products.external' );
const props = { const props = {
...defaultProps, ...defaultProps,
name: 'External product 786794', name: 'External product 786794',
regularPrice: '800', regularPrice: '800',
taxClass: 'Tax Class External' taxClass: 'Tax Class External',
}; };
return await repo.create(props); return await repo.create( props );
}; };
// Create a product for each product type // Create a product for each product type
@ -146,66 +150,78 @@ const initProducts = async () => {
const groupedProduct = await initGroupedProduct(); const groupedProduct = await initGroupedProduct();
const externalProduct = await initExternalProduct(); const externalProduct = await initExternalProduct();
return [simpleProduct, variableProduct, groupedProduct, externalProduct]; return [ simpleProduct, variableProduct, groupedProduct, externalProduct ];
}; };
let products; let products;
const runCreateOrderTest = () => { const runCreateOrderTest = () => {
describe('WooCommerce Orders > Add new order', () => { describe( 'WooCommerce Orders > Add new order', () => {
beforeAll(async () => { beforeAll( async () => {
// Initialize products for each product type // Initialize products for each product type
products = await initProducts(); products = await initProducts();
// Login // Login
await merchant.login(); await merchant.login();
}); } );
it('can create new order', async () => { it( 'can create new order', async () => {
// Go to "add order" page // Go to "add order" page
await merchant.openNewOrder(); await merchant.openNewOrder();
// Make sure we're on the add order page // Make sure we're on the add order page
await expect(page.title()).resolves.toMatch('Add new order'); await expect( page.title() ).resolves.toMatch( 'Add new order' );
// Set order data // Set order data
await expect(page).toSelect('#order_status', 'Processing'); await expect( page ).toSelect( '#order_status', 'Processing' );
await expect(page).toFill('input[name=order_date]', '2018-12-13'); await expect( page ).toFill(
await expect(page).toFill('input[name=order_date_hour]', '18'); 'input[name=order_date]',
await expect(page).toFill('input[name=order_date_minute]', '55'); '2018-12-13'
);
await expect( page ).toFill( 'input[name=order_date_hour]', '18' );
await expect( page ).toFill(
'input[name=order_date_minute]',
'55'
);
// Create order, verify that it was created. Trash order, verify that it was trashed. // Create order, verify that it was created. Trash order, verify that it was trashed.
const orderEdit = new AdminEdit(); const orderEdit = new AdminEdit();
await orderEdit.verifyPublish( await orderEdit.verifyPublish(
'.order_actions li .save_order', '.order_actions li .save_order',
'#message', '#message',
'Order updated.', 'Order updated.'
); );
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Processing' } ); await expect( page ).toMatchElement(
'#select2-order_status-container',
{
text: 'Processing',
}
);
await expect( page ).toMatchElement( await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content', '#woocommerce-order-notes .note_content',
{ {
text: 'Order status changed from Pending payment to Processing.', text:
'Order status changed from Pending payment to Processing.',
} }
); );
}); } );
it('can create new complex order with multiple product types & tax classes', async () => { it( 'can create new complex order with multiple product types & tax classes', async () => {
// Go to "add order" page // Go to "add order" page
await merchant.openNewOrder(); await merchant.openNewOrder();
// Open modal window for adding line items // Open modal window for adding line items
await expect(page).toClick('button.add-line-item'); await expect( page ).toClick( 'button.add-line-item' );
await expect(page).toClick('button.add-order-item'); await expect( page ).toClick( 'button.add-order-item' );
await page.waitForSelector('.wc-backbone-modal-header'); await page.waitForSelector( '.wc-backbone-modal-header' );
// Search for each product to add, then verify that they are saved // Search for each product to add, then verify that they are saved
for (const { name } of products) { for ( const { name } of products ) {
await expect(page).toClick( await expect( page ).toClick(
'.wc-backbone-modal-content tr:last-child .select2-selection__arrow' '.wc-backbone-modal-content tr:last-child .select2-selection__arrow'
); );
await expect(page).toFill( await expect( page ).toFill(
'#wc-backbone-modal-dialog + .select2-container .select2-search__field', '#wc-backbone-modal-dialog + .select2-container .select2-search__field',
name name
); );
@ -213,52 +229,60 @@ const runCreateOrderTest = () => {
'li[data-selected]' 'li[data-selected]'
); );
await firstResult.click(); await firstResult.click();
await expect(page).toMatchElement( await expect( page ).toMatchElement(
'.wc-backbone-modal-content tr:nth-last-child(2) .wc-product-search option', '.wc-backbone-modal-content tr:nth-last-child(2) .wc-product-search option',
name name
); );
} }
// Save the line items // Save the line items
await expect(page).toClick('.wc-backbone-modal-content #btn-ok'); await expect( page ).toClick(
'.wc-backbone-modal-content #btn-ok'
);
await uiUnblocked(); await uiUnblocked();
// Recalculate taxes // Recalculate taxes
await expect(page).toDisplayDialog(async () => { await expect( page ).toDisplayDialog( async () => {
await expect(page).toClick('.calculate-action'); await expect( page ).toClick( '.calculate-action' );
}); } );
await page.waitForSelector('th.line_tax'); await page.waitForSelector( 'th.line_tax' );
// Save the order and verify line items // Save the order and verify line items
await expect(page).toClick('button.save_order'); await expect( page ).toClick( 'button.save_order' );
await page.waitForNavigation(); await page.waitForNavigation();
for (const { name } of products) { for ( const { name } of products ) {
await expect(page).toMatchElement('.wc-order-item-name', { await expect( page ).toMatchElement( '.wc-order-item-name', {
text: name text: name,
}); } );
} }
// Verify that the names of each tax class were shown // Verify that the names of each tax class were shown
for (const taxRate of taxRates) { for ( const taxRate of taxRates ) {
await expect(page).toMatchElement('th.line_tax', { await expect( page ).toMatchElement( 'th.line_tax', {
text: taxRate.name text: taxRate.name,
}); } );
await expect(page).toMatchElement('.wc-order-totals td.label', { await expect( page ).toMatchElement(
text: taxRate.name '.wc-order-totals td.label',
}); {
text: taxRate.name,
}
);
} }
// Verify tax amounts // Verify tax amounts
for (const amount of taxTotals) { for ( const amount of taxTotals ) {
await expect(page).toMatchElement('td.line_tax', { await expect( page ).toMatchElement( 'td.line_tax', {
text: amount text: amount,
}); } );
await expect(page).toMatchElement('.wc-order-totals td.total', { await expect( page ).toMatchElement(
text: amount '.wc-order-totals td.total',
}); {
text: amount,
}
);
} }
}); } );
}); } );
}; };
module.exports = runCreateOrderTest; module.exports = runCreateOrderTest;

View File

@ -18,7 +18,7 @@ const {
* *
* @param {string} buttonSelector * @param {string} buttonSelector
* @param {string} resultSelector * @param {string} resultSelector
* @returns {Promise<void>} * @return {Promise<void>}
*/ */
const clickAndWaitForSelector = async ( buttonSelector, resultSelector ) => { const clickAndWaitForSelector = async ( buttonSelector, resultSelector ) => {
await evalAndClick( buttonSelector ); await evalAndClick( buttonSelector );

View File

@ -12,84 +12,121 @@ const {
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const runRefundOrderTest = () => { const runRefundOrderTest = () => {
describe('WooCommerce Orders > Refund an order', () => { describe( 'WooCommerce Orders > Refund an order', () => {
let productId; let productId;
let orderId; let orderId;
let currencySymbol; let currencySymbol;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
orderId = await createOrder( { orderId = await createOrder( {
productId, productId,
status: 'completed' status: 'completed',
} ); } );
await merchant.login(); await merchant.login();
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
// Get the currency symbol for the store's selected currency // Get the currency symbol for the store's selected currency
await page.waitForSelector('.woocommerce-Price-currencySymbol'); await page.waitForSelector( '.woocommerce-Price-currencySymbol' );
let currencyElement = await page.$('.woocommerce-Price-currencySymbol'); const currencyElement = await page.$(
currencySymbol = await page.evaluate(el => el.textContent, currencyElement); '.woocommerce-Price-currencySymbol'
}); );
currencySymbol = await page.evaluate(
( el ) => el.textContent,
currencyElement
);
} );
it('can issue a refund by quantity', async () => { it( 'can issue a refund by quantity', async () => {
// Click the Refund button // Click the Refund button
await expect(page).toClick('button.refund-items'); await expect( page ).toClick( 'button.refund-items' );
// Verify the refund section shows // Verify the refund section shows
await page.waitForSelector('div.wc-order-refund-items', { visible: true }); await page.waitForSelector( 'div.wc-order-refund-items', {
await verifyCheckboxIsSet('#restock_refunded_items'); visible: true,
} );
await verifyCheckboxIsSet( '#restock_refunded_items' );
// Initiate a refund // Initiate a refund
await expect(page).toFill('.refund_order_item_qty', '1'); await expect( page ).toFill( '.refund_order_item_qty', '1' );
await expect(page).toFill('#refund_reason', 'No longer wanted'); await expect( page ).toFill( '#refund_reason', 'No longer wanted' );
await Promise.all([ await Promise.all( [
verifyValueOfInputField('.refund_line_total', simpleProductPrice), verifyValueOfInputField(
verifyValueOfInputField('#refund_amount', simpleProductPrice), '.refund_line_total',
expect(page).toMatchElement('.do-manual-refund', { text: `Refund ${currencySymbol + simpleProductPrice} manually` }), simpleProductPrice
]); ),
verifyValueOfInputField( '#refund_amount', simpleProductPrice ),
expect( page ).toMatchElement( '.do-manual-refund', {
text: `Refund ${
currencySymbol + simpleProductPrice
} manually`,
} ),
] );
await clickAndWaitForSelector( '.do-manual-refund', '.quantity .refunded' ); await clickAndWaitForSelector(
'.do-manual-refund',
'.quantity .refunded'
);
await uiUnblocked(); await uiUnblocked();
await Promise.all([ await Promise.all( [
// Verify the product line item shows the refunded quantity and amount // Verify the product line item shows the refunded quantity and amount
expect(page).toMatchElement('.quantity .refunded', { text: '-1' }), expect( page ).toMatchElement( '.quantity .refunded', {
expect(page).toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }), text: '-1',
} ),
expect( page ).toMatchElement( '.line_cost .refunded', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// Verify the refund shows in the list with the amount // Verify the refund shows in the list with the amount
expect(page).toMatchElement('.refund .description', { text: 'No longer wanted' }), expect( page ).toMatchElement( '.refund .description', {
expect(page).toMatchElement('.refund > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }), text: 'No longer wanted',
} ),
expect( page ).toMatchElement( '.refund > .line_cost', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// Verify system note was added // Verify system note was added
expect(page).toMatchElement('.system-note', { text: 'Order status changed from Completed to Refunded.' }), expect( page ).toMatchElement( '.system-note', {
]); text: 'Order status changed from Completed to Refunded.',
}); } ),
] );
} );
it('can delete an issued refund', async () => { it( 'can delete an issued refund', async () => {
await clickAndWaitForSelector( 'a.delete_refund', '.refund-items' ); await clickAndWaitForSelector( 'a.delete_refund', '.refund-items' );
await uiUnblocked(); await uiUnblocked();
await Promise.all([ await Promise.all( [
// Verify the refunded row item is no longer showing // Verify the refunded row item is no longer showing
expect(page).not.toMatchElement('tr.refund', { visible: true }), expect( page ).not.toMatchElement( 'tr.refund', {
visible: true,
} ),
// Verify the product line item doesn't show the refunded quantity and amount // Verify the product line item doesn't show the refunded quantity and amount
expect(page).not.toMatchElement('.quantity .refunded', { text: '-1' }), expect( page ).not.toMatchElement( '.quantity .refunded', {
expect(page).not.toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }), text: '-1',
} ),
expect( page ).not.toMatchElement( '.line_cost .refunded', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// Verify the refund shows in the list with the amount // Verify the refund shows in the list with the amount
expect(page).not.toMatchElement('.refund .description', { text: 'No longer wanted' }), expect( page ).not.toMatchElement( '.refund .description', {
expect(page).not.toMatchElement('.refund > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }), text: 'No longer wanted',
]); } ),
}); expect( page ).not.toMatchElement( '.refund > .line_cost', {
text: `-${ currencySymbol + simpleProductPrice }`,
}); } ),
] );
} );
} );
}; };
module.exports = runRefundOrderTest; module.exports = runRefundOrderTest;

View File

@ -42,7 +42,7 @@ const customerShipping = {
/** /**
* Set the billing fields for the customer account for this test suite. * Set the billing fields for the customer account for this test suite.
* *
* @returns {Promise<number>} * @return {Promise<number>}
*/ */
const updateCustomerBilling = async () => { const updateCustomerBilling = async () => {
const client = factories.api.withDefaultPermalinks; const client = factories.api.withDefaultPermalinks;
@ -55,7 +55,7 @@ const updateCustomerBilling = async () => {
return; return;
} }
const customerId = customers.data[0].id; const customerId = customers.data[ 0 ].id;
const customerData = { const customerData = {
id: customerId, id: customerId,
billing: customerBilling, billing: customerBilling,
@ -69,65 +69,65 @@ const updateCustomerBilling = async () => {
* Data table to be fed into `it.each()`. * Data table to be fed into `it.each()`.
*/ */
const queries = [ const queries = [
[customerBilling.first_name, 'billing first name'], [ customerBilling.first_name, 'billing first name' ],
[customerBilling.last_name, 'billing last name'], [ customerBilling.last_name, 'billing last name' ],
[customerBilling.company, 'billing company name'], [ customerBilling.company, 'billing company name' ],
[customerBilling.address_1, 'billing first address'], [ customerBilling.address_1, 'billing first address' ],
[customerBilling.address_2, 'billing second address'], [ customerBilling.address_2, 'billing second address' ],
[customerBilling.city, 'billing city name'], [ customerBilling.city, 'billing city name' ],
[customerBilling.postcode, 'billing post code'], [ customerBilling.postcode, 'billing post code' ],
[customerBilling.email, 'billing email'], [ customerBilling.email, 'billing email' ],
[customerBilling.phone, 'billing phone'], [ customerBilling.phone, 'billing phone' ],
[customerBilling.state, 'billing state'], [ customerBilling.state, 'billing state' ],
[customerShipping.first_name, 'shipping first name'], [ customerShipping.first_name, 'shipping first name' ],
[customerShipping.last_name, 'shipping last name'], [ customerShipping.last_name, 'shipping last name' ],
[customerShipping.address_1, 'shipping first address'], [ customerShipping.address_1, 'shipping first address' ],
[customerShipping.address_2, 'shipping second address'], [ customerShipping.address_2, 'shipping second address' ],
[customerShipping.city, 'shipping city name'], [ customerShipping.city, 'shipping city name' ],
[customerShipping.postcode, 'shipping post code'], [ customerShipping.postcode, 'shipping post code' ],
[itemName, 'shipping item name'] [ itemName, 'shipping item name' ],
]; ];
const runOrderSearchingTest = () => { const runOrderSearchingTest = () => {
describe('WooCommerce Orders > Search orders', () => { describe( 'WooCommerce Orders > Search orders', () => {
let productId; let productId;
let orderId; let orderId;
let customerId; let customerId;
beforeAll( async () => { beforeAll( async () => {
productId = await createSimpleProduct('Wanted Product'); productId = await createSimpleProduct( 'Wanted Product' );
customerId = await updateCustomerBilling(); customerId = await updateCustomerBilling();
orderId = await createOrder({ orderId = await createOrder( {
customerId, customerId,
productId, productId,
customerBilling, customerBilling,
customerShipping, customerShipping,
}); } );
// Login and open All Orders view // Login and open All Orders view
await merchant.login(); await merchant.login();
await merchant.openAllOrdersView(); await merchant.openAllOrdersView();
}); } );
it('can search for order by order id', async () => { it( 'can search for order by order id', async () => {
// Convert the order ID to string so we can search on it // Convert the order ID to string so we can search on it
await searchForOrder(orderId.toString(), orderId, searchString); await searchForOrder( orderId.toString(), orderId, searchString );
}); } );
it.each(queries)( it.each( queries )(
'can search for order containing "%s" as the %s', 'can search for order containing "%s" as the %s',
async (value) => { async ( value ) => {
await searchForOrder(value, orderId, searchString); await searchForOrder( value, orderId, searchString );
} }
); );
/** /**
* shipping state is abbreviated. This test passes if billing and shipping state are the same * shipping state is abbreviated. This test passes if billing and shipping state are the same
*/ */
it.skip('can search for order by shipping state name', async () => { it.skip( 'can search for order by shipping state name', async () => {
await searchForOrder('New York', orderId, searchString); await searchForOrder( 'New York', orderId, searchString );
}) } );
}); } );
}; };
module.exports = runOrderSearchingTest; module.exports = runOrderSearchingTest;

View File

@ -12,13 +12,13 @@ const statusColumnTextSelector = 'mark.order-status > span';
// Define order statuses to filter against // Define order statuses to filter against
const orderStatus = [ const orderStatus = [
['Pending payment', 'wc-pending'], [ 'Pending payment', 'wc-pending' ],
['Processing', 'wc-processing'], [ 'Processing', 'wc-processing' ],
['On hold', 'wc-on-hold'], [ 'On hold', 'wc-on-hold' ],
['Completed', 'wc-completed'], [ 'Completed', 'wc-completed' ],
['Cancelled', 'wc-cancelled'], [ 'Cancelled', 'wc-cancelled' ],
['Refunded', 'wc-refunded'], [ 'Refunded', 'wc-refunded' ],
['Failed', 'wc-failed'], [ 'Failed', 'wc-failed' ],
]; ];
const defaultOrder = { const defaultOrder = {
payment_method: 'cod', payment_method: 'cod',
@ -26,21 +26,21 @@ const defaultOrder = {
first_name: 'John', first_name: 'John',
last_name: 'Doe', last_name: 'Doe',
email: 'john.doe@example.com', email: 'john.doe@example.com',
} },
}; };
const runOrderStatusFiltersTest = () => { const runOrderStatusFiltersTest = () => {
describe('WooCommerce Orders > Filter Orders by Status', () => { describe( 'WooCommerce Orders > Filter Orders by Status', () => {
beforeAll(async () => { beforeAll( async () => {
// First, let's create some orders we can filter against // First, let's create some orders we can filter against
const orders = orderStatus.map((entryPair) => { const orders = orderStatus.map( ( entryPair ) => {
const statusName = entryPair[1].replace('wc-', ''); const statusName = entryPair[ 1 ].replace( 'wc-', '' );
return { return {
...defaultOrder, ...defaultOrder,
status: statusName, status: statusName,
}; };
}); } );
// Create the orders using the API // Create the orders using the API
await withRestApi.batchCreateOrders( orders, false ); await withRestApi.batchCreateOrders( orders, false );
@ -48,42 +48,53 @@ const runOrderStatusFiltersTest = () => {
// Next, let's login and navigate to the Orders page // Next, let's login and navigate to the Orders page
await merchant.login(); await merchant.login();
await merchant.openAllOrdersView(); await merchant.openAllOrdersView();
}); } );
afterAll( async () => { afterAll( async () => {
// Make sure we're on the all orders view and cleanup the orders we created // Make sure we're on the all orders view and cleanup the orders we created
await merchant.openAllOrdersView(); await merchant.openAllOrdersView();
await moveAllItemsToTrash(); await moveAllItemsToTrash();
}); } );
it.each(orderStatus)('should filter by %s', async (statusText, statusClassName) => { it.each( orderStatus )(
// Identify which statuses should be shown or hidden 'should filter by %s',
const shownStatus = { text: statusText }; async ( statusText, statusClassName ) => {
const hiddenStatuses = orderStatus // Identify which statuses should be shown or hidden
.filter((pair) => !pair.includes(statusText)) const shownStatus = { text: statusText };
.map(([statusText]) => { const hiddenStatuses = orderStatus
return { text: statusText }; .filter( ( pair ) => ! pair.includes( statusText ) )
}); .map( ( [ statusText ] ) => {
return { text: statusText };
} );
// Click the status filter and verify that only the matching order is shown // Click the status filter and verify that only the matching order is shown
await clickFilter('.' + statusClassName); await clickFilter( '.' + statusClassName );
await expect(page).toMatchElement(statusColumnTextSelector, shownStatus); await expect( page ).toMatchElement(
statusColumnTextSelector,
shownStatus
);
// Verify other statuses don't show // Verify other statuses don't show
for (const hiddenStatus of hiddenStatuses) { for ( const hiddenStatus of hiddenStatuses ) {
await expect(page).not.toMatchElement(statusColumnTextSelector, hiddenStatus); await expect( page ).not.toMatchElement(
statusColumnTextSelector,
hiddenStatus
);
}
} }
}); );
it('should filter by All', async () => { it( 'should filter by All', async () => {
// Make sure all the order statuses that were created show in this list // Make sure all the order statuses that were created show in this list
await clickFilter('.all'); await clickFilter( '.all' );
for (const [statusText] of orderStatus) { for ( const [ statusText ] of orderStatus ) {
await expect(page).toMatchElement(statusColumnTextSelector, { text: statusText }); await expect( page ).toMatchElement( statusColumnTextSelector, {
text: statusText,
} );
} }
}); } );
}); } );
}; };
module.exports = runOrderStatusFiltersTest; module.exports = runOrderStatusFiltersTest;

View File

@ -11,7 +11,7 @@ const { it, describe, beforeAll } = require( '@jest/globals' );
const runPageLoadTest = () => { const runPageLoadTest = () => {
describe.each( MENUS )( describe.each( MENUS )(
' %s > Opening Top Level Pages', '%s > Opening Top Level Pages',
( menuTitle, menuElement, subMenus ) => { ( menuTitle, menuElement, subMenus ) => {
beforeAll( async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
@ -34,9 +34,11 @@ const runPageLoadTest = () => {
// Click sub-menu item and wait for the page to finish loading // Click sub-menu item and wait for the page to finish loading
if ( subMenuElement.length ) { if ( subMenuElement.length ) {
await Promise.all([ await Promise.all( [
page.click( subMenuElement ), page.click( subMenuElement ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( {
waitUntil: 'networkidle0',
} ),
] ); ] );
} }

View File

@ -18,37 +18,43 @@ const { it, describe, beforeAll } = require( '@jest/globals' );
let productId; let productId;
const runProductEditDetailsTest = () => { const runProductEditDetailsTest = () => {
describe('Products > Edit Product', () => { describe( 'Products > Edit Product', () => {
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await merchant.login(); await merchant.login();
}); } );
it('can edit a product and save the changes', async () => { it( 'can edit a product and save the changes', async () => {
await merchant.goToProduct(productId); await merchant.goToProduct( productId );
// Clear the input fields first, then add the new values // Clear the input fields first, then add the new values
await expect(page).toFill('#title', ''); await expect( page ).toFill( '#title', '' );
await expect(page).toFill('#_regular_price', ''); await expect( page ).toFill( '#_regular_price', '' );
await expect(page).toFill('#title', 'Awesome product'); await expect( page ).toFill( '#title', 'Awesome product' );
// Switch to text mode to work around the iframe // Switch to text mode to work around the iframe
await expect(page).toClick('#content-html'); await expect( page ).toClick( '#content-html' );
await expect(page).toFill('.wp-editor-area', 'This product is pretty awesome.'); await expect( page ).toFill(
'.wp-editor-area',
'This product is pretty awesome.'
);
await expect(page).toFill('#_regular_price', '100.05'); await expect( page ).toFill( '#_regular_price', '100.05' );
// Save the changes // Save the changes
await verifyAndPublish('Product updated.'); await verifyAndPublish( 'Product updated.' );
await uiUnblocked(); await uiUnblocked();
// Verify the changes saved // Verify the changes saved
await expect(page).toMatchElement('#title', 'Awesome product'); await expect( page ).toMatchElement( '#title', 'Awesome product' );
await expect(page).toMatchElement('.wp-editor-area', 'This product is pretty awesome.'); await expect( page ).toMatchElement(
await expect(page).toMatchElement('#_regular_price', '100.05'); '.wp-editor-area',
}); 'This product is pretty awesome.'
}); );
} await expect( page ).toMatchElement( '#_regular_price', '100.05' );
} );
} );
};
module.exports = runProductEditDetailsTest; module.exports = runProductEditDetailsTest;

View File

@ -104,13 +104,13 @@ const runImportProductsTest = () => {
await merchant.openImportProducts(); await merchant.openImportProducts();
} ); } );
afterAll(async () => { afterAll( async () => {
// Delete imported products // Delete imported products
await withRestApi.deleteAllProducts(); await withRestApi.deleteAllProducts();
await withRestApi.deleteAllProductAttributes( false ); await withRestApi.deleteAllProductAttributes( false );
await withRestApi.deleteAllProductCategories( false ); await withRestApi.deleteAllProductCategories( false );
await withRestApi.deleteAllProductTags( false ); await withRestApi.deleteAllProductTags( false );
}); } );
it( 'should show error message if you go without providing CSV file', async () => { it( 'should show error message if you go without providing CSV file', async () => {
// Verify the error message if you go without providing CSV file // Verify the error message if you go without providing CSV file

View File

@ -13,22 +13,19 @@ const {
waitForSelector, waitForSelector,
waitForSelectorWithoutThrow, waitForSelectorWithoutThrow,
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const { const { waitAndClick } = require( '@woocommerce/e2e-environment' );
waitAndClick,
} = require( '@woocommerce/e2e-environment' );
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe } = require( '@jest/globals' );
it,
describe,
} = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const VirtualProductName = 'Virtual Product Name'; const VirtualProductName = 'Virtual Product Name';
const NonVirtualProductName = 'Non-Virtual Product Name'; const NonVirtualProductName = 'Non-Virtual Product Name';
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const defaultAttributes = [ 'val2', 'val1', 'val2' ]; const defaultAttributes = [ 'val2', 'val1', 'val2' ];
const openNewProductAndVerify = async () => { const openNewProductAndVerify = async () => {
@ -36,13 +33,13 @@ const openNewProductAndVerify = async () => {
await merchant.openNewProduct(); await merchant.openNewProduct();
// Make sure we're on the add product page // Make sure we're on the add product page
await expect(page.title()).resolves.toMatch('Add new product'); await expect( page.title() ).resolves.toMatch( 'Add new product' );
} };
/** /**
* Select a variation action from the actions menu. * Select a variation action from the actions menu.
* *
* @param action item you selected from the variation actions menu * @param action item you selected from the variation actions menu
*/ */
const selectVariationAction = async ( action ) => { const selectVariationAction = async ( action ) => {
await waitForSelector( page, 'select.variation_actions:not(:disabled)' ); await waitForSelector( page, 'select.variation_actions:not(:disabled)' );
@ -67,9 +64,8 @@ const expandVariations = async () => {
}; };
const runAddSimpleProductTest = () => { const runAddSimpleProductTest = () => {
describe('Add New Simple Product Page', () => { describe( 'Add New Simple Product Page', () => {
it('can create simple virtual product and add it to the cart', async () => { it( 'can create simple virtual product and add it to the cart', async () => {
// @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed // @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed
await setBrowserViewport( { await setBrowserViewport( {
width: 970, width: 970,
@ -80,30 +76,35 @@ const runAddSimpleProductTest = () => {
await openNewProductAndVerify(); await openNewProductAndVerify();
// Set product data and publish the product // Set product data and publish the product
await expect(page).toFill('#title', VirtualProductName); await expect( page ).toFill( '#title', VirtualProductName );
await expect(page).toClick('#_virtual'); await expect( page ).toClick( '#_virtual' );
await clickTab('General'); await clickTab( 'General' );
await expect(page).toFill('#_regular_price', simpleProductPrice); await expect( page ).toFill(
'#_regular_price',
simpleProductPrice
);
await verifyAndPublish(); await verifyAndPublish();
await merchant.logout(); await merchant.logout();
}); } );
it('can have a shopper add the simple virtual product to the cart', async () => { it( 'can have a shopper add the simple virtual product to the cart', async () => {
// See product in the shop and add it to the cart // See product in the shop and add it to the cart
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage(VirtualProductName); await shopper.addToCartFromShopPage( VirtualProductName );
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(VirtualProductName); await shopper.productIsInCart( VirtualProductName );
// Assert that the page does not contain shipping calculation button // Assert that the page does not contain shipping calculation button
await expect(page).not.toMatchElement('a.shipping-calculator-button'); await expect( page ).not.toMatchElement(
'a.shipping-calculator-button'
);
// Remove product from cart // Remove product from cart
await shopper.removeFromCart(VirtualProductName); await shopper.removeFromCart( VirtualProductName );
}); } );
it('can create simple non-virtual product and add it to the cart', async () => { it( 'can create simple non-virtual product and add it to the cart', async () => {
// @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed // @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed
await setBrowserViewport( { await setBrowserViewport( {
width: 960, width: 960,
@ -114,74 +115,104 @@ const runAddSimpleProductTest = () => {
await openNewProductAndVerify(); await openNewProductAndVerify();
// Set product data and publish the product // Set product data and publish the product
await expect(page).toFill('#title', NonVirtualProductName); await expect( page ).toFill( '#title', NonVirtualProductName );
await clickTab('General'); await clickTab( 'General' );
await expect(page).toFill('#_regular_price', simpleProductPrice); await expect( page ).toFill(
'#_regular_price',
simpleProductPrice
);
await verifyAndPublish(); await verifyAndPublish();
await merchant.logout(); await merchant.logout();
}); } );
it('can have a shopper add the simple non-virtual product to the cart', async () => { it( 'can have a shopper add the simple non-virtual product to the cart', async () => {
// See product in the shop and add it to the cart // See product in the shop and add it to the cart
await shopper.goToShop(); await shopper.goToShop();
await page.reload({ waitUntil: ['networkidle0', 'domcontentloaded'] }); await page.reload( {
waitUntil: [ 'networkidle0', 'domcontentloaded' ],
} );
await shopper.addToCartFromShopPage(NonVirtualProductName); await shopper.addToCartFromShopPage( NonVirtualProductName );
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(NonVirtualProductName); await shopper.productIsInCart( NonVirtualProductName );
// Assert that the page does contain shipping calculation button // Assert that the page does contain shipping calculation button
await page.waitForSelector('a.shipping-calculator-button'); await page.waitForSelector( 'a.shipping-calculator-button' );
await expect(page).toMatchElement('a.shipping-calculator-button'); await expect( page ).toMatchElement(
'a.shipping-calculator-button'
);
// Remove product from cart // Remove product from cart
await shopper.removeFromCart(NonVirtualProductName); await shopper.removeFromCart( NonVirtualProductName );
}); } );
}); } );
}; };
const runAddVariableProductTest = () => { const runAddVariableProductTest = () => {
describe('Add New Variable Product Page', () => { describe( 'Add New Variable Product Page', () => {
it('can create product with variations', async () => { it( 'can create product with variations', async () => {
await merchant.login(); await merchant.login();
await openNewProductAndVerify(); await openNewProductAndVerify();
// Set product data // Set product data
await expect(page).toFill('#title', 'Variable Product with Three Variations'); await expect( page ).toFill(
await expect(page).toSelect('#product-type', 'Variable product'); '#title',
}); 'Variable Product with Three Variations'
);
it('can create set variable product attributes', async () => { await expect( page ).toSelect(
'#product-type',
'Variable product'
);
} );
it( 'can create set variable product attributes', async () => {
// Create attributes for variations // Create attributes for variations
await waitAndClick( page, '.attribute_tab a' ); await waitAndClick( page, '.attribute_tab a' );
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' ); await expect( page ).toSelect(
'select[name="attribute_taxonomy"]',
'Custom product attribute'
);
for ( let i = 0; i < 3; i++ ) { for ( let i = 0; i < 3; i++ ) {
await expect(page).toClick( 'button.add_attribute', {text: 'Add'} ); await expect( page ).toClick( 'button.add_attribute', {
text: 'Add',
} );
// Wait for attribute form to load // Wait for attribute form to load
await uiUnblocked(); await uiUnblocked();
await page.focus(`input[name="attribute_names[${i}]"]`); await page.focus( `input[name="attribute_names[${ i }]"]` );
await expect(page).toFill(`input[name="attribute_names[${i}]"]`, 'attr #' + (i + 1)); await expect( page ).toFill(
await expect(page).toFill(`textarea[name="attribute_values[${i}]"]`, 'val1 | val2'); `input[name="attribute_names[${ i }]"]`,
await waitAndClick( page, `input[name="attribute_variation[${i}]"]`); 'attr #' + ( i + 1 )
);
await expect( page ).toFill(
`textarea[name="attribute_values[${ i }]"]`,
'val1 | val2'
);
await waitAndClick(
page,
`input[name="attribute_variation[${ i }]"]`
);
} }
await expect(page).toClick( 'button', {text: 'Save attributes'}); await expect( page ).toClick( 'button', {
text: 'Save attributes',
} );
// Wait for attribute form to save (triggers 2 UI blocks) // Wait for attribute form to save (triggers 2 UI blocks)
await uiUnblocked(); await uiUnblocked();
await uiUnblocked(); await uiUnblocked();
}); } );
it('can create variations from all attributes', async () => { it( 'can create variations from all attributes', async () => {
// Create variations from attributes // Create variations from attributes
await waitForSelector( page, '.variations_tab' ); await waitForSelector( page, '.variations_tab' );
await waitAndClick( page, '.variations_tab a' ); await waitAndClick( page, '.variations_tab a' );
await selectVariationAction('Create variations from all attributes'); await selectVariationAction(
'Create variations from all attributes'
);
// headless: false doesn't require this // headless: false doesn't require this
const firstDialog = await expect( page ).toDisplayDialog( const firstDialog = await expect( page ).toDisplayDialog(
@ -190,24 +221,22 @@ const runAddVariableProductTest = () => {
} }
); );
await expect(firstDialog.message()).toMatch('Are you sure you want to link all variations?'); await expect( firstDialog.message() ).toMatch(
'Are you sure you want to link all variations?'
);
// Set some variation data // Set some variation data
await uiUnblocked(); await uiUnblocked();
await uiUnblocked(); await uiUnblocked();
}); } );
it('can add variation attributes', async () => { it( 'can add variation attributes', async () => {
await waitAndClick( page, '.variations_tab a' ); await waitAndClick( page, '.variations_tab a' );
await uiUnblocked(); await uiUnblocked();
await waitForSelector( await waitForSelector( page, 'select[name="attribute_attr-1[0]"]', {
page, visible: true,
'select[name="attribute_attr-1[0]"]', timeout: 5000,
{ } );
visible: true,
timeout: 5000
}
);
// Verify that variations were created // Verify that variations were created
for ( let index = 0; index < 8; index++ ) { for ( let index = 0; index < 8; index++ ) {
@ -215,34 +244,67 @@ const runAddVariableProductTest = () => {
const val2 = { text: 'val2' }; const val2 = { text: 'val2' };
// odd / even // odd / even
let attr3 = !! ( index % 2 ); const attr3 = !! ( index % 2 );
// 0-1,4-5 / 2-3,6-7 // 0-1,4-5 / 2-3,6-7
let attr2 = ( index % 4 ) > 1; const attr2 = index % 4 > 1;
// 0-3 / 4-7 // 0-3 / 4-7
let attr1 = ( index > 3 ); const attr1 = index > 3;
await expect( page ).toMatchElement( `select[name="attribute_attr-1[${index}]"]`, attr1 ? val2 : val1 ); await expect( page ).toMatchElement(
await expect( page ).toMatchElement( `select[name="attribute_attr-2[${index}]"]`, attr2 ? val2 : val1 ); `select[name="attribute_attr-1[${ index }]"]`,
await expect( page ).toMatchElement( `select[name="attribute_attr-3[${index}]"]`, attr3 ? val2 : val1 ); attr1 ? val2 : val1
);
await expect( page ).toMatchElement(
`select[name="attribute_attr-2[${ index }]"]`,
attr2 ? val2 : val1
);
await expect( page ).toMatchElement(
`select[name="attribute_attr-3[${ index }]"]`,
attr3 ? val2 : val1
);
} }
await expandVariations(); await expandVariations();
await waitForSelectorWithoutThrow( 'input[name="variable_is_virtual[0]"]', 5 ); await waitForSelectorWithoutThrow(
await setCheckbox('input[name="variable_is_virtual[0]"]'); 'input[name="variable_is_virtual[0]"]',
await expect(page).toFill('input[name="variable_regular_price[0]"]', '9.99'); 5
);
await setCheckbox( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toFill(
'input[name="variable_regular_price[0]"]',
'9.99'
);
await setCheckbox('input[name="variable_is_virtual[1]"]'); await setCheckbox( 'input[name="variable_is_virtual[1]"]' );
await expect(page).toFill('input[name="variable_regular_price[1]"]', '11.99'); await expect( page ).toFill(
'input[name="variable_regular_price[1]"]',
'11.99'
);
await setCheckbox('input[name="variable_manage_stock[2]"]'); await setCheckbox( 'input[name="variable_manage_stock[2]"]' );
await expect(page).toFill('input[name="variable_regular_price[2]"]', '20'); await expect( page ).toFill(
await expect(page).toFill('input[name="variable_weight[2]"]', '200'); 'input[name="variable_regular_price[2]"]',
await expect(page).toFill('input[name="variable_length[2]"]', '10'); '20'
await expect(page).toFill('input[name="variable_width[2]"]', '20'); );
await expect(page).toFill('input[name="variable_height[2]"]', '15'); await expect( page ).toFill(
'input[name="variable_weight[2]"]',
'200'
);
await expect( page ).toFill(
'input[name="variable_length[2]"]',
'10'
);
await expect( page ).toFill(
'input[name="variable_width[2]"]',
'20'
);
await expect( page ).toFill(
'input[name="variable_height[2]"]',
'15'
);
await saveChanges(); await saveChanges();
}); } );
it( 'can bulk-edit variations', async () => { it( 'can bulk-edit variations', async () => {
// Verify that all 'Downloadable' checkboxes are UNCHECKED. // Verify that all 'Downloadable' checkboxes are UNCHECKED.
@ -258,7 +320,7 @@ const runAddVariableProductTest = () => {
} }
// Perform the 'Toggle "Downloadable"' bulk action // Perform the 'Toggle "Downloadable"' bulk action
await selectVariationAction('Toggle "Downloadable"'); await selectVariationAction( 'Toggle "Downloadable"' );
await clickGoButton(); await clickGoButton();
await uiUnblocked(); await uiUnblocked();
@ -407,7 +469,7 @@ const runAddVariableProductTest = () => {
const variationsCount = await page.$$( '.woocommerce_variation' ); const variationsCount = await page.$$( '.woocommerce_variation' );
expect( variationsCount ).toHaveLength( 0 ); expect( variationsCount ).toHaveLength( 0 );
} ); } );
}); } );
}; };
module.exports = { module.exports = {

View File

@ -3,76 +3,88 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const { merchant, createSimpleProduct } = require( '@woocommerce/e2e-utils' );
merchant,
createSimpleProduct
} = require( '@woocommerce/e2e-utils' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const runProductSearchTest = () => { const runProductSearchTest = () => {
describe('Products > Search and View a product', () => { describe( 'Products > Search and View a product', () => {
beforeAll(async () => { beforeAll( async () => {
await createSimpleProduct(); await createSimpleProduct();
// Make sure the simple product name is greater than 1 to do a search // Make sure the simple product name is greater than 1 to do a search
await expect(simpleProductName.length).toBeGreaterThan(1); await expect( simpleProductName.length ).toBeGreaterThan( 1 );
await merchant.login(); await merchant.login();
}); } );
beforeEach(async () => { beforeEach( async () => {
await merchant.openAllProductsView(); await merchant.openAllProductsView();
}); } );
it('can do a partial search for a product', async () => { it( 'can do a partial search for a product', async () => {
// Create partial search string // Create partial search string
let searchString = simpleProductName.substring(0, (simpleProductName.length / 2)); const searchString = simpleProductName.substring(
await expect(page).toFill('#post-search-input', searchString); 0,
simpleProductName.length / 2
);
await expect( page ).toFill( '#post-search-input', searchString );
// Click search and wait for the page to finish loading // Click search and wait for the page to finish loading
await Promise.all( [ await Promise.all( [
page.click( '#search-submit' ), page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify we are getting the results back in the list // Verify we are getting the results back in the list
await expect(page).toMatchElement('.row-title', { text: simpleProductName }); await expect( page ).toMatchElement( '.row-title', {
}); text: simpleProductName,
} );
} );
it('can view a product\'s details after search', async () => { it( "can view a product's details after search", async () => {
await expect(page).toFill('#post-search-input', simpleProductName); await expect( page ).toFill(
'#post-search-input',
simpleProductName
);
await Promise.all( [ await Promise.all( [
page.click( '#search-submit' ), page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Click to view the product and wait for the page to finish loading // Click to view the product and wait for the page to finish loading
await Promise.all( [ await Promise.all( [
expect(page).toClick('.row-title', simpleProductName), expect( page ).toClick( '.row-title', simpleProductName ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
await expect(page).toMatchElement('#title', simpleProductName); await expect( page ).toMatchElement( '#title', simpleProductName );
await expect(page).toMatchElement('#_regular_price', simpleProductPrice); await expect( page ).toMatchElement(
}); '#_regular_price',
simpleProductPrice
);
} );
it('returns no results for non-existent product search', async () => { it( 'returns no results for non-existent product search', async () => {
await expect(page).toFill('#post-search-input', 'abcd1234'); await expect( page ).toFill( '#post-search-input', 'abcd1234' );
// Click search and wait for the page to finish loading // Click search and wait for the page to finish loading
await Promise.all( [ await Promise.all( [
page.click( '#search-submit' ), page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify we are getting the results back in the list // Verify we are getting the results back in the list
await expect(page).toMatchElement('.no-items', { text: 'No products found' }); await expect( page ).toMatchElement( '.no-items', {
}); text: 'No products found',
}); } );
} } );
} );
};
module.exports = runProductSearchTest; module.exports = runProductSearchTest;

View File

@ -11,74 +11,126 @@ const {
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const runUpdateGeneralSettingsTest = () => { const runUpdateGeneralSettingsTest = () => {
describe('WooCommerce General Settings', () => { describe( 'WooCommerce General Settings', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can update settings', async () => { it( 'can update settings', async () => {
// Go to general settings page // Go to general settings page
await merchant.openSettings('general'); await merchant.openSettings( 'general' );
// Make sure the general tab is active // Make sure the general tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'General'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'General',
} );
// Set selling location to all countries first, // Set selling location to all countries first,
// so we can choose california as base location. // so we can choose california as base location.
await expect(page).toSelect('#woocommerce_allowed_countries', 'Sell to all countries'); await expect( page ).toSelect(
'#woocommerce_allowed_countries',
'Sell to all countries'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to all countries'}), text: 'Your settings have been saved.',
]); } ),
expect( page ).toMatchElement(
'#woocommerce_allowed_countries',
{ text: 'Sell to all countries' }
),
] );
// Set base location with state CA. // Set base location with state CA.
await expect(page).toSelect('select[name="woocommerce_default_country"]', 'United States (US) — California'); await expect( page ).toSelect(
'select[name="woocommerce_default_country"]',
'United States (US) — California'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('select[name="woocommerce_default_country"]', {text: 'United States (US) — California'}), text: 'Your settings have been saved.',
]); } ),
expect(
page
).toMatchElement(
'select[name="woocommerce_default_country"]',
{ text: 'United States (US) — California' }
),
] );
// Set selling location to specific countries first, so we can choose U.S as base location (without state). // Set selling location to specific countries first, so we can choose U.S as base location (without state).
// This will makes specific countries option appears. // This will makes specific countries option appears.
await expect(page).toSelect('#woocommerce_allowed_countries', 'Sell to specific countries'); await expect( page ).toSelect(
await expect(page).toSelect('select[name="woocommerce_specific_allowed_countries[]"]', 'United States (US)'); '#woocommerce_allowed_countries',
'Sell to specific countries'
);
await expect( page ).toSelect(
'select[name="woocommerce_specific_allowed_countries[]"]',
'United States (US)'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to specific countries'}), text: 'Your settings have been saved.',
expect(page).toMatchElement('select[name="woocommerce_specific_allowed_countries[]"]', {text: 'United States (US)'}), } ),
]); expect( page ).toMatchElement(
'#woocommerce_allowed_countries',
{ text: 'Sell to specific countries' }
),
expect(
page
).toMatchElement(
'select[name="woocommerce_specific_allowed_countries[]"]',
{ text: 'United States (US)' }
),
] );
// Set currency options. // Set currency options.
await expect(page).toFill('#woocommerce_price_thousand_sep', ','); await expect( page ).toFill(
await expect(page).toFill('#woocommerce_price_decimal_sep', '.'); '#woocommerce_price_thousand_sep',
await expect(page).toFill('#woocommerce_price_num_decimals', '2'); ','
);
await expect( page ).toFill(
'#woocommerce_price_decimal_sep',
'.'
);
await expect( page ).toFill(
'#woocommerce_price_num_decimals',
'2'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
verifyValueOfInputField('#woocommerce_price_thousand_sep', ','), text: 'Your settings have been saved.',
verifyValueOfInputField('#woocommerce_price_decimal_sep', '.'), } ),
verifyValueOfInputField('#woocommerce_price_num_decimals', '2'), verifyValueOfInputField(
]); '#woocommerce_price_thousand_sep',
}); ','
}); ),
verifyValueOfInputField(
'#woocommerce_price_decimal_sep',
'.'
),
verifyValueOfInputField(
'#woocommerce_price_num_decimals',
'2'
),
] );
} );
} );
}; };
module.exports = runUpdateGeneralSettingsTest; module.exports = runUpdateGeneralSettingsTest;

View File

@ -12,65 +12,124 @@ const {
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const runProductSettingsTest = () => { const runProductSettingsTest = () => {
describe('WooCommerce Products > Downloadable Products Settings', () => { describe( 'WooCommerce Products > Downloadable Products Settings', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can update settings', async () => { it( 'can update settings', async () => {
// Go to downloadable products settings page // Go to downloadable products settings page
await merchant.openSettings('products', 'downloadable'); await merchant.openSettings( 'products', 'downloadable' );
// Make sure the product tab is active // Make sure the product tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'Products'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
await expect(page).toMatchElement('ul.subsubsub > li > a.current', {text: 'Downloadable products'}); text: 'Products',
} );
await expect( page ).toMatchElement(
'ul.subsubsub > li > a.current',
{
text: 'Downloadable products',
}
);
await expect(page).toSelect('#woocommerce_file_download_method', 'Redirect only (Insecure)'); await expect( page ).toSelect(
await setCheckbox('#woocommerce_downloads_require_login'); '#woocommerce_file_download_method',
await setCheckbox('#woocommerce_downloads_grant_access_after_payment'); 'Redirect only (Insecure)'
await setCheckbox('#woocommerce_downloads_redirect_fallback_allowed'); );
await unsetCheckbox('#woocommerce_downloads_add_hash_to_filename'); await setCheckbox( '#woocommerce_downloads_require_login' );
await setCheckbox(
'#woocommerce_downloads_grant_access_after_payment'
);
await setCheckbox(
'#woocommerce_downloads_redirect_fallback_allowed'
);
await unsetCheckbox(
'#woocommerce_downloads_add_hash_to_filename'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_file_download_method', {text: 'Redirect only (Insecure)'}), text: 'Your settings have been saved.',
verifyCheckboxIsSet('#woocommerce_downloads_require_login'), } ),
verifyCheckboxIsSet('#woocommerce_downloads_grant_access_after_payment'), expect( page ).toMatchElement(
verifyCheckboxIsSet('#woocommerce_downloads_redirect_fallback_allowed'), '#woocommerce_file_download_method',
verifyCheckboxIsUnset('#woocommerce_downloads_add_hash_to_filename') {
]); text: 'Redirect only (Insecure)',
}
),
verifyCheckboxIsSet( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsSet(
'#woocommerce_downloads_grant_access_after_payment'
),
verifyCheckboxIsSet(
'#woocommerce_downloads_redirect_fallback_allowed'
),
verifyCheckboxIsUnset(
'#woocommerce_downloads_add_hash_to_filename'
),
] );
await page.reload(); await page.reload();
await expect(page).toSelect('#woocommerce_file_download_method', 'X-Accel-Redirect/X-Sendfile'); await expect( page ).toSelect(
await unsetCheckbox('#woocommerce_downloads_require_login'); '#woocommerce_file_download_method',
await unsetCheckbox('#woocommerce_downloads_grant_access_after_payment'); 'X-Accel-Redirect/X-Sendfile'
await unsetCheckbox('#woocommerce_downloads_redirect_fallback_allowed'); );
await setCheckbox('#woocommerce_downloads_add_hash_to_filename'); await unsetCheckbox( '#woocommerce_downloads_require_login' );
await unsetCheckbox(
'#woocommerce_downloads_grant_access_after_payment'
);
await unsetCheckbox(
'#woocommerce_downloads_redirect_fallback_allowed'
);
await setCheckbox( '#woocommerce_downloads_add_hash_to_filename' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_file_download_method', {text: 'X-Accel-Redirect/X-Sendfile'}), text: 'Your settings have been saved.',
verifyCheckboxIsUnset('#woocommerce_downloads_require_login'), } ),
verifyCheckboxIsUnset('#woocommerce_downloads_grant_access_after_payment'), expect( page ).toMatchElement(
verifyCheckboxIsUnset('#woocommerce_downloads_redirect_fallback_allowed'), '#woocommerce_file_download_method',
verifyCheckboxIsSet('#woocommerce_downloads_add_hash_to_filename') {
]); text: 'X-Accel-Redirect/X-Sendfile',
}
),
verifyCheckboxIsUnset( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsUnset(
'#woocommerce_downloads_grant_access_after_payment'
),
verifyCheckboxIsUnset(
'#woocommerce_downloads_redirect_fallback_allowed'
),
verifyCheckboxIsSet(
'#woocommerce_downloads_add_hash_to_filename'
),
] );
await page.reload(); await page.reload();
await expect(page).toSelect('#woocommerce_file_download_method', 'Force downloads'); await expect( page ).toSelect(
'#woocommerce_file_download_method',
'Force downloads'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_file_download_method', {text: 'Force downloads'}) text: 'Your settings have been saved.',
]); } ),
}); expect( page ).toMatchElement(
}); '#woocommerce_file_download_method',
{
text: 'Force downloads',
}
),
] );
} );
} );
}; };
module.exports = runProductSettingsTest; module.exports = runProductSettingsTest;

View File

@ -1,72 +1,76 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { merchant, withRestApi } = require('@woocommerce/e2e-utils'); const { merchant, withRestApi } = require( '@woocommerce/e2e-utils' );
const runAddShippingClassesTest = () => { const runAddShippingClassesTest = () => {
describe('Merchant can add shipping classes', () => { describe( 'Merchant can add shipping classes', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
// Go to Shipping Classes page // Go to Shipping Classes page
await merchant.openSettings('shipping', 'classes'); await merchant.openSettings( 'shipping', 'classes' );
}); } );
afterAll(async () => { afterAll( async () => {
await withRestApi.deleteAllShippingClasses( false ); await withRestApi.deleteAllShippingClasses( false );
}); } );
it('can add shipping classes', async () => { it( 'can add shipping classes', async () => {
const shippingClassSlug = { const shippingClassSlug = {
name: 'Small Items', name: 'Small Items',
slug: 'small-items', slug: 'small-items',
description: 'Small items that don\'t cost much to ship.' description: "Small items that don't cost much to ship.",
}; };
const shippingClassNoSlug = { const shippingClassNoSlug = {
name: 'Poster Pack', name: 'Poster Pack',
slug: '', slug: '',
description: '' description: '',
}; };
const shippingClasses = [shippingClassSlug, shippingClassNoSlug]; const shippingClasses = [ shippingClassSlug, shippingClassNoSlug ];
// Add shipping classes // Add shipping classes
for (const { name, slug, description } of shippingClasses) { for ( const { name, slug, description } of shippingClasses ) {
await expect(page).toClick('.wc-shipping-class-add'); await expect( page ).toClick( '.wc-shipping-class-add' );
await expect(page).toFill( await expect( page ).toFill(
'.editing:last-child [data-attribute="name"]', '.editing:last-child [data-attribute="name"]',
name name
); );
await expect(page).toFill( await expect( page ).toFill(
'.editing:last-child [data-attribute="slug"]', '.editing:last-child [data-attribute="slug"]',
slug slug
); );
await expect(page).toFill( await expect( page ).toFill(
'.editing:last-child [data-attribute="description"]', '.editing:last-child [data-attribute="description"]',
description description
); );
} }
await expect(page).toClick('.wc-shipping-class-save'); await expect( page ).toClick( '.wc-shipping-class-save' );
// Set the expected auto-generated slug // Set the expected auto-generated slug
shippingClassNoSlug.slug = 'poster-pack'; shippingClassNoSlug.slug = 'poster-pack';
// Verify that the specified shipping classes were saved // Verify that the specified shipping classes were saved
for (const { name, slug, description } of shippingClasses) { for ( const { name, slug, description } of shippingClasses ) {
const row = await expect( const row = await expect( page ).toMatchElement(
page '.wc-shipping-class-rows tr',
).toMatchElement('.wc-shipping-class-rows tr', { text: slug, timeout: 50000 }); {
text: slug,
timeout: 50000,
}
);
await expect(row).toMatchElement( await expect( row ).toMatchElement(
'.wc-shipping-class-name', '.wc-shipping-class-name',
name name
); );
await expect(row).toMatchElement( await expect( row ).toMatchElement(
'.wc-shipping-class-description', '.wc-shipping-class-description',
description description
); );
} }
}); } );
}); } );
}; };
module.exports = runAddShippingClassesTest; module.exports = runAddShippingClassesTest;

View File

@ -15,14 +15,12 @@ const {
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductPrice = config.has( 'products.simple.price' ) ? config.get( 'products.simple.price' ) : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const california = 'state:US:CA'; const california = 'state:US:CA';
const sanFranciscoZIP = '94107'; const sanFranciscoZIP = '94107';
const shippingZoneNameUS = 'US with Flat rate'; const shippingZoneNameUS = 'US with Flat rate';
@ -30,41 +28,56 @@ const shippingZoneNameFL = 'CA with Free shipping';
const shippingZoneNameSF = 'SF with Local pickup'; const shippingZoneNameSF = 'SF with Local pickup';
const runAddNewShippingZoneTest = () => { const runAddNewShippingZoneTest = () => {
describe('WooCommerce Shipping Settings - Add new shipping zone', () => { describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await withRestApi.deleteAllShippingZones( false ); await withRestApi.deleteAllShippingZones( false );
await merchant.login(); await merchant.login();
}); } );
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('add shipping zone for San Francisco with free Local pickup', async () => { it( 'add shipping zone for San Francisco with free Local pickup', async () => {
// Add a new shipping zone for San Francisco 94107, CA, US with Local pickup // Add a new shipping zone for San Francisco 94107, CA, US with Local pickup
await addShippingZoneAndMethod(shippingZoneNameSF, california, sanFranciscoZIP, 'local_pickup'); await addShippingZoneAndMethod(
}); shippingZoneNameSF,
california,
sanFranciscoZIP,
'local_pickup'
);
} );
it('add shipping zone for California with Free shipping', async () => { it( 'add shipping zone for California with Free shipping', async () => {
// Add a new shipping zone for CA, US with Free shipping // Add a new shipping zone for CA, US with Free shipping
await addShippingZoneAndMethod(shippingZoneNameFL, california, ' ', 'free_shipping'); await addShippingZoneAndMethod(
}); shippingZoneNameFL,
california,
' ',
'free_shipping'
);
} );
it('add shipping zone for the US with Flat rate', async () => { it( 'add shipping zone for the US with Flat rate', async () => {
// Add a new shipping zone for the US with Flat rate // Add a new shipping zone for the US with Flat rate
await addShippingZoneAndMethod(shippingZoneNameUS); await addShippingZoneAndMethod( shippingZoneNameUS );
// Set Flat rate cost // Set Flat rate cost
await expect(page).toClick('a.wc-shipping-zone-method-settings', {text: 'Flat rate'}); await expect( page ).toClick(
await clearAndFillInput('#woocommerce_flat_rate_cost', '10'); 'a.wc-shipping-zone-method-settings',
await expect(page).toClick('.wc-backbone-modal-main button#btn-ok'); { text: 'Flat rate' }
);
await clearAndFillInput( '#woocommerce_flat_rate_cost', '10' );
await expect( page ).toClick(
'.wc-backbone-modal-main button#btn-ok'
);
await merchant.logout(); await merchant.logout();
}); } );
it('allows customer to pay for a Flat rate shipping method', async() => { it( 'allows customer to pay for a Flat rate shipping method', async () => {
await shopper.login(); await shopper.login();
// Add product to cart as a shopper // Add product to cart as a shopper
@ -73,61 +86,85 @@ const runAddNewShippingZoneTest = () => {
await shopper.goToCart(); await shopper.goToCart();
// Set shipping country to United States (US) // Set shipping country to United States (US)
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await expect(page).toClick('#select2-calc_shipping_country-container'); await expect( page ).toClick(
await selectOptionInSelect2('United States (US)'); '#select2-calc_shipping_country-container'
);
await selectOptionInSelect2( 'United States (US)' );
// Set shipping state to New York // Set shipping state to New York
await expect(page).toClick('#select2-calc_shipping_state-container'); await expect( page ).toClick(
await selectOptionInSelect2('New York'); '#select2-calc_shipping_state-container'
await expect(page).toClick('button[name="calc_shipping"]'); );
await selectOptionInSelect2( 'New York' );
await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping costs // Verify shipping costs
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping .amount', {text: '$10.00'}); await expect( page ).toMatchElement( '.shipping .amount', {
await expect(page).toMatchElement('.order-total .amount', {text: `$1${simpleProductPrice}`}); text: '$10.00',
}); } );
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$1${ simpleProductPrice }`,
} );
} );
it('allows customer to benefit from a Free shipping if in CA', async () => { it( 'allows customer to benefit from a Free shipping if in CA', async () => {
await page.reload(); await page.reload();
// Set shipping state to California // Set shipping state to California
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await expect(page).toClick('#select2-calc_shipping_state-container'); await expect( page ).toClick(
await selectOptionInSelect2('California'); '#select2-calc_shipping_state-container'
);
await selectOptionInSelect2( 'California' );
// Set shipping postcode to 94000 // Set shipping postcode to 94000
await clearAndFillInput('#calc_shipping_postcode', '94000'); await clearAndFillInput( '#calc_shipping_postcode', '94000' );
await expect(page).toClick('button[name="calc_shipping"]'); await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping method and cost // Verify shipping method and cost
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping ul#shipping_method > li', {text: 'Free shipping'}); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.order-total .amount', {text: `$${simpleProductPrice}`}); '.shipping ul#shipping_method > li',
}); {
text: 'Free shipping',
}
);
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$${ simpleProductPrice }`,
} );
} );
it('allows customer to benefit from a free Local pickup if in SF', async () => { it( 'allows customer to benefit from a free Local pickup if in SF', async () => {
await page.reload(); await page.reload();
// Set shipping postcode to 94107 // Set shipping postcode to 94107
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await clearAndFillInput('#calc_shipping_postcode', '94107'); await clearAndFillInput( '#calc_shipping_postcode', '94107' );
await expect(page).toClick('button[name="calc_shipping"]'); await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping method and cost // Verify shipping method and cost
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping ul#shipping_method > li', {text: 'Local pickup'}); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.order-total .amount', {text: `$${simpleProductPrice}`}); '.shipping ul#shipping_method > li',
{
text: 'Local pickup',
}
);
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$${ simpleProductPrice }`,
} );
await shopper.removeFromCart( productId ); await shopper.removeFromCart( productId );
}); } );
}); } );
}; };
module.exports = runAddNewShippingZoneTest; module.exports = runAddNewShippingZoneTest;

View File

@ -14,165 +14,254 @@ const {
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const runTaxSettingsTest = () => { const runTaxSettingsTest = () => {
describe('WooCommerce Tax Settings', () => { describe( 'WooCommerce Tax Settings', () => {
beforeAll(async () => { beforeAll( async () => {
await merchant.login(); await merchant.login();
}); } );
it('can enable tax calculation', async () => { it( 'can enable tax calculation', async () => {
// Go to general settings page // Go to general settings page
await merchant.openSettings('general'); await merchant.openSettings( 'general' );
// Make sure the general tab is active // Make sure the general tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'General'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'General',
} );
// Enable tax calculation // Enable tax calculation
await setCheckbox('input[name="woocommerce_calc_taxes"]'); await setCheckbox( 'input[name="woocommerce_calc_taxes"]' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
verifyCheckboxIsSet('#woocommerce_calc_taxes'), text: 'Your settings have been saved.',
]); } ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
// Verify that tax settings are now present // Verify that tax settings are now present
await expect(page).toMatchElement('a.nav-tab', {text: 'Tax'}); await expect( page ).toMatchElement( 'a.nav-tab', { text: 'Tax' } );
}); } );
it('can set tax options', async () => { it( 'can set tax options', async () => {
// Go to tax settings page // Go to tax settings page
await merchant.openSettings('tax'); await merchant.openSettings( 'tax' );
// Make sure the tax tab is active // Make sure the tax tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'Tax'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'Tax',
} );
// Prices exclusive of tax // Prices exclusive of tax
await expect(page).toClick('input[name="woocommerce_prices_include_tax"][value="no"]'); await expect( page ).toClick(
'input[name="woocommerce_prices_include_tax"][value="no"]'
);
// Tax based on customer shipping address // Tax based on customer shipping address
await expect(page).toSelect('#woocommerce_tax_based_on', 'Customer shipping address'); await expect( page ).toSelect(
'#woocommerce_tax_based_on',
'Customer shipping address'
);
// Standard tax class for shipping // Standard tax class for shipping
await expect(page).toSelect('#woocommerce_shipping_tax_class', 'Standard'); await expect( page ).toSelect(
'#woocommerce_shipping_tax_class',
'Standard'
);
// Leave rounding unchecked (no-op) // Leave rounding unchecked (no-op)
// Display prices excluding tax // Display prices excluding tax
await expect(page).toSelect('#woocommerce_tax_display_shop', 'Excluding tax'); await expect( page ).toSelect(
'#woocommerce_tax_display_shop',
'Excluding tax'
);
// Display prices including tax in cart and at checkout // Display prices including tax in cart and at checkout
await expect(page).toSelect('#woocommerce_tax_display_cart', 'Including tax'); await expect( page ).toSelect(
'#woocommerce_tax_display_cart',
'Including tax'
);
// Display a single tax total // Display a single tax total
await expect(page).toSelect('#woocommerce_tax_total_display', 'As a single total'); await expect( page ).toSelect(
'#woocommerce_tax_total_display',
'As a single total'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
verifyValueOfInputField('input[name="woocommerce_prices_include_tax"][value="no"]', 'no'), text: 'Your settings have been saved.',
expect(page).toMatchElement('#woocommerce_tax_based_on', {text: 'Customer shipping address'}), } ),
expect(page).toMatchElement('#woocommerce_shipping_tax_class', {text: 'Standard'}), verifyValueOfInputField(
expect(page).toMatchElement('#woocommerce_tax_display_shop', {text: 'Excluding tax'}), 'input[name="woocommerce_prices_include_tax"][value="no"]',
expect(page).toMatchElement('#woocommerce_tax_display_cart', {text: 'Including tax'}), 'no'
expect(page).toMatchElement('#woocommerce_tax_total_display', {text: 'As a single total'}), ),
]); expect( page ).toMatchElement( '#woocommerce_tax_based_on', {
}); text: 'Customer shipping address',
} ),
expect( page ).toMatchElement(
'#woocommerce_shipping_tax_class',
{
text: 'Standard',
}
),
expect( page ).toMatchElement(
'#woocommerce_tax_display_shop',
{ text: 'Excluding tax' }
),
expect( page ).toMatchElement(
'#woocommerce_tax_display_cart',
{ text: 'Including tax' }
),
expect( page ).toMatchElement(
'#woocommerce_tax_total_display',
{ text: 'As a single total' }
),
] );
} );
it('can add tax classes', async () => { it( 'can add tax classes', async () => {
// Go to tax settings page // Go to tax settings page
await merchant.openSettings('tax'); await merchant.openSettings( 'tax' );
// Make sure the tax tab is active // Make sure the tax tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'Tax'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'Tax',
} );
// Remove additional tax classes // Remove additional tax classes
await clearAndFillInput('#woocommerce_tax_classes', ''); await clearAndFillInput( '#woocommerce_tax_classes', '' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('#woocommerce_tax_classes', {text: ''}), text: 'Your settings have been saved.',
]); } ),
expect( page ).toMatchElement( '#woocommerce_tax_classes', {
text: '',
} ),
] );
// Add a "fancy" tax class // Add a "fancy" tax class
await clearAndFillInput('#woocommerce_tax_classes', 'Fancy'); await clearAndFillInput( '#woocommerce_tax_classes', 'Fancy' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).toMatchElement('ul.subsubsub > li > a', {text: 'Fancy rates'}), text: 'Your settings have been saved.',
]); } ),
}); expect( page ).toMatchElement( 'ul.subsubsub > li > a', {
text: 'Fancy rates',
} ),
] );
} );
it('can set rate settings', async () => { it( 'can set rate settings', async () => {
// Go to "fancy" rates tax settings page // Go to "fancy" rates tax settings page
await merchant.openSettings('tax', 'fancy'); await merchant.openSettings( 'tax', 'fancy' );
// Make sure the tax tab is active, with the "fancy" subsection // Make sure the tax tab is active, with the "fancy" subsection
await expect(page).toMatchElement('a.nav-tab-active', {text: 'Tax'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
await expect(page).toMatchElement('ul.subsubsub > li > a.current', {text: 'Fancy rates'}); text: 'Tax',
} );
await expect( page ).toMatchElement(
'ul.subsubsub > li > a.current',
{
text: 'Fancy rates',
}
);
// Create a state tax // Create a state tax
await expect(page).toClick('.wc_tax_rates a.insert'); await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect(page).toFill('input[name^="tax_rate_country[new-0"]', 'US'); await expect( page ).toFill(
await expect(page).toFill('input[name^="tax_rate_state[new-0"]', 'CA'); 'input[name^="tax_rate_country[new-0"]',
await expect(page).toFill('input[name^="tax_rate[new-0"]', '7.5'); 'US'
await expect(page).toFill('input[name^="tax_rate_name[new-0"]', 'CA State Tax'); );
await expect( page ).toFill(
'input[name^="tax_rate_state[new-0"]',
'CA'
);
await expect( page ).toFill(
'input[name^="tax_rate[new-0"]',
'7.5'
);
await expect( page ).toFill(
'input[name^="tax_rate_name[new-0"]',
'CA State Tax'
);
// Create a federal tax // Create a federal tax
await expect(page).toClick('.wc_tax_rates a.insert'); await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect(page).toFill('input[name^="tax_rate_country[new-1"]', 'US'); await expect( page ).toFill(
await expect(page).toFill('input[name^="tax_rate[new-1"]', '1.5'); 'input[name^="tax_rate_country[new-1"]',
await expect(page).toFill('input[name^="tax_rate_priority[new-1"]', '2'); 'US'
await expect(page).toFill('input[name^="tax_rate_name[new-1"]', 'Federal Tax'); );
await expect(page).toClick('input[name^="tax_rate_shipping[new-1"]'); await expect( page ).toFill(
'input[name^="tax_rate[new-1"]',
'1.5'
);
await expect( page ).toFill(
'input[name^="tax_rate_priority[new-1"]',
'2'
);
await expect( page ).toFill(
'input[name^="tax_rate_name[new-1"]',
'Federal Tax'
);
await expect( page ).toClick(
'input[name^="tax_rate_shipping[new-1"]'
);
// Save changes (AJAX here) // Save changes (AJAX here)
await expect(page).toClick('button.woocommerce-save-button'); await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked(); await uiUnblocked();
// Verify 2 tax rates // Verify 2 tax rates
expect(await page.$$('#rates tr')).toHaveLength(2); expect( await page.$$( '#rates tr' ) ).toHaveLength( 2 );
// Delete federal rate // Delete federal rate
await expect(page).toClick('#rates tr:nth-child(2) input'); await expect( page ).toClick( '#rates tr:nth-child(2) input' );
await expect(page).toClick('.wc_tax_rates a.remove_tax_rates'); await expect( page ).toClick( '.wc_tax_rates a.remove_tax_rates' );
// Save changes (AJAX here) // Save changes (AJAX here)
await expect(page).toClick('button.woocommerce-save-button'); await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked(); await uiUnblocked();
// Verify 1 rate // Verify 1 rate
expect(await page.$$('#rates tr')).toHaveLength(1); expect( await page.$$( '#rates tr' ) ).toHaveLength( 1 );
await expect(page).toMatchElement( await expect( page ).toMatchElement(
'#rates tr:first-of-type input[name^="tax_rate_state"][value="CA"]' '#rates tr:first-of-type input[name^="tax_rate_state"][value="CA"]'
); );
}); } );
it('can remove tax classes', async () => { it( 'can remove tax classes', async () => {
// Go to tax settings page // Go to tax settings page
await merchant.openSettings('tax'); await merchant.openSettings( 'tax' );
// Make sure the tax tab is active // Make sure the tax tab is active
await expect(page).toMatchElement('a.nav-tab-active', {text: 'Tax'}); await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'Tax',
} );
// Remove "fancy" tax class // Remove "fancy" tax class
await clearAndFillInput('#woocommerce_tax_classes', ' '); await clearAndFillInput( '#woocommerce_tax_classes', ' ' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Verify that settings have been saved // Verify that settings have been saved
await Promise.all([ await Promise.all( [
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), expect( page ).toMatchElement( '#message', {
expect(page).not.toMatchElement('ul.subsubsub > li > a', {text: 'Fancy rates'}), text: 'Your settings have been saved.',
]); } ),
}); expect( page ).not.toMatchElement( 'ul.subsubsub > li > a', {
}); text: 'Fancy rates',
} ),
] );
} );
} );
}; };
module.exports = runTaxSettingsTest; module.exports = runTaxSettingsTest;

View File

@ -15,13 +15,15 @@ const {
const { it, describe, beforeAll } = require( '@jest/globals' ); const { it, describe, beforeAll } = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const firstProductPrice = config.has( 'products.simple.price' ) ? config.get( 'products.simple.price' ) : '9.99'; const firstProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const secondProductPrice = '4.99'; const secondProductPrice = '4.99';
const fourProductPrice = firstProductPrice * 4; const fourProductPrice = firstProductPrice * 4;
var twoProductsPrice = (+firstProductPrice) + (+secondProductPrice); const twoProductsPrice = +firstProductPrice + +secondProductPrice;
var firstProductPriceWithFlatRate = (+firstProductPrice) + (+5); const firstProductPriceWithFlatRate = +firstProductPrice + +5;
var fourProductPriceWithFlatRate = (+fourProductPrice) + (+5); const fourProductPriceWithFlatRate = +fourProductPrice + +5;
var twoProductsPriceWithFlatRate = (+twoProductsPrice) + (+5); const twoProductsPriceWithFlatRate = +twoProductsPrice + +5;
const firstProductName = 'First Product'; const firstProductName = 'First Product';
const secondProductName = 'Second Product'; const secondProductName = 'Second Product';
const shippingZoneNameDE = 'Germany Free Shipping'; const shippingZoneNameDE = 'Germany Free Shipping';
@ -30,98 +32,146 @@ const shippingZoneNameFR = 'France Flat Local';
const shippingCountryFR = 'country:FR'; const shippingCountryFR = 'country:FR';
const runCartCalculateShippingTest = () => { const runCartCalculateShippingTest = () => {
describe('Cart Calculate Shipping', () => { describe( 'Cart Calculate Shipping', () => {
let firstProductId; let firstProductId;
let secondProductId; let secondProductId;
beforeAll(async () => { beforeAll( async () => {
firstProductId = await createSimpleProduct(firstProductName); firstProductId = await createSimpleProduct( firstProductName );
secondProductId = await createSimpleProduct(secondProductName, secondProductPrice); secondProductId = await createSimpleProduct(
secondProductName,
secondProductPrice
);
await withRestApi.resetSettingsGroupToDefault( 'general', false ); await withRestApi.resetSettingsGroupToDefault( 'general', false );
// Add a new shipping zone Germany with Free shipping // Add a new shipping zone Germany with Free shipping
await withRestApi.addShippingZoneAndMethod(shippingZoneNameDE, shippingCountryDE, ' ', 'free_shipping', '', [], false ); await withRestApi.addShippingZoneAndMethod(
shippingZoneNameDE,
shippingCountryDE,
' ',
'free_shipping',
'',
[],
false
);
// Add a new shipping zone for France with Flat rate & Local pickup // Add a new shipping zone for France with Flat rate & Local pickup
await withRestApi.addShippingZoneAndMethod(shippingZoneNameFR, shippingCountryFR, ' ', 'flat_rate', '5', ['local_pickup'], false ); await withRestApi.addShippingZoneAndMethod(
shippingZoneNameFR,
shippingCountryFR,
' ',
'flat_rate',
'5',
[ 'local_pickup' ],
false
);
await shopper.emptyCart(); await shopper.emptyCart();
}); } );
afterAll(async () => { afterAll( async () => {
await withRestApi.deleteAllShippingZones( false ); await withRestApi.deleteAllShippingZones( false );
}); } );
it('allows customer to calculate Free Shipping if in Germany', async () => { it( 'allows customer to calculate Free Shipping if in Germany', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( firstProductId ); await shopper.addToCartFromShopPage( firstProductId );
await shopper.goToCart(); await shopper.goToCart();
// Set shipping country to Germany // Set shipping country to Germany
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await expect(page).toClick('#select2-calc_shipping_country-container'); await expect( page ).toClick(
await selectOptionInSelect2('Germany'); '#select2-calc_shipping_country-container'
await expect(page).toClick('button[name="calc_shipping"]'); );
await selectOptionInSelect2( 'Germany' );
await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping costs // Verify shipping costs
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping ul#shipping_method > li', {text: 'Free shipping'}); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.order-total .amount', {text: `$${firstProductPrice}`}); '.shipping ul#shipping_method > li',
}); {
text: 'Free shipping',
}
);
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$${ firstProductPrice }`,
} );
} );
it('allows customer to calculate Flat rate and Local pickup if in France', async () => { it( 'allows customer to calculate Flat rate and Local pickup if in France', async () => {
await page.reload( { waitUntil: ['networkidle0', 'domcontentloaded'] } ); await page.reload( {
waitUntil: [ 'networkidle0', 'domcontentloaded' ],
} );
// Set shipping country to France // Set shipping country to France
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await expect(page).toClick('#select2-calc_shipping_country-container'); await expect( page ).toClick(
await selectOptionInSelect2('France'); '#select2-calc_shipping_country-container'
await expect(page).toClick('button[name="calc_shipping"]'); );
await selectOptionInSelect2( 'France' );
await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping costs // Verify shipping costs
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping .amount', {text: '$5.00'}); await expect( page ).toMatchElement( '.shipping .amount', {
await expect(page).toMatchElement('.order-total .amount', {text: `$${firstProductPriceWithFlatRate}`}); text: '$5.00',
}); } );
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$${ firstProductPriceWithFlatRate }`,
} );
} );
it('should show correct total cart price after updating quantity', async () => { it( 'should show correct total cart price after updating quantity', async () => {
await shopper.setCartQuantity(firstProductName, 4); await shopper.setCartQuantity( firstProductName, 4 );
await expect(page).toClick('button', {text: 'Update cart'}); await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.order-total .amount', {text: `$${fourProductPriceWithFlatRate}`}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: `$${ fourProductPriceWithFlatRate }`,
} );
} );
it('should show correct total cart price with 2 products and flat rate', async () => { it( 'should show correct total cart price with 2 products and flat rate', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( secondProductId ); await shopper.addToCartFromShopPage( secondProductId );
await shopper.goToCart(); await shopper.goToCart();
await shopper.setCartQuantity(firstProductName, 1); await shopper.setCartQuantity( firstProductName, 1 );
await expect(page).toClick('button', {text: 'Update cart'}); await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked(); await uiUnblocked();
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.shipping .amount', {text: '$5.00'}); await expect( page ).toMatchElement( '.shipping .amount', {
await expect(page).toMatchElement('.order-total .amount', {text: `$${twoProductsPriceWithFlatRate}`}); text: '$5.00',
}); } );
await expect( page ).toMatchElement( '.order-total .amount', {
text: `$${ twoProductsPriceWithFlatRate }`,
} );
} );
it('should show correct total cart price with 2 products without flat rate', async () => { it( 'should show correct total cart price with 2 products without flat rate', async () => {
await page.reload( { waitUntil: ['networkidle0', 'domcontentloaded'] } ); await page.reload( {
waitUntil: [ 'networkidle0', 'domcontentloaded' ],
} );
// Set shipping country to Spain // Set shipping country to Spain
await expect(page).toClick('a.shipping-calculator-button'); await expect( page ).toClick( 'a.shipping-calculator-button' );
await expect(page).toClick('#select2-calc_shipping_country-container'); await expect( page ).toClick(
await selectOptionInSelect2('Spain'); '#select2-calc_shipping_country-container'
await expect(page).toClick('button[name="calc_shipping"]'); );
await selectOptionInSelect2( 'Spain' );
await expect( page ).toClick( 'button[name="calc_shipping"]' );
await uiUnblocked(); await uiUnblocked();
// Verify shipping costs // Verify shipping costs
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.order-total .amount', {text: `$${twoProductsPrice}`}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: `$${ twoProductsPrice }`,
}); } );
} );
} );
}; };
module.exports = runCartCalculateShippingTest; module.exports = runCartCalculateShippingTest;

View File

@ -16,56 +16,79 @@ const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
const { it, describe, beforeAll } = require( '@jest/globals' ); const { it, describe, beforeAll } = require( '@jest/globals' );
const runCartApplyCouponsTest = () => { const runCartApplyCouponsTest = () => {
describe('Cart applying coupons', () => { describe( 'Cart applying coupons', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await shopper.emptyCart(); await shopper.emptyCart();
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await uiUnblocked(); await uiUnblocked();
await shopper.goToCart(); await shopper.goToCart();
}); } );
it.each( getCouponsTable() )( 'allows cart to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => { it.each( getCouponsTable() )(
const coupon = await getCouponId( couponType ); 'allows cart to apply %s coupon',
await applyCoupon(coupon); async ( couponType, cartDiscount, orderTotal ) => {
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' }); const coupon = await getCouponId( couponType );
await applyCoupon( coupon );
await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
// Verify discount applied and order total // Verify discount applied and order total
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.cart-discount .amount', cartDiscount); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.order-total .amount', orderTotal); '.cart-discount .amount',
await removeCoupon(coupon); cartDiscount
}); );
await expect( page ).toMatchElement(
'.order-total .amount',
orderTotal
);
await removeCoupon( coupon );
}
);
it('prevents cart applying same coupon twice', async () => { it( 'prevents cart applying same coupon twice', async () => {
const couponId = await getCouponId( 'fixed cart' ); const couponId = await getCouponId( 'fixed cart' );
await applyCoupon( couponId ); await applyCoupon( couponId );
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
await applyCoupon( couponId ); await applyCoupon( couponId );
// Verify only one discount applied // Verify only one discount applied
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied' // This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); await expect( page ).toMatchElement( '.cart-discount .amount', {
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); text: '$5.00',
}); } );
await expect( page ).toMatchElement( '.order-total .amount', {
text: '$4.99',
} );
} );
it('allows cart to apply multiple coupons', async () => { it( 'allows cart to apply multiple coupons', async () => {
await applyCoupon( await getCouponId( 'fixed product' ) ); await applyCoupon( await getCouponId( 'fixed product' ) );
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
// Verify discount applied and order total // Verify discount applied and order total
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: '$0.00',
} );
} );
it('restores cart total when coupons are removed', async () => { it( 'restores cart total when coupons are removed', async () => {
await removeCoupon( await getCouponId( 'fixed cart' ) ); await removeCoupon( await getCouponId( 'fixed cart' ) );
await removeCoupon( await getCouponId( 'fixed product' ) ); await removeCoupon( await getCouponId( 'fixed product' ) );
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: '$9.99',
}); } );
} );
} );
}; };
module.exports = runCartApplyCouponsTest; module.exports = runCartApplyCouponsTest;

View File

@ -1,7 +1,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
merchant, merchant,
createSimpleProduct, createSimpleProduct,
@ -14,63 +14,59 @@
/** /**
* External dependencies * External dependencies
*/ */
const { const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
it,
describe,
beforeAll,
afterAll,
} = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const runCartRedirectionTest = () => { const runCartRedirectionTest = () => {
describe('Cart > Redirect to cart from shop', () => { describe( 'Cart > Redirect to cart from shop', () => {
let simplePostIdValue; let simplePostIdValue;
beforeAll(async () => { beforeAll( async () => {
simplePostIdValue = await createSimpleProduct(); simplePostIdValue = await createSimpleProduct();
// Set checkbox in settings to enable cart redirection // Set checkbox in settings to enable cart redirection
await merchant.login(); await merchant.login();
await merchant.openSettings('products'); await merchant.openSettings( 'products' );
await setCheckbox('#woocommerce_cart_redirect_after_add'); await setCheckbox( '#woocommerce_cart_redirect_after_add' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
await merchant.logout(); await merchant.logout();
}); } );
it('can redirect user to cart from shop page', async () => { it( 'can redirect user to cart from shop page', async () => {
await shopper.goToShop(); await shopper.goToShop();
// Add to cart from shop page // Add to cart from shop page
const addToCartXPath = `//li[contains(@class, "type-product") and a/h2[contains(text(), "${ simpleProductName }")]]` + const addToCartXPath =
'//a[contains(@class, "add_to_cart_button") and contains(@class, "ajax_add_to_cart")'; `//li[contains(@class, "type-product") and a/h2[contains(text(), "${ simpleProductName }")]]` +
'//a[contains(@class, "add_to_cart_button") and contains(@class, "ajax_add_to_cart")';
const [ addToCartButton ] = await page.$x( addToCartXPath + ']' ); const [ addToCartButton ] = await page.$x( addToCartXPath + ']' );
addToCartButton.click(); addToCartButton.click();
await utils.waitForTimeout( 1000 ); // to avoid flakiness await utils.waitForTimeout( 1000 ); // to avoid flakiness
await shopper.productIsInCart(simpleProductName); await shopper.productIsInCart( simpleProductName );
await shopper.removeFromCart( simplePostIdValue ); await shopper.removeFromCart( simplePostIdValue );
}); } );
it('can redirect user to cart from detail page', async () => { it( 'can redirect user to cart from detail page', async () => {
await shopper.goToProduct(simplePostIdValue); await shopper.goToProduct( simplePostIdValue );
// Add to cart from detail page // Add to cart from detail page
await shopper.addToCart(); await shopper.addToCart();
await utils.waitForTimeout( 1000 ); // to avoid flakiness await utils.waitForTimeout( 1000 ); // to avoid flakiness
await shopper.productIsInCart(simpleProductName); await shopper.productIsInCart( simpleProductName );
await shopper.removeFromCart( simplePostIdValue ); await shopper.removeFromCart( simplePostIdValue );
}); } );
afterAll(async () => { afterAll( async () => {
await merchant.login(); await merchant.login();
await merchant.openSettings('products'); await merchant.openSettings( 'products' );
await unsetCheckbox('#woocommerce_cart_redirect_after_add'); await unsetCheckbox( '#woocommerce_cart_redirect_after_add' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
}); } );
}); } );
}; };
module.exports = runCartRedirectionTest; module.exports = runCartRedirectionTest;

View File

@ -15,83 +15,95 @@ const { it, describe, beforeAll } = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const singleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const twoProductPrice = singleProductPrice * 2; const twoProductPrice = singleProductPrice * 2;
const runCartPageTest = () => { const runCartPageTest = () => {
describe('Cart page', () => { describe( 'Cart page', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault( 'general', false ); await withRestApi.resetSettingsGroupToDefault( 'general', false );
await withRestApi.resetSettingsGroupToDefault( 'products', false ); await withRestApi.resetSettingsGroupToDefault( 'products', false );
await withRestApi.resetSettingsGroupToDefault( 'tax', false ); await withRestApi.resetSettingsGroupToDefault( 'tax', false );
}); } );
it('should display no item in the cart', async () => { it( 'should display no item in the cart', async () => {
await shopper.goToCart(); await shopper.goToCart();
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); await expect( page ).toMatchElement( '.cart-empty', {
}); text: 'Your cart is currently empty.',
} );
} );
it('should add the product to the cart from the shop page', async () => { it( 'should add the product to the cart from the shop page', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(simpleProductName); await shopper.productIsInCart( simpleProductName );
}); } );
it('should increase item qty when "Add to cart" of the same product is clicked', async () => { it( 'should increase item qty when "Add to cart" of the same product is clicked', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(simpleProductName, 2); await shopper.productIsInCart( simpleProductName, 2 );
}); } );
it('should update qty when updated via qty input', async () => { it( 'should update qty when updated via qty input', async () => {
await shopper.goToCart(); await shopper.goToCart();
await shopper.setCartQuantity(simpleProductName, 4); await shopper.setCartQuantity( simpleProductName, 4 );
await expect(page).toClick('button', {text: 'Update cart'}); await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked(); await uiUnblocked();
await shopper.productIsInCart(simpleProductName, 4); await shopper.productIsInCart( simpleProductName, 4 );
}); } );
it('should remove the item from the cart when remove is clicked', async () => { it( 'should remove the item from the cart when remove is clicked', async () => {
await shopper.goToCart(); await shopper.goToCart();
await shopper.removeFromCart( productId ); await shopper.removeFromCart( productId );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); await expect( page ).toMatchElement( '.cart-empty', {
}); text: 'Your cart is currently empty.',
} );
} );
it('should update subtotal in cart totals when adding product to the cart', async () => { it( 'should update subtotal in cart totals when adding product to the cart', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(simpleProductName, 1); await shopper.productIsInCart( simpleProductName, 1 );
await expect(page).toMatchElement('.cart-subtotal .amount', {text: `$${ singleProductPrice }`}); await expect( page ).toMatchElement( '.cart-subtotal .amount', {
text: `$${ singleProductPrice }`,
} );
await shopper.setCartQuantity(simpleProductName, 2); await shopper.setCartQuantity( simpleProductName, 2 );
await expect(page).toClick('button', {text: 'Update cart'}); await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.cart-subtotal .amount', {text: `$${ twoProductPrice }`}); await expect( page ).toMatchElement( '.cart-subtotal .amount', {
}); text: `$${ twoProductPrice }`,
} );
} );
it('should go to the checkout page when "Proceed to Checkout" is clicked', async () => { it( 'should go to the checkout page when "Proceed to Checkout" is clicked', async () => {
await shopper.goToCart(); await shopper.goToCart();
await Promise.all([ await Promise.all( [
page.waitForNavigation({waitUntil: 'networkidle0'}), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
expect(page).toClick('.checkout-button', {text: 'Proceed to checkout'}), expect( page ).toClick( '.checkout-button', {
]); text: 'Proceed to checkout',
} ),
] );
await expect(page).toMatchElement('#order_review'); await expect( page ).toMatchElement( '#order_review' );
}); } );
}); } );
}; };
module.exports = runCartPageTest; module.exports = runCartPageTest;

View File

@ -16,12 +16,11 @@ const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
*/ */
const { it, describe, beforeAll } = require( '@jest/globals' ); const { it, describe, beforeAll } = require( '@jest/globals' );
const runCheckoutApplyCouponsTest = () => { const runCheckoutApplyCouponsTest = () => {
describe('Checkout coupons', () => { describe( 'Checkout coupons', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await shopper.emptyCart(); await shopper.emptyCart();
await shopper.goToShop(); await shopper.goToShop();
@ -29,48 +28,71 @@ const runCheckoutApplyCouponsTest = () => {
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await uiUnblocked(); await uiUnblocked();
await shopper.goToCheckout(); await shopper.goToCheckout();
}); } );
it.each( getCouponsTable() )( 'allows checkout to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => { it.each( getCouponsTable() )(
const coupon = await getCouponId( couponType ); 'allows checkout to apply %s coupon',
await applyCoupon(coupon); async ( couponType, cartDiscount, orderTotal ) => {
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' }); const coupon = await getCouponId( couponType );
await applyCoupon( coupon );
await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
// Wait for page to expand total calculations to avoid flakyness // Wait for page to expand total calculations to avoid flakyness
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
// Verify discount applied and order total // Verify discount applied and order total
await expect(page).toMatchElement('.cart-discount .amount', cartDiscount); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.order-total .amount', orderTotal); '.cart-discount .amount',
await removeCoupon(coupon); cartDiscount
}); );
await expect( page ).toMatchElement(
'.order-total .amount',
orderTotal
);
await removeCoupon( coupon );
}
);
it('prevents checkout applying same coupon twice', async () => { it( 'prevents checkout applying same coupon twice', async () => {
const couponId = await getCouponId( 'fixed cart' ); const couponId = await getCouponId( 'fixed cart' );
await applyCoupon( couponId ); await applyCoupon( couponId );
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
await applyCoupon( couponId ); await applyCoupon( couponId );
// Verify only one discount applied // Verify only one discount applied
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied' // This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); await expect( page ).toMatchElement( '.cart-discount .amount', {
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); text: '$5.00',
}); } );
await expect( page ).toMatchElement( '.order-total .amount', {
text: '$4.99',
} );
} );
it('allows checkout to apply multiple coupons', async () => { it( 'allows checkout to apply multiple coupons', async () => {
await applyCoupon( await getCouponId( 'fixed product' ) ); await applyCoupon( await getCouponId( 'fixed product' ) );
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'Coupon code applied successfully.',
} );
// Verify discount applied and order total // Verify discount applied and order total
await page.waitForSelector('.order-total'); await page.waitForSelector( '.order-total' );
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: '$0.00',
} );
} );
it('restores checkout total when coupons are removed', async () => { it( 'restores checkout total when coupons are removed', async () => {
await removeCoupon( await getCouponId( 'fixed cart' ) ); await removeCoupon( await getCouponId( 'fixed cart' ) );
await removeCoupon( await getCouponId( 'fixed product' ) ); await removeCoupon( await getCouponId( 'fixed product' ) );
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'}); await expect( page ).toMatchElement( '.order-total .amount', {
}); text: '$9.99',
}); } );
} );
} );
}; };
module.exports = runCheckoutApplyCouponsTest; module.exports = runCheckoutApplyCouponsTest;

View File

@ -1,7 +1,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
merchant, merchant,
createSimpleProduct, createSimpleProduct,
@ -21,21 +21,28 @@ const config = require( 'config' );
const customerBilling = config.get( 'addresses.customer.billing' ); const customerBilling = config.get( 'addresses.customer.billing' );
const runCheckoutCreateAccountTest = () => { const runCheckoutCreateAccountTest = () => {
describe('Shopper Checkout Create Account', () => { describe( 'Shopper Checkout Create Account', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await withRestApi.deleteCustomerByEmail( customerBilling.email ); await withRestApi.deleteCustomerByEmail( customerBilling.email );
// Set checkbox for creating an account during checkout // Set checkbox for creating an account during checkout
await merchant.login(); await merchant.login();
await merchant.openSettings('account'); await merchant.openSettings( 'account' );
await setCheckbox('#woocommerce_enable_signup_and_login_from_checkout'); await setCheckbox(
'#woocommerce_enable_signup_and_login_from_checkout'
);
await settingsPageSaveChanges(); await settingsPageSaveChanges();
// Set free shipping within California // Set free shipping within California
await addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', ' ', 'free_shipping' ); await addShippingZoneAndMethod(
'Free Shipping CA',
'state:US:CA',
' ',
'free_shipping'
);
await merchant.logout(); await merchant.logout();
@ -46,25 +53,29 @@ const runCheckoutCreateAccountTest = () => {
await shopper.goToCheckout(); await shopper.goToCheckout();
}, 45000 ); }, 45000 );
it('can create an account during checkout', async () => { it( 'can create an account during checkout', async () => {
// Fill all the details for a new customer // Fill all the details for a new customer
await shopper.fillBillingDetails( customerBilling ); await shopper.fillBillingDetails( customerBilling );
await uiUnblocked(); await uiUnblocked();
// Set checkbox for creating account during checkout // Set checkbox for creating account during checkout
await setCheckbox('#createaccount'); await setCheckbox( '#createaccount' );
// Place an order // Place an order
await shopper.placeOrder(); await shopper.placeOrder();
await expect(page).toMatchElement('h1.entry-title', {text: 'Order received'}); await expect( page ).toMatchElement( 'h1.entry-title', {
}); text: 'Order received',
} );
} );
it('can verify that the customer has been created', async () => { it( 'can verify that the customer has been created', async () => {
await merchant.login(); await merchant.login();
await merchant.openAllUsersView(); await merchant.openAllUsersView();
await expect(page).toMatchElement('td.email.column-email > a', { text: customerBilling.email }); await expect( page ).toMatchElement( 'td.email.column-email > a', {
}); text: customerBilling.email,
}); } );
} );
} );
}; };
module.exports = runCheckoutCreateAccountTest; module.exports = runCheckoutCreateAccountTest;

View File

@ -1,7 +1,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
merchant, merchant,
createSimpleProduct, createSimpleProduct,
@ -15,19 +15,19 @@
*/ */
const { it, describe, beforeAll } = require( '@jest/globals' ); const { it, describe, beforeAll } = require( '@jest/globals' );
const config = require('config'); const config = require( 'config' );
const runCheckoutLoginAccountTest = () => { const runCheckoutLoginAccountTest = () => {
describe('Shopper Checkout Login Account', () => { describe( 'Shopper Checkout Login Account', () => {
let productId; let productId;
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
// Set checkbox for logging to account during checkout // Set checkbox for logging to account during checkout
await merchant.login(); await merchant.login();
await merchant.openSettings('account'); await merchant.openSettings( 'account' );
await setCheckbox('#woocommerce_enable_checkout_login_reminder'); await setCheckbox( '#woocommerce_enable_checkout_login_reminder' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
await merchant.logout(); await merchant.logout();
@ -36,42 +36,54 @@ const runCheckoutLoginAccountTest = () => {
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await uiUnblocked(); await uiUnblocked();
await shopper.goToCheckout(); await shopper.goToCheckout();
}); } );
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('can login to an existing account during checkout', async () => { it( 'can login to an existing account during checkout', async () => {
// Click to login during checkout // Click to login during checkout
await page.waitForSelector('.woocommerce-form-login-toggle'); await page.waitForSelector( '.woocommerce-form-login-toggle' );
await expect(page).toClick('.woocommerce-info > a.showlogin'); await expect( page ).toClick( '.woocommerce-info > a.showlogin' );
// Fill shopper's login credentials and proceed further // Fill shopper's login credentials and proceed further
await page.type( '#username', config.get('users.customer.username') ); await page.type(
await page.type( '#password', config.get('users.customer.password') ); '#username',
config.get( 'users.customer.username' )
);
await page.type(
'#password',
config.get( 'users.customer.password' )
);
await Promise.all([ await Promise.all( [
page.waitForNavigation({waitUntil: 'networkidle0'}), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click('button[name="login"]'), page.click( 'button[name="login"]' ),
]); ] );
// Place an order // Place an order
await shopper.placeOrder(); await shopper.placeOrder();
await expect(page).toMatchElement('h1.entry-title', {text: 'Order received'}); await expect( page ).toMatchElement( 'h1.entry-title', {
text: 'Order received',
} );
// Verify the email of a logged in user // Verify the email of a logged in user
await expect(page).toMatchElement('ul > li.email', {text: 'Email: john.doe@example.com'}); await expect( page ).toMatchElement( 'ul > li.email', {
text: 'Email: john.doe@example.com',
} );
// Verify the user is logged in on my account page // Verify the user is logged in on my account page
await shopper.gotoMyAccount(); await shopper.gotoMyAccount();
await Promise.all( [ await Promise.all( [
await expect(page.url()).toMatch('my-account/'), await expect( page.url() ).toMatch( 'my-account/' ),
await expect(page).toMatchElement('h1', {text: 'My account'}), await expect( page ).toMatchElement( 'h1', {
text: 'My account',
} ),
] ); ] );
}); } );
}); } );
}; };
module.exports = runCheckoutLoginAccountTest; module.exports = runCheckoutLoginAccountTest;

View File

@ -16,7 +16,9 @@ const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const singleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const twoProductPrice = singleProductPrice * 2; const twoProductPrice = singleProductPrice * 2;
const threeProductPrice = singleProductPrice * 3; const threeProductPrice = singleProductPrice * 3;
const fourProductPrice = singleProductPrice * 4; const fourProductPrice = singleProductPrice * 4;
@ -28,128 +30,233 @@ let customerOrderId;
const runCheckoutPageTest = () => { const runCheckoutPageTest = () => {
let productId; let productId;
describe('Checkout page', () => { describe( 'Checkout page', () => {
beforeAll(async () => { beforeAll( async () => {
productId = await createSimpleProduct(); productId = await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault( 'general', false ); await withRestApi.resetSettingsGroupToDefault( 'general', false );
await withRestApi.resetSettingsGroupToDefault( 'products', false ); await withRestApi.resetSettingsGroupToDefault( 'products', false );
await withRestApi.resetSettingsGroupToDefault( 'tax', false ); await withRestApi.resetSettingsGroupToDefault( 'tax', false );
// Set free shipping within California // Set free shipping within California
await withRestApi.addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', '', 'free_shipping', '', [], false ); await withRestApi.addShippingZoneAndMethod(
'Free Shipping CA',
'state:US:CA',
'',
'free_shipping',
'',
[],
false
);
// Set base location with state CA. // Set base location with state CA.
await withRestApi.updateSettingOption( 'general', 'woocommerce_default_country', { value: 'US:CA' } ); await withRestApi.updateSettingOption(
'general',
'woocommerce_default_country',
{ value: 'US:CA' }
);
// Sell to all countries // Sell to all countries
await withRestApi.updateSettingOption( 'general', 'woocommerce_allowed_countries', { value: 'all' } ); await withRestApi.updateSettingOption(
'general',
'woocommerce_allowed_countries',
{ value: 'all' }
);
// Set currency to USD // Set currency to USD
await withRestApi.updateSettingOption( 'general', 'woocommerce_currency', { value: 'USD' } ); await withRestApi.updateSettingOption(
'general',
'woocommerce_currency',
{ value: 'USD' }
);
// Tax calculation should have been enabled by another test - no-op // Tax calculation should have been enabled by another test - no-op
// Enable BACS payment method // Enable BACS payment method
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true }, false ); await withRestApi.updatePaymentGateway(
'bacs',
{ enabled: true },
false
);
// Enable COD payment method // Enable COD payment method
await withRestApi.updatePaymentGateway( 'cod', { enabled: true }, false ); await withRestApi.updatePaymentGateway(
}); 'cod',
{ enabled: true },
false
);
} );
afterAll(async () => { afterAll( async () => {
await withRestApi.deleteAllShippingZones( false ); await withRestApi.deleteAllShippingZones( false );
}); } );
it('should display cart items in order review', async () => { it( 'should display cart items in order review', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice); await shopper.productIsInCheckout(
}); simpleProductName,
`1`,
singleProductPrice,
singleProductPrice
);
} );
it('allows customer to choose available payment methods', async () => { it( 'allows customer to choose available payment methods', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `2`, twoProductPrice, twoProductPrice); await shopper.productIsInCheckout(
simpleProductName,
`2`,
twoProductPrice,
twoProductPrice
);
await expect(page).toClick('.wc_payment_method label', {text: 'Direct bank transfer'}); await expect( page ).toClick( '.wc_payment_method label', {
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); text: 'Direct bank transfer',
}); } );
await expect( page ).toClick( '.wc_payment_method label', {
text: 'Cash on delivery',
} );
} );
it('allows customer to fill billing details', async () => { it( 'allows customer to fill billing details', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `3`, threeProductPrice, threeProductPrice); await shopper.productIsInCheckout(
await shopper.fillBillingDetails(config.get('addresses.customer.billing')); simpleProductName,
}); `3`,
threeProductPrice,
threeProductPrice
);
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
} );
it('allows customer to fill shipping details', async () => { it( 'allows customer to fill shipping details', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `4`, fourProductPrice, fourProductPrice); await shopper.productIsInCheckout(
simpleProductName,
`4`,
fourProductPrice,
fourProductPrice
);
// Select checkbox to ship to a different address // Select checkbox to ship to a different address
await page.evaluate(() => { await page.evaluate( () => {
document.querySelector('#ship-to-different-address-checkbox').click(); document
}); .querySelector( '#ship-to-different-address-checkbox' )
.click();
} );
await uiUnblocked(); await uiUnblocked();
await shopper.fillShippingDetails(config.get('addresses.customer.shipping')); await shopper.fillShippingDetails(
}); config.get( 'addresses.customer.shipping' )
);
} );
it('allows guest customer to place order', async () => { it( 'allows guest customer to place order', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `5`, fiveProductPrice, fiveProductPrice); await shopper.productIsInCheckout(
await shopper.fillBillingDetails(config.get('addresses.customer.billing')); simpleProductName,
`5`,
fiveProductPrice,
fiveProductPrice
);
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked(); await uiUnblocked();
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); await expect( page ).toClick( '.wc_payment_method label', {
await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'}); text: 'Cash on delivery',
} );
await expect( page ).toMatchElement( '.payment_method_cod', {
text: 'Pay with cash upon delivery.',
} );
await uiUnblocked(); await uiUnblocked();
await shopper.placeOrder(); await shopper.placeOrder();
await expect(page).toMatch('Order received'); await expect( page ).toMatch( 'Order received' );
// Get order ID from the order received html element on the page // Get order ID from the order received html element on the page
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); const orderReceivedHtmlElement = await page.$(
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); '.woocommerce-order-overview__order.order'
guestOrderId = orderReceivedText.split(/(\s+)/)[6].toString(); );
}); const orderReceivedText = await page.evaluate(
( element ) => element.textContent,
orderReceivedHtmlElement
);
guestOrderId = orderReceivedText.split( /(\s+)/ )[ 6 ].toString();
} );
it('allows existing customer to place order', async () => { it( 'allows existing customer to place order', async () => {
await shopper.login(); await shopper.login();
await shopper.goToShop(); await shopper.goToShop();
await shopper.addToCartFromShopPage( productId ); await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice); await shopper.productIsInCheckout(
await shopper.fillBillingDetails(config.get('addresses.customer.billing')); simpleProductName,
`1`,
singleProductPrice,
singleProductPrice
);
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked(); await uiUnblocked();
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); await expect( page ).toClick( '.wc_payment_method label', {
await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'}); text: 'Cash on delivery',
} );
await expect( page ).toMatchElement( '.payment_method_cod', {
text: 'Pay with cash upon delivery.',
} );
await uiUnblocked(); await uiUnblocked();
await shopper.placeOrder(); await shopper.placeOrder();
await expect(page).toMatch('Order received'); await expect( page ).toMatch( 'Order received' );
// Get order ID from the order received html element on the page // Get order ID from the order received html element on the page
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); const orderReceivedHtmlElement = await page.$(
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); '.woocommerce-order-overview__order.order'
customerOrderId = orderReceivedText.split(/(\s+)/)[6].toString(); );
}); const orderReceivedText = await page.evaluate(
( element ) => element.textContent,
orderReceivedHtmlElement
);
customerOrderId = orderReceivedText
.split( /(\s+)/ )[ 6 ]
.toString();
} );
it('merchant can confirm the order was received', async () => { it( 'merchant can confirm the order was received', async () => {
await merchant.login(); await merchant.login();
await merchant.verifyOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice); await merchant.verifyOrder(
await merchant.verifyOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true); guestOrderId,
}); simpleProductName,
}); singleProductPrice,
5,
fiveProductPrice
);
await merchant.verifyOrder(
customerOrderId,
simpleProductName,
singleProductPrice,
1,
singleProductPrice,
true
);
} );
} );
}; };
module.exports = runCheckoutPageTest; module.exports = runCheckoutPageTest;

View File

@ -17,37 +17,42 @@ const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
const customerEmailAddress = 'john.doe.test@example.com'; const customerEmailAddress = 'john.doe.test@example.com';
const runMyAccountCreateAccountTest = () => { const runMyAccountCreateAccountTest = () => {
describe('Shopper My Account Create Account', () => { describe( 'Shopper My Account Create Account', () => {
beforeAll(async () => { beforeAll( async () => {
await withRestApi.deleteCustomerByEmail( customerEmailAddress ); await withRestApi.deleteCustomerByEmail( customerEmailAddress );
await merchant.login(); await merchant.login();
// Set checkbox in the settings to enable registration in my account // Set checkbox in the settings to enable registration in my account
await merchant.openSettings('account'); await merchant.openSettings( 'account' );
await setCheckbox('#woocommerce_enable_myaccount_registration'); await setCheckbox( '#woocommerce_enable_myaccount_registration' );
await settingsPageSaveChanges(); await settingsPageSaveChanges();
await merchant.logout(); await merchant.logout();
}); } );
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('can create a new account via my account', async () => { it( 'can create a new account via my account', async () => {
await shopper.gotoMyAccount(); await shopper.gotoMyAccount();
await page.waitForSelector('.woocommerce-form-register'); await page.waitForSelector( '.woocommerce-form-register' );
await expect(page).toFill('input#reg_email', customerEmailAddress); await expect( page ).toFill(
await expect(page).toClick('button[name="register"]'); 'input#reg_email',
await page.waitForNavigation({waitUntil: 'networkidle0'}); customerEmailAddress
await expect(page).toMatchElement('h1', 'My account'); );
await expect( page ).toClick( 'button[name="register"]' );
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
await expect( page ).toMatchElement( 'h1', 'My account' );
// Verify user has been created successfully // Verify user has been created successfully
await merchant.login(); await merchant.login();
await merchant.openAllUsersView(); await merchant.openAllUsersView();
await expect(page).toMatchElement('td.email.column-email > a', {text: customerEmailAddress}); await expect( page ).toMatchElement( 'td.email.column-email > a', {
}); text: customerEmailAddress,
}); } );
} );
} );
}; };
module.exports = runMyAccountCreateAccountTest; module.exports = runMyAccountCreateAccountTest;

View File

@ -6,7 +6,7 @@ const {
shopper, shopper,
merchant, merchant,
createSimpleProduct, createSimpleProduct,
uiUnblocked uiUnblocked,
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
/** /**
@ -20,39 +20,46 @@ const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const runMyAccountPayOrderTest = () => { const runMyAccountPayOrderTest = () => {
describe('Customer can pay for their order through My Account', () => { describe( 'Customer can pay for their order through My Account', () => {
beforeAll(async () => { beforeAll( async () => {
simplePostIdValue = await createSimpleProduct(); simplePostIdValue = await createSimpleProduct();
await shopper.login(); await shopper.login();
await shopper.goToProduct(simplePostIdValue); await shopper.goToProduct( simplePostIdValue );
await shopper.addToCart(simpleProductName); await shopper.addToCart( simpleProductName );
await shopper.goToCheckout(); await shopper.goToCheckout();
await shopper.fillBillingDetails(config.get('addresses.customer.billing')); await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked(); await uiUnblocked();
await shopper.placeOrder(); await shopper.placeOrder();
// Get order ID from the order received html element on the page // Get order ID from the order received html element on the page
orderNum = await page.$$eval(".woocommerce-order-overview__order strong", elements => elements.map(item => item.textContent)); orderNum = await page.$$eval(
'.woocommerce-order-overview__order strong',
( elements ) => elements.map( ( item ) => item.textContent )
);
await merchant.login(); await merchant.login();
await merchant.updateOrderStatus(orderNum, 'Pending payment'); await merchant.updateOrderStatus( orderNum, 'Pending payment' );
await merchant.logout(); await merchant.logout();
}); } );
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('allows customer to pay for their order in My Account', async () => { it( 'allows customer to pay for their order in My Account', async () => {
await shopper.login(); await shopper.login();
await shopper.goToOrders(); await shopper.goToOrders();
await expect(page).toClick('a.woocommerce-button.button.pay'); await expect( page ).toClick( 'a.woocommerce-button.button.pay' );
await page.waitForNavigation({waitUntil: 'networkidle0'}); await page.waitForNavigation( { waitUntil: 'networkidle0' } );
await expect(page).toMatchElement('.entry-title', {text: 'Pay for order'}); await expect( page ).toMatchElement( '.entry-title', {
text: 'Pay for order',
} );
await shopper.placeOrder(); await shopper.placeOrder();
await expect(page).toMatch('Order received'); await expect( page ).toMatch( 'Order received' );
}); } );
}); } );
} };
module.exports = runMyAccountPayOrderTest; module.exports = runMyAccountPayOrderTest;

View File

@ -2,9 +2,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const { shopper } = require( '@woocommerce/e2e-utils' );
shopper,
} = require( '@woocommerce/e2e-utils' );
/** /**
* External dependencies * External dependencies
@ -12,35 +10,74 @@ const {
const { it, describe } = require( '@jest/globals' ); const { it, describe } = require( '@jest/globals' );
const pages = [ const pages = [
['Orders', 'my-account/orders', shopper.goToOrders], [ 'Orders', 'my-account/orders', shopper.goToOrders ],
['Downloads', 'my-account/downloads', shopper.goToDownloads], [ 'Downloads', 'my-account/downloads', shopper.goToDownloads ],
['Addresses', 'my-account/edit-address', shopper.goToAddresses], [ 'Addresses', 'my-account/edit-address', shopper.goToAddresses ],
['Account details', 'my-account/edit-account', shopper.goToAccountDetails] [
'Account details',
'my-account/edit-account',
shopper.goToAccountDetails,
],
]; ];
const runMyAccountPageTest = () => { const runMyAccountPageTest = () => {
describe('My account page', () => { describe( 'My account page', () => {
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('allows customer to login', async () => { it( 'allows customer to login', async () => {
await shopper.login(); await shopper.login();
expect(page).toMatch('Hello'); expect( page ).toMatch( 'Hello' );
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Dashboard' }); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Orders' }); '.woocommerce-MyAccount-navigation-link',
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Downloads' }); {
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Addresses' }); text: 'Dashboard',
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Account details' }); }
await expect(page).toMatchElement('.woocommerce-MyAccount-navigation-link', { text: 'Logout' }); );
}); await expect( page ).toMatchElement(
'.woocommerce-MyAccount-navigation-link',
{
text: 'Orders',
}
);
await expect( page ).toMatchElement(
'.woocommerce-MyAccount-navigation-link',
{
text: 'Downloads',
}
);
await expect( page ).toMatchElement(
'.woocommerce-MyAccount-navigation-link',
{
text: 'Addresses',
}
);
await expect( page ).toMatchElement(
'.woocommerce-MyAccount-navigation-link',
{
text: 'Account details',
}
);
await expect( page ).toMatchElement(
'.woocommerce-MyAccount-navigation-link',
{
text: 'Logout',
}
);
} );
it.each(pages)('allows customer to see %s page', async (pageTitle, path, goToPage) => { it.each( pages )(
await goToPage(); 'allows customer to see %s page',
expect(page.url()).toMatch(path); async ( pageTitle, path, goToPage ) => {
await expect(page).toMatchElement('h1', { text: pageTitle }); await goToPage();
}); expect( page.url() ).toMatch( path );
}); await expect( page ).toMatchElement( 'h1', {
} text: pageTitle,
} );
}
);
} );
};
module.exports = runMyAccountPageTest; module.exports = runMyAccountPageTest;

View File

@ -2,7 +2,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
merchant, merchant,
createSimpleProduct, createSimpleProduct,
@ -23,38 +23,45 @@ const customerEmail = config.get( 'addresses.customer.billing.email' );
const storeName = 'WooCommerce Core E2E Test Suite'; const storeName = 'WooCommerce Core E2E Test Suite';
const runOrderEmailReceivingTest = () => { const runOrderEmailReceivingTest = () => {
describe('Shopper Order Email Receiving', () => { describe( 'Shopper Order Email Receiving', () => {
beforeAll(async () => { beforeAll( async () => {
simplePostIdValue = await createSimpleProduct(); simplePostIdValue = await createSimpleProduct();
await merchant.login(); await merchant.login();
await deleteAllEmailLogs(); await deleteAllEmailLogs();
await merchant.logout(); await merchant.logout();
}); } );
afterAll( async () => { afterAll( async () => {
await shopper.logout(); await shopper.logout();
} ); } );
it('should receive order email after purchasing an item', async () => { it( 'should receive order email after purchasing an item', async () => {
await shopper.login(); await shopper.login();
// Go to the shop and purchase an item // Go to the shop and purchase an item
await shopper.goToProduct(simplePostIdValue); await shopper.goToProduct( simplePostIdValue );
await shopper.addToCart(simpleProductName); await shopper.addToCart( simpleProductName );
await shopper.goToCheckout(); await shopper.goToCheckout();
await uiUnblocked(); await uiUnblocked();
await shopper.placeOrder(); await shopper.placeOrder();
// Get order ID from the order received html element on the page // Get order ID from the order received html element on the page
orderId = await page.$$eval(".woocommerce-order-overview__order strong", elements => elements.map(item => item.textContent)); orderId = await page.$$eval(
'.woocommerce-order-overview__order strong',
( elements ) => elements.map( ( item ) => item.textContent )
);
// Verify the new order email has been received // Verify the new order email has been received
await merchant.login(); await merchant.login();
await merchant.openEmailLog(); await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } ); await expect( page ).toMatchElement( '.column-receiver', {
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } ); text: customerEmail,
}); } );
}); await expect( page ).toMatchElement( '.column-subject', {
text: `[${ storeName }]: New order #${ orderId }`,
} );
} );
} );
}; };
module.exports = runOrderEmailReceivingTest; module.exports = runOrderEmailReceivingTest;

View File

@ -15,9 +15,15 @@ const { it, beforeAll } = require( '@jest/globals' );
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const singleProductPrice = config.has( 'products.simple.price' )
const singleProductPrice2 = config.has('products.simple.price') ? '1' + singleProductPrice : '19.99'; ? config.get( 'products.simple.price' )
const singleProductPrice3 = config.has('products.simple.price') ? '2' + singleProductPrice : '29.99'; : '9.99';
const singleProductPrice2 = config.has( 'products.simple.price' )
? '1' + singleProductPrice
: '19.99';
const singleProductPrice3 = config.has( 'products.simple.price' )
? '2' + singleProductPrice
: '29.99';
const clothing = 'Clothing'; const clothing = 'Clothing';
const audio = 'Audio'; const audio = 'Audio';
const hardware = 'Hardware'; const hardware = 'Hardware';
@ -26,60 +32,88 @@ const productTitle = 'li.first > a > h2.woocommerce-loop-product__title';
const getWordPressVersion = async () => { const getWordPressVersion = async () => {
const context = await getEnvironmentContext(); const context = await getEnvironmentContext();
return context.wpVersion; return context.wpVersion;
} };
const runProductBrowseSearchSortTest = () => { const runProductBrowseSearchSortTest = () => {
utils.describeIf( getWordPressVersion() >= 5.8 )( 'Search, browse by categories and sort items in the shop', () => { utils.describeIf( getWordPressVersion() >= 5.8 )(
beforeAll(async () => { 'Search, browse by categories and sort items in the shop',
// Create 1st product with Clothing category () => {
await createSimpleProductWithCategory(simpleProductName + ' 1', singleProductPrice, clothing); beforeAll( async () => {
// Create 2nd product with Audio category // Create 1st product with Clothing category
await createSimpleProductWithCategory(simpleProductName + ' 2', singleProductPrice2, audio); await createSimpleProductWithCategory(
// Create 3rd product with Hardware category simpleProductName + ' 1',
await createSimpleProductWithCategory(simpleProductName + ' 3', singleProductPrice3, hardware); singleProductPrice,
}); clothing
);
// Create 2nd product with Audio category
await createSimpleProductWithCategory(
simpleProductName + ' 2',
singleProductPrice2,
audio
);
// Create 3rd product with Hardware category
await createSimpleProductWithCategory(
simpleProductName + ' 3',
singleProductPrice3,
hardware
);
} );
it('should let user search the store', async () => { it( 'should let user search the store', async () => {
await shopper.goToShop(); await shopper.goToShop();
await shopper.searchForProduct(simpleProductName + ' 1'); await shopper.searchForProduct( simpleProductName + ' 1' );
page.waitForNavigation({waitUntil: 'networkidle0'}); page.waitForNavigation( { waitUntil: 'networkidle0' } );
}); } );
it('should let user browse products by categories', async () => { it( 'should let user browse products by categories', async () => {
// Browse through Clothing category link // Browse through Clothing category link
await Promise.all([ await Promise.all( [
page.click('span.posted_in > a', {text: clothing}), page.click( 'span.posted_in > a', { text: clothing } ),
page.waitForNavigation({waitUntil: 'networkidle0'}), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]); ] );
// Verify Clothing category page // Verify Clothing category page
await page.waitForSelector(productTitle); await page.waitForSelector( productTitle );
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 1'}); await expect( page ).toMatchElement( productTitle, {
await expect(page).toClick(productTitle, {text: simpleProductName + ' 1'}); text: simpleProductName + ' 1',
page.waitForNavigation({waitUntil: 'networkidle0'}); } );
await page.waitForSelector('h1.entry-title'); await expect( page ).toClick( productTitle, {
await expect(page).toMatchElement('h1.entry-title', simpleProductName + ' 1'); text: simpleProductName + ' 1',
}); } );
page.waitForNavigation( { waitUntil: 'networkidle0' } );
await page.waitForSelector( 'h1.entry-title' );
await expect( page ).toMatchElement(
'h1.entry-title',
simpleProductName + ' 1'
);
} );
it('should let user sort the products in the shop', async () => { it( 'should let user sort the products in the shop', async () => {
await shopper.goToShop(); await shopper.goToShop();
// Sort by price high to low // Sort by price high to low
await page.select('.orderby', 'price-desc'); await page.select( '.orderby', 'price-desc' );
// Verify the first product in sort order // Verify the first product in sort order
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 3'}); await expect( page ).toMatchElement( productTitle, {
text: simpleProductName + ' 3',
} );
// Sort by price low to high // Sort by price low to high
await page.select('.orderby', 'price'); await page.select( '.orderby', 'price' );
// Verify the first product in sort order // Verify the first product in sort order
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 1'}); await expect( page ).toMatchElement( productTitle, {
text: simpleProductName + ' 1',
} );
// Sort by date of creation, latest to oldest // Sort by date of creation, latest to oldest
await page.select('.orderby', 'date'); await page.select( '.orderby', 'date' );
// Verify the first product in sort order // Verify the first product in sort order
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 3'}); await expect( page ).toMatchElement( productTitle, {
}); text: simpleProductName + ' 3',
}); } );
} );
}
);
}; };
module.exports = runProductBrowseSearchSortTest; module.exports = runProductBrowseSearchSortTest;

View File

@ -24,123 +24,147 @@ const defaultVariableProduct = config.get( 'products.variable' );
let variableProductId; let variableProductId;
// Variables for grouped product // Variables for grouped product
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has( 'products.simple.price' )
? config.get( 'products.simple.price' )
: '9.99';
const simple1 = { const simple1 = {
name: simpleProductName + ' 1', name: simpleProductName + ' 1',
regularPrice: simpleProductPrice regularPrice: simpleProductPrice,
}; };
const simple2 = { const simple2 = {
name: simpleProductName + ' 2', name: simpleProductName + ' 2',
regularPrice: simpleProductPrice regularPrice: simpleProductPrice,
}; };
const groupedProduct = { const groupedProduct = {
name: 'Grouped Product', name: 'Grouped Product',
groupedProducts: [simple1, simple2] groupedProducts: [ simple1, simple2 ],
}; };
let groupedPostIdValue; let groupedPostIdValue;
const runSingleProductPageTest = () => { const runSingleProductPageTest = () => {
describe('Single Product Page', () => { describe( 'Single Product Page', () => {
beforeAll(async () => { beforeAll( async () => {
simplePostIdValue = await createSimpleProduct(); simplePostIdValue = await createSimpleProduct();
}); } );
it('should be able to add simple products to the cart', async () => { it( 'should be able to add simple products to the cart', async () => {
// Add 5 simple products to cart // Add 5 simple products to cart
await shopper.goToProduct(simplePostIdValue); await shopper.goToProduct( simplePostIdValue );
await expect(page).toFill('div.quantity input.qty', '5'); await expect( page ).toFill( 'div.quantity input.qty', '5' );
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-message', {text: 'have been added to your cart.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'have been added to your cart.',
} );
// Verify cart contents // Verify cart contents
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(simpleProductName, 5); await shopper.productIsInCart( simpleProductName, 5 );
}); } );
it('should be able to remove simple products from the cart', async () => { it( 'should be able to remove simple products from the cart', async () => {
// Remove items from cart // Remove items from cart
await shopper.removeFromCart( simplePostIdValue ); await shopper.removeFromCart( simplePostIdValue );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); await expect( page ).toMatchElement( '.cart-empty', {
}); text: 'Your cart is currently empty.',
}); } );
} );
} );
describe('Variable Product Page', () => { describe( 'Variable Product Page', () => {
beforeAll(async () => { beforeAll( async () => {
variableProductId = await createVariableProduct(); variableProductId = await createVariableProduct();
}); } );
it('should be able to add variation products to the cart', async () => { it( 'should be able to add variation products to the cart', async () => {
// Add a product with one set of variations to cart // Add a product with one set of variations to cart
await shopper.goToProduct(variableProductId); await shopper.goToProduct( variableProductId );
for (const attr of defaultVariableProduct.attributes) { for ( const attr of defaultVariableProduct.attributes ) {
const { name, options } = attr; const { name, options } = attr;
const selectElem = `#${name.toLowerCase()}`; const selectElem = `#${ name.toLowerCase() }`;
const value = options[0]; const value = options[ 0 ];
await expect(page).toSelect(selectElem, value); await expect( page ).toSelect( selectElem, value );
} }
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-message', {text: 'has been added to your cart.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: 'has been added to your cart.',
} );
// Verify cart contents // Verify cart contents
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(defaultVariableProduct.name); await shopper.productIsInCart( defaultVariableProduct.name );
}); } );
it('should be able to remove variation products from the cart', async () => { it( 'should be able to remove variation products from the cart', async () => {
// Remove items from cart // Remove items from cart
await shopper.removeFromCart( variableProductId ); await shopper.removeFromCart( variableProductId );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); await expect( page ).toMatchElement( '.cart-empty', {
}); text: 'Your cart is currently empty.',
}); } );
} );
} );
describe('Grouped Product Page', () => { describe( 'Grouped Product Page', () => {
beforeAll(async () => { beforeAll( async () => {
groupedPostIdValue = await createGroupedProduct(groupedProduct); groupedPostIdValue = await createGroupedProduct( groupedProduct );
}); } );
it('should be able to add grouped products to the cart', async () => { it( 'should be able to add grouped products to the cart', async () => {
// Add a grouped product to cart // Add a grouped product to cart
await shopper.goToProduct(groupedPostIdValue); await shopper.goToProduct( groupedPostIdValue );
await page.waitForSelector('form.grouped_form'); await page.waitForSelector( 'form.grouped_form' );
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-error', await expect( page ).toMatchElement( '.woocommerce-error', {
{text: 'Please choose the quantity of items you wish to add to your cart…'}); text:
const quantityFields = await page.$$('div.quantity input.qty'); 'Please choose the quantity of items you wish to add to your cart…',
await quantityFields[0].click({clickCount: 3}); } );
await quantityFields[0].type('5'); const quantityFields = await page.$$( 'div.quantity input.qty' );
await quantityFields[1].click({clickCount: 3}); await quantityFields[ 0 ].click( { clickCount: 3 } );
await quantityFields[1].type('5'); await quantityFields[ 0 ].type( '5' );
await quantityFields[ 1 ].click( { clickCount: 3 } );
await quantityFields[ 1 ].type( '5' );
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-message', await expect( page ).toMatchElement( '.woocommerce-message', {
{text: '“'+simpleProductName+' 1” and “'+simpleProductName+' 2” have been added to your cart.'}); text:
'“' +
simpleProductName +
' 1” and “' +
simpleProductName +
' 2” have been added to your cart.',
} );
// Verify cart contents // Verify cart contents
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart(simpleProductName+' 1'); await shopper.productIsInCart( simpleProductName + ' 1' );
await shopper.productIsInCart(simpleProductName+' 2'); await shopper.productIsInCart( simpleProductName + ' 2' );
}); } );
it('should be able to remove grouped products from the cart', async () => { it( 'should be able to remove grouped products from the cart', async () => {
// Remove items from cart // Remove items from cart
await shopper.removeFromCart( simpleProductName+' 1' ); await shopper.removeFromCart( simpleProductName + ' 1' );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.woocommerce-message', {text: '“'+simpleProductName+' 1” removed.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
text: '“' + simpleProductName + ' 1” removed.',
} );
await Promise.all( [ await Promise.all( [
// Reload page and perform item removal, since removeFromCart won't remove it when placed in a row // Reload page and perform item removal, since removeFromCart won't remove it when placed in a row
page.reload(), page.reload(),
page.waitForNavigation( { waitUntil: 'networkidle0' } ), page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] ); ] );
await shopper.removeFromCart(simpleProductName+' 2'); await shopper.removeFromCart( simpleProductName + ' 2' );
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.woocommerce-message', {text: '“'+simpleProductName+' 2” removed.'}); await expect( page ).toMatchElement( '.woocommerce-message', {
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); text: '“' + simpleProductName + ' 2” removed.',
}); } );
}); await expect( page ).toMatchElement( '.cart-empty', {
text: 'Your cart is currently empty.',
} );
} );
} );
}; };
module.exports = runSingleProductPageTest; module.exports = runSingleProductPageTest;

View File

@ -2,105 +2,128 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const { shopper, createVariableProduct } = require( '@woocommerce/e2e-utils' );
shopper, const config = require( 'config' );
createVariableProduct,
} = require( '@woocommerce/e2e-utils' );
const config = require('config');
let variablePostIdValue; let variablePostIdValue;
const cartDialogMessage = 'Please select some product options before adding this product to your cart.'; const cartDialogMessage =
const attributes = config.get( 'products.variable.attributes' ) 'Please select some product options before adding this product to your cart.';
const attributes = config.get( 'products.variable.attributes' );
const runVariableProductUpdateTest = () => { const runVariableProductUpdateTest = () => {
describe('Shopper > Update variable product',() => { describe( 'Shopper > Update variable product', () => {
beforeAll(async () => { beforeAll( async () => {
variablePostIdValue = await createVariableProduct(); variablePostIdValue = await createVariableProduct();
}); } );
it('shopper can change variable attributes to the same value', async () => { it( 'shopper can change variable attributes to the same value', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct( variablePostIdValue );
for (const a of attributes) { for ( const a of attributes ) {
const { name, options } = a; const { name, options } = a;
const attrHTMLId = `#${name.toLowerCase()}`; const attrHTMLId = `#${ name.toLowerCase() }`;
await expect(page).toSelect(attrHTMLId, options[0]); await expect( page ).toSelect( attrHTMLId, options[ 0 ] );
} }
await expect(page).toMatchElement('.woocommerce-variation-price', { await expect( page ).toMatchElement(
text: '9.99' '.woocommerce-variation-price',
}); {
}); text: '9.99',
}
);
} );
it('shopper can change attributes to combination with dimensions and weight', async () => { it( 'shopper can change attributes to combination with dimensions and weight', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct( variablePostIdValue );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[0].name.toLowerCase()}`, `#${ attributes[ 0 ].name.toLowerCase() }`,
attributes[0].options[0] attributes[ 0 ].options[ 0 ]
); );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[1].name.toLowerCase()}`, `#${ attributes[ 1 ].name.toLowerCase() }`,
attributes[1].options[1] attributes[ 1 ].options[ 1 ]
); );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[2].name.toLowerCase()}`, `#${ attributes[ 2 ].name.toLowerCase() }`,
attributes[2].options[0] attributes[ 2 ].options[ 0 ]
); );
await expect(page).toMatchElement('.woocommerce-variation-price', { text: '20.00' }); await expect( page ).toMatchElement(
await expect(page).toMatchElement('.woocommerce-variation-availability', { text: 'Out of stock' }); '.woocommerce-variation-price',
await expect(page).toMatchElement('.woocommerce-product-attributes-item--weight', { text: '200 kg' }); { text: '20.00' }
await expect(page).toMatchElement('.woocommerce-product-attributes-item--dimensions', { text: '10 × 20 × 15 cm' });
});
it('shopper can change variable product attributes to variation with a different price', async () => {
await shopper.goToProduct(variablePostIdValue);
await expect(page).toSelect(
`#${attributes[0].name.toLowerCase()}`,
attributes[0].options[0]
); );
await expect(page).toSelect( await expect( page ).toMatchElement(
`#${attributes[1].name.toLowerCase()}`, '.woocommerce-variation-availability',
attributes[1].options[0] {
text: 'Out of stock',
}
); );
await expect(page).toSelect( await expect( page ).toMatchElement(
`#${attributes[2].name.toLowerCase()}`, '.woocommerce-product-attributes-item--weight',
attributes[2].options[1] {
text: '200 kg',
}
);
await expect(
page
).toMatchElement(
'.woocommerce-product-attributes-item--dimensions',
{ text: '10 × 20 × 15 cm' }
);
} );
it( 'shopper can change variable product attributes to variation with a different price', async () => {
await shopper.goToProduct( variablePostIdValue );
await expect( page ).toSelect(
`#${ attributes[ 0 ].name.toLowerCase() }`,
attributes[ 0 ].options[ 0 ]
);
await expect( page ).toSelect(
`#${ attributes[ 1 ].name.toLowerCase() }`,
attributes[ 1 ].options[ 0 ]
);
await expect( page ).toSelect(
`#${ attributes[ 2 ].name.toLowerCase() }`,
attributes[ 2 ].options[ 1 ]
); );
await expect(page).toMatchElement('.woocommerce-variation-price', { text: '11.99' }); await expect( page ).toMatchElement(
}); '.woocommerce-variation-price',
{ text: '11.99' }
);
} );
it('shopper can reset variations', async () => { it( 'shopper can reset variations', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct( variablePostIdValue );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[0].name.toLowerCase()}`, `#${ attributes[ 0 ].name.toLowerCase() }`,
attributes[0].options[0] attributes[ 0 ].options[ 0 ]
); );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[1].name.toLowerCase()}`, `#${ attributes[ 1 ].name.toLowerCase() }`,
attributes[1].options[1] attributes[ 1 ].options[ 1 ]
); );
await expect(page).toSelect( await expect( page ).toSelect(
`#${attributes[2].name.toLowerCase()}`, `#${ attributes[ 2 ].name.toLowerCase() }`,
attributes[2].options[0] attributes[ 2 ].options[ 0 ]
); );
await expect(page).toClick('.reset_variations'); await expect( page ).toClick( '.reset_variations' );
// Verify the reset by attempting to add the product to the cart // Verify the reset by attempting to add the product to the cart
const couponDialog = await expect(page).toDisplayDialog(async () => { const couponDialog = await expect( page ).toDisplayDialog(
await expect(page).toClick('.single_add_to_cart_button'); async () => {
}); await expect( page ).toClick(
'.single_add_to_cart_button'
expect(couponDialog.message()).toMatch(cartDialogMessage); );
}); }
);
});
expect( couponDialog.message() ).toMatch( cartDialogMessage );
} );
} );
}; };
module.exports = runVariableProductUpdateTest; module.exports = runVariableProductUpdateTest;

View File

@ -6,7 +6,7 @@ const { createCoupon } = require( '@woocommerce/e2e-utils' );
const couponsTable = [ const couponsTable = [
[ 'fixed cart', { text: '$5.00' }, { text: '$4.99' } ], [ 'fixed cart', { text: '$5.00' }, { text: '$4.99' } ],
[ 'percentage', { text: '$4.99' }, { text: '$5.00' } ], [ 'percentage', { text: '$4.99' }, { text: '$5.00' } ],
[ 'fixed product', { text: '$5.00' }, { text: '$4.99' } ] [ 'fixed product', { text: '$5.00' }, { text: '$4.99' } ],
]; ];
let couponFixedCart; let couponFixedCart;
@ -28,12 +28,18 @@ const getCouponId = async ( couponType ) => {
return couponFixedCart; return couponFixedCart;
case 'percentage': case 'percentage':
if ( ! couponPercentage ) { if ( ! couponPercentage ) {
couponPercentage = await createCoupon( '50', 'Percentage discount' ); couponPercentage = await createCoupon(
'50',
'Percentage discount'
);
} }
return couponPercentage; return couponPercentage;
case 'fixed product': case 'fixed product':
if ( ! couponFixedProduct ) { if ( ! couponFixedProduct ) {
couponFixedProduct = await createCoupon( '5', 'Fixed product discount' ); couponFixedProduct = await createCoupon(
'5',
'Fixed product discount'
);
} }
return couponFixedProduct; return couponFixedProduct;
} }

View File

@ -1,7 +1,5 @@
module.exports = { module.exports = {
extends: [ extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
'plugin:jest/recommended',
],
env: { env: {
'jest/globals': true, 'jest/globals': true,
}, },

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Standardize linting: Ensure e2e packages are lintable

View File

@ -29,9 +29,9 @@
"@wordpress/jest-preset-default": "^7.1.3", "@wordpress/jest-preset-default": "^7.1.3",
"app-root-path": "^3.0.0", "app-root-path": "^3.0.0",
"commander": "4.1.1", "commander": "4.1.1",
"config": "3.3.3", "config": "3.3.3",
"jest": "^25.1.0", "jest": "^25.1.0",
"jest-circus": "25.1.0", "jest-circus": "25.1.0",
"jest-each": "25.5.0", "jest-each": "25.5.0",
"jest-puppeteer": "^4.4.0", "jest-puppeteer": "^4.4.0",
"node-stream-zip": "^1.13.6", "node-stream-zip": "^1.13.6",
@ -53,7 +53,7 @@
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3", "@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2", "@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0", "@wordpress/browserslist-config": "^4.1.0",
"@wordpress/eslint-plugin": "7.3.0", "@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.1.0", "eslint": "^8.1.0",
"ndb": "^1.1.5", "ndb": "^1.1.5",
"semver": "^7.3.2" "semver": "^7.3.2"
@ -66,7 +66,7 @@
"clean": "rm -rf ./build ./build-module", "clean": "rm -rf ./build ./build-module",
"compile": "e2e-builds", "compile": "e2e-builds",
"build": "pnpm run clean && pnpm run compile", "build": "pnpm run clean && pnpm run compile",
"prepack": "pnpm run build", "prepare": "pnpm run build",
"docker:up": "./bin/docker-compose.sh up", "docker:up": "./bin/docker-compose.sh up",
"docker:wait": "bash ./bin/wait-for-build.sh", "docker:wait": "bash ./bin/wait-for-build.sh",
"docker:down": "./bin/docker-compose.sh down", "docker:down": "./bin/docker-compose.sh down",
@ -75,14 +75,15 @@
"test:e2e": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js", "test:e2e": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js",
"test:e2e-debug": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug", "test:e2e-debug": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug",
"test:e2e-dev": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev", "test:e2e-dev": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev",
"lint": "eslint src" "lint": "eslint src --ext=js,ts,tsx",
"lint:fix": "eslint src --ext=js,ts,tsx --fix"
}, },
"bin": { "bin": {
"wc-e2e": "bin/wc-e2e.sh" "wc-e2e": "bin/wc-e2e.sh"
}, },
"lint-staged": { "lint-staged": {
"*.(t|j)s?(x)": [ "*.(t|j)s?(x)": [
"eslint --fix" "pnpm lint:fix"
] ]
} }
} }

View File

@ -78,8 +78,8 @@ global.it = ( () => {
/** /**
* Save a screenshot during a test if the test fails. * Save a screenshot during a test if the test fails.
* *
* @param testName * @param testName
* @param callback * @param callback
* @return {Promise<void>} * @return {Promise<void>}
*/ */
const screenshotTest = async ( testName, callback ) => { const screenshotTest = async ( testName, callback ) => {

View File

@ -67,7 +67,7 @@ const initializeSlack = () => {
/** /**
* Post a message to a Slack channel for a failed test. * Post a message to a Slack channel for a failed test.
* *
* @param testName * @param testName
* @return {Promise<void>} * @return {Promise<void>}
*/ */
async function sendFailedTestMessageToSlack( testName ) { async function sendFailedTestMessageToSlack( testName ) {
@ -131,7 +131,7 @@ async function sendFailedTestMessageToSlack( testName ) {
/** /**
* Post a screenshot to a Slack channel for a failed test. * Post a screenshot to a Slack channel for a failed test.
* *
* @param screenshotOfFailedTest * @param screenshotOfFailedTest
* @return {Promise<void>} * @return {Promise<void>}
*/ */
async function sendFailedTestScreenshotToSlack( screenshotOfFailedTest ) { async function sendFailedTestScreenshotToSlack( screenshotOfFailedTest ) {

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Standardize linting: Ensure e2e packages are lintable

View File

@ -45,7 +45,13 @@
"clean": "rm -rf ./build ./build-module", "clean": "rm -rf ./build ./build-module",
"compile": "e2e-builds", "compile": "e2e-builds",
"build": "pnpm run clean && pnpm run compile", "build": "pnpm run clean && pnpm run compile",
"prepack": "pnpm run build", "prepare": "pnpm run build",
"lint": "eslint src" "lint": "eslint src --ext=js,ts,tsx",
"lint:fix": "eslint src --ext=js,ts,tsx --fix"
},
"lint-staged": {
"*.(t|j)s?(x)": [
"pnpm lint:fix"
]
} }
} }

View File

@ -34,7 +34,7 @@ const uuid = require( 'uuid' );
/** /**
* Verify and publish * Verify and publish
* *
* @param noticeText The text that appears in the notice after publishing. * @param noticeText The text that appears in the notice after publishing.
*/ */
const verifyAndPublish = async ( noticeText ) => { const verifyAndPublish = async ( noticeText ) => {
// Wait for auto save // Wait for auto save
@ -53,7 +53,7 @@ const verifyAndPublish = async ( noticeText ) => {
/** /**
* Wait for primary button to be enabled and click. * Wait for primary button to be enabled and click.
* *
* @param waitForNetworkIdle - Wait for network idle after click * @param waitForNetworkIdle - Wait for network idle after click
* @return {Promise<void>} * @return {Promise<void>}
*/ */
const waitAndClickPrimary = async ( waitForNetworkIdle = true ) => { const waitAndClickPrimary = async ( waitForNetworkIdle = true ) => {
@ -231,8 +231,8 @@ const completeOnboardingWizard = async () => {
/** /**
* Create simple product. * Create simple product.
* *
* @param {string} productTitle Defaults to Simple Product. Customizable title. * @param {string} productTitle Defaults to Simple Product. Customizable title.
* @param {string} productPrice Defaults to $9.99. Customizable pricing. * @param {string} productPrice Defaults to $9.99. Customizable pricing.
* @param {Object} additionalProps Defaults to nothing. Additional product properties. * @param {Object} additionalProps Defaults to nothing. Additional product properties.
*/ */
const createSimpleProduct = async ( const createSimpleProduct = async (
@ -252,9 +252,9 @@ const createSimpleProduct = async (
/** /**
* Create simple product with categories * Create simple product with categories
* *
* @param productName Product's name which can be changed when writing a test * @param productName Product's name which can be changed when writing a test
* @param productPrice Product's price which can be changed when writing a test * @param productPrice Product's price which can be changed when writing a test
* @param categoryName Product's category which can be changed when writing a test * @param categoryName Product's category which can be changed when writing a test
*/ */
const createSimpleProductWithCategory = async ( const createSimpleProductWithCategory = async (
productName, productName,
@ -281,10 +281,10 @@ const createSimpleProductWithCategory = async (
/** /**
* Create simple downloadable product * Create simple downloadable product
* *
* @param name Product's name. Defaults to 'Simple Product' (see createSimpleProduct definition). * @param name Product's name. Defaults to 'Simple Product' (see createSimpleProduct definition).
* @param downloadLimit Product's download limit. Defaults to '-1' (unlimited). * @param downloadLimit Product's download limit. Defaults to '-1' (unlimited).
* @param downloadName Product's download name. Defaults to 'Single'. * @param downloadName Product's download name. Defaults to 'Single'.
* @param price Product's price. Defaults to '$9.99' (see createSimpleProduct definition). * @param price Product's price. Defaults to '$9.99' (see createSimpleProduct definition).
*/ */
const createSimpleDownloadableProduct = async ( const createSimpleDownloadableProduct = async (
name, name,
@ -312,7 +312,7 @@ const createSimpleDownloadableProduct = async (
* Create variable product. * Create variable product.
* Also, create variations for all attributes. * Also, create variations for all attributes.
* *
* @param varProduct Defaults to the variable product object in `default.json` * @param varProduct Defaults to the variable product object in `default.json`
* @return the ID of the created variable product * @return the ID of the created variable product
*/ */
const createVariableProduct = async ( varProduct = defaultVariableProduct ) => { const createVariableProduct = async ( varProduct = defaultVariableProduct ) => {
@ -383,7 +383,7 @@ const createVariableProduct = async ( varProduct = defaultVariableProduct ) => {
/** /**
* Create grouped product. * Create grouped product.
* *
* @param groupedProduct Defaults to the grouped product object in `default.json` * @param groupedProduct Defaults to the grouped product object in `default.json`
* @return ID of the grouped product * @return ID of the grouped product
*/ */
const createGroupedProduct = async ( const createGroupedProduct = async (
@ -446,7 +446,7 @@ const createOrder = async ( orderOptions = {} ) => {
/** /**
* Create a basic order with the provided order status. * Create a basic order with the provided order status.
* *
* @param orderStatus Status of the new order. Defaults to `Pending payment`. * @param orderStatus Status of the new order. Defaults to `Pending payment`.
*/ */
const createSimpleOrder = async ( orderStatus = 'Pending payment' ) => { const createSimpleOrder = async ( orderStatus = 'Pending payment' ) => {
// Go to 'Add new order' page // Go to 'Add new order' page
@ -481,7 +481,7 @@ const createSimpleOrder = async ( orderStatus = 'Pending payment' ) => {
* Creates a batch of orders from the given `statuses` * Creates a batch of orders from the given `statuses`
* using the "Batch Create Order" API. * using the "Batch Create Order" API.
* *
* @param statuses Array of order statuses * @param statuses Array of order statuses
*/ */
const batchCreateOrders = async ( statuses ) => { const batchCreateOrders = async ( statuses ) => {
const defaultOrder = config.get( 'orders.basicPaidOrder' ); const defaultOrder = config.get( 'orders.basicPaidOrder' );
@ -505,8 +505,8 @@ const batchCreateOrders = async ( statuses ) => {
/** /**
* Adds a product to an order in the merchant. * Adds a product to an order in the merchant.
* *
* @param orderId ID of the order to add the product to. * @param orderId ID of the order to add the product to.
* @param productName Name of the product being added to the order. * @param productName Name of the product being added to the order.
*/ */
const addProductToOrder = async ( orderId, productName ) => { const addProductToOrder = async ( orderId, productName ) => {
await merchant.goToOrder( orderId ); await merchant.goToOrder( orderId );
@ -539,8 +539,8 @@ const addProductToOrder = async ( orderId, productName ) => {
/** /**
* Creates a basic coupon with the provided coupon amount. Returns the coupon code. * Creates a basic coupon with the provided coupon amount. Returns the coupon code.
* *
* @param couponAmount Amount to be applied. Defaults to 5. * @param couponAmount Amount to be applied. Defaults to 5.
* @param discountType Type of a coupon. Defaults to Fixed cart discount. * @param discountType Type of a coupon. Defaults to Fixed cart discount.
*/ */
const createCoupon = async ( const createCoupon = async (
couponAmount = '5', couponAmount = '5',
@ -576,10 +576,10 @@ const createCoupon = async (
/** /**
* Adds a shipping zone along with a shipping method. * Adds a shipping zone along with a shipping method.
* *
* @param zoneName Shipping zone name. * @param zoneName Shipping zone name.
* @param zoneLocation Shiping zone location. Defaults to country:US. For states use: state:US:CA * @param zoneLocation Shiping zone location. Defaults to country:US. For states use: state:US:CA
* @param zipCode Shipping zone zip code. Defaults to empty one space. * @param zipCode Shipping zone zip code. Defaults to empty one space.
* @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup) * @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup)
*/ */
const addShippingZoneAndMethod = async ( const addShippingZoneAndMethod = async (
zoneName, zoneName,
@ -626,8 +626,8 @@ const addShippingZoneAndMethod = async (
/** /**
* Click the Update button on the order details page. * Click the Update button on the order details page.
* *
* @param noticeText The text that appears in the notice after updating the order. * @param noticeText The text that appears in the notice after updating the order.
* @param waitForSave Optionally wait for auto save. * @param waitForSave Optionally wait for auto save.
*/ */
const clickUpdateOrder = async ( noticeText, waitForSave = false ) => { const clickUpdateOrder = async ( noticeText, waitForSave = false ) => {
if ( waitForSave ) { if ( waitForSave ) {

View File

@ -531,7 +531,7 @@ const merchant = {
/** /**
* Deactivate a plugin by the plugin's name with the option to delete the plugin as well. * Deactivate a plugin by the plugin's name with the option to delete the plugin as well.
* *
* @param {string} pluginName The name of the plugin to deactivate. For example, `WooCommerce`. * @param {string} pluginName The name of the plugin to deactivate. For example, `WooCommerce`.
* @param {boolean} deletePlugin Pass in `true` to delete the plugin. Defaults to `false`. * @param {boolean} deletePlugin Pass in `true` to delete the plugin. Defaults to `false`.
*/ */
deactivatePlugin: async ( pluginName, deletePlugin = false ) => { deactivatePlugin: async ( pluginName, deletePlugin = false ) => {

View File

@ -2,9 +2,9 @@
* Take a string name and generate the slug for it. * Take a string name and generate the slug for it.
* Example: 'My plugin' => 'my-plugin' * Example: 'My plugin' => 'my-plugin'
* *
* @param text string to convert to a slug * @param text string to convert to a slug
* *
* Sourced from: https://gist.github.com/spyesx/561b1d65d4afb595f295 * Sourced from: https://gist.github.com/spyesx/561b1d65d4afb595f295
*/ */
export const getSlug = ( text ) => { export const getSlug = ( text ) => {
text = text.trim().toLowerCase(); text = text.trim().toLowerCase();
@ -39,7 +39,7 @@ export const itIf = ( condition ) => ( condition ? it : it.skip );
/** /**
* Wait for a timeout in milliseconds * Wait for a timeout in milliseconds
* *
* @param timeout delay time in milliseconds * @param timeout delay time in milliseconds
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export const waitForTimeout = async ( timeout ) => { export const waitForTimeout = async ( timeout ) => {

View File

@ -16,9 +16,9 @@ const userEndpoint = '/wp/v2/users';
/** /**
* Utility function to delete all merchant created data store objects. * Utility function to delete all merchant created data store objects.
* *
* @param repository * @param repository
* @param defaultObjectId * @param defaultObjectId
* @param statuses Status of the object to check * @param statuses Status of the object to check
* @return {Promise<void>} * @return {Promise<void>}
*/ */
const deleteAllRepositoryObjects = async ( const deleteAllRepositoryObjects = async (
@ -226,13 +226,13 @@ export const withRestApi = {
/** /**
* Adds a shipping zone along with a shipping method using the API. * Adds a shipping zone along with a shipping method using the API.
* *
* @param zoneName Shipping zone name. * @param zoneName Shipping zone name.
* @param zoneLocation Shiping zone location. Defaults to country:US. For states use: state:US:CA. * @param zoneLocation Shiping zone location. Defaults to country:US. For states use: state:US:CA.
* @param zipCode Shipping zone zip code. Default is no zip code. * @param zipCode Shipping zone zip code. Default is no zip code.
* @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup). * @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup).
* @param cost Shipping method cost. Default is no cost. * @param cost Shipping method cost. Default is no cost.
* @param additionalZoneMethods Array of additional zone methods to add to the shipping zone. * @param additionalZoneMethods Array of additional zone methods to add to the shipping zone.
* @param {boolean} testResponse Test the response status code. * @param {boolean} testResponse Test the response status code.
*/ */
addShippingZoneAndMethod: async ( addShippingZoneAndMethod: async (
zoneName, zoneName,
@ -367,7 +367,7 @@ export const withRestApi = {
/** /**
* Delete a customer account by their email address if the user exists. * Delete a customer account by their email address if the user exists.
* *
* @param emailAddress Customer user account email address. * @param emailAddress Customer user account email address.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
deleteCustomerByEmail: async ( emailAddress ) => { deleteCustomerByEmail: async ( emailAddress ) => {
@ -394,8 +394,8 @@ export const withRestApi = {
/** /**
* Reset a settings group to default values except selects. * Reset a settings group to default values except selects.
* *
* @param settingsGroup * @param settingsGroup
* @param {boolean} testResponse Test the response status code. * @param {boolean} testResponse Test the response status code.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
resetSettingsGroupToDefault: async ( resetSettingsGroupToDefault: async (
@ -437,8 +437,8 @@ export const withRestApi = {
* Update a setting to the supplied value. * Update a setting to the supplied value.
* *
* @param {string} settingsGroup The settings group to update. * @param {string} settingsGroup The settings group to update.
* @param {string} settingId The setting ID to update * @param {string} settingId The setting ID to update
* @param {Object} payload An object with a key/value pair to update. * @param {Object} payload An object with a key/value pair to update.
*/ */
updateSettingOption: async ( settingsGroup, settingId, payload = {} ) => { updateSettingOption: async ( settingsGroup, settingId, payload = {} ) => {
const settingsClient = Setting.restRepository( client ); const settingsClient = Setting.restRepository( client );
@ -447,9 +447,9 @@ export const withRestApi = {
/** /**
* Update a payment gateway. * Update a payment gateway.
* *
* @param {string} paymentGatewayId The ID of the payment gateway to update. * @param {string} paymentGatewayId The ID of the payment gateway to update.
* @param {Object} payload An object with the key/value pair to update. * @param {Object} payload An object with the key/value pair to update.
* @param {boolean} testResponse Test the response status code. * @param {boolean} testResponse Test the response status code.
*/ */
updatePaymentGateway: async ( updatePaymentGateway: async (
paymentGatewayId, paymentGatewayId,
@ -467,7 +467,7 @@ export const withRestApi = {
/** /**
* Create a batch of orders using the "Batch Create Order" API endpoint. * Create a batch of orders using the "Batch Create Order" API endpoint.
* *
* @param orders Array of orders to be created * @param orders Array of orders to be created
* @param {boolean} testResponse Test the response status code. * @param {boolean} testResponse Test the response status code.
*/ */
batchCreateOrders: async ( orders, testResponse = true ) => { batchCreateOrders: async ( orders, testResponse = true ) => {

View File

@ -114,8 +114,8 @@ export const backboneUnblocked = async () => {
/** /**
* Conditionally wait for a selector without throwing an error. * Conditionally wait for a selector without throwing an error.
* *
* @param selector * @param selector
* @param timeoutInSeconds * @param timeoutInSeconds
* @return {Promise<boolean>} * @return {Promise<boolean>}
*/ */
export const waitForSelectorWithoutThrow = async ( export const waitForSelectorWithoutThrow = async (
@ -136,7 +136,7 @@ export const waitForSelectorWithoutThrow = async (
/** /**
* Publish, verify that item was published. Trash, verify that item was trashed. * Publish, verify that item was published. Trash, verify that item was trashed.
* *
* @param {string} button (Publish) * @param {string} button (Publish)
* @param {string} publishNotice * @param {string} publishNotice
* @param {string} publishVerification * @param {string} publishVerification
* @param {string} trashVerification * @param {string} trashVerification
@ -193,7 +193,7 @@ export const verifyCheckboxIsUnset = async ( selector ) => {
* Verify the value of input field once it was saved (can be used for radio buttons verification as well). * Verify the value of input field once it was saved (can be used for radio buttons verification as well).
* *
* @param {string} selector Selector of the input field that needs to be verified. * @param {string} selector Selector of the input field that needs to be verified.
* @param {string} value Value of the input field that needs to be verified. * @param {string} value Value of the input field that needs to be verified.
*/ */
export const verifyValueOfInputField = async ( selector, value ) => { export const verifyValueOfInputField = async ( selector, value ) => {
await page.focus( selector ); await page.focus( selector );
@ -249,7 +249,7 @@ export const evalAndClick = async ( selector ) => {
/** /**
* Select a value from select2 search input field. * Select a value from select2 search input field.
* *
* @param {string} value Value of what to be selected * @param {string} value Value of what to be selected
* @param {string} selector Selector of the select2 search field * @param {string} selector Selector of the select2 search field
*/ */
export const selectOptionInSelect2 = async ( export const selectOptionInSelect2 = async (
@ -266,8 +266,8 @@ export const selectOptionInSelect2 = async (
/** /**
* Search by any term for an order * Search by any term for an order
* *
* @param {string} value Value to be entered into the search field * @param {string} value Value to be entered into the search field
* @param {string} orderId Order ID * @param {string} orderId Order ID
* @param {string} customerName Customer's full name attached to order ID. * @param {string} customerName Customer's full name attached to order ID.
*/ */
export const searchForOrder = async ( value, orderId, customerName ) => { export const searchForOrder = async ( value, orderId, customerName ) => {
@ -284,7 +284,7 @@ export const searchForOrder = async ( value, orderId, customerName ) => {
* Apply a coupon code within cart or checkout. * Apply a coupon code within cart or checkout.
* Method will try to apply a coupon in the checkout, otherwise will try to apply in the cart. * Method will try to apply a coupon in the checkout, otherwise will try to apply in the cart.
* *
* @param couponCode string * @param couponCode string
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export const applyCoupon = async ( couponCode ) => { export const applyCoupon = async ( couponCode ) => {
@ -310,7 +310,7 @@ export const applyCoupon = async ( couponCode ) => {
/** /**
* Remove one coupon within cart or checkout. * Remove one coupon within cart or checkout.
* *
* @param couponCode Coupon name. * @param couponCode Coupon name.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export const removeCoupon = async ( couponCode ) => { export const removeCoupon = async ( couponCode ) => {
@ -348,7 +348,7 @@ export const selectOrderAction = async ( action ) => {
* *
* @param {string} buttonSelector Selector of button to click * @param {string} buttonSelector Selector of button to click
* @param {string} resultSelector Selector to wait for after click * @param {string} resultSelector Selector to wait for after click
* @param {number} timeout Timeout length in milliseconds. Default 5000. * @param {number} timeout Timeout length in milliseconds. Default 5000.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export const clickAndWaitForSelector = async ( export const clickAndWaitForSelector = async (
@ -367,9 +367,9 @@ export const clickAndWaitForSelector = async (
* Behavior can be modified with @param options. Possible keys: `visible`, `hidden`, `timeout`. * Behavior can be modified with @param options. Possible keys: `visible`, `hidden`, `timeout`.
* More details at: https://pptr.dev/#?product=Puppeteer&show=api-pagewaitforselectorselector-options * More details at: https://pptr.dev/#?product=Puppeteer&show=api-pagewaitforselectorselector-options
* *
* @param {Puppeteer.Page} page Puppeteer representation of the page. * @param {Puppeteer.Page} page Puppeteer representation of the page.
* @param {string} selector CSS selector of the element * @param {string} selector CSS selector of the element
* @param {Object} options Custom options to modify function behavior. * @param {Object} options Custom options to modify function behavior.
*/ */
export async function waitForSelector( page, selector, options = {} ) { export async function waitForSelector( page, selector, options = {} ) {
// set up default options // set up default options
@ -384,7 +384,7 @@ export async function waitForSelector( page, selector, options = {} ) {
* Retrieves the desired HTML attribute from a selector. * Retrieves the desired HTML attribute from a selector.
* For example, the 'value' attribute of an input element. * For example, the 'value' attribute of an input element.
* *
* @param {string} selector Selector of the element you want to get the attribute from. * @param {string} selector Selector of the element you want to get the attribute from.
* @param {string} attribute The desired HTML attribute. * @param {string} attribute The desired HTML attribute.
* @return {Promise<string>} * @return {Promise<string>}
*/ */
@ -399,8 +399,8 @@ export async function getSelectorAttribute( selector, attribute ) {
/** /**
* Asserts the value of the desired HTML attribute of a selector. * Asserts the value of the desired HTML attribute of a selector.
* *
* @param {string} selector Selector of the element you want to verify. * @param {string} selector Selector of the element you want to verify.
* @param {string} attribute The desired HTML attribute. * @param {string} attribute The desired HTML attribute.
* @param {string} expectedValue The expected value. * @param {string} expectedValue The expected value.
*/ */
export async function verifyValueOfElementAttribute( export async function verifyValueOfElementAttribute(

View File

@ -4,9 +4,9 @@ export class AdminEdit {
/** /**
* Publish the object being edited and verify published status * Publish the object being edited and verify published status
* *
* @param button Publish button selector * @param button Publish button selector
* @param publishNotice Publish notice selector * @param publishNotice Publish notice selector
* @param publishVerification Expected notice on successful publish * @param publishVerification Expected notice on successful publish
* @return {Promise<void>} * @return {Promise<void>}
*/ */
async verifyPublish( button, publishNotice, publishVerification ) { async verifyPublish( button, publishNotice, publishVerification ) {

View File

@ -187,8 +187,10 @@ importers:
packages/js/api-core-tests: packages/js/api-core-tests:
specifiers: specifiers:
'@woocommerce/eslint-plugin': workspace:*
allure-commandline: ^2.17.2 allure-commandline: ^2.17.2
dotenv: ^10.0.0 dotenv: ^10.0.0
eslint: ^8.12.0
jest: ^25.1.0 jest: ^25.1.0
jest-allure: ^0.1.3 jest-allure: ^0.1.3
jest-runner-groups: ^2.1.0 jest-runner-groups: ^2.1.0
@ -202,6 +204,9 @@ importers:
jest-runner-groups: 2.1.0 jest-runner-groups: 2.1.0
postman-collection: 4.1.0 postman-collection: 4.1.0
supertest: 6.1.6 supertest: 6.1.6
devDependencies:
'@woocommerce/eslint-plugin': link:../eslint-plugin
eslint: 8.12.0
packages/js/components: packages/js/components:
specifiers: specifiers:
@ -605,13 +610,17 @@ importers:
packages/js/e2e-builds: packages/js/e2e-builds:
specifiers: specifiers:
'@babel/core': 7.12.9 '@babel/core': 7.12.9
'@woocommerce/eslint-plugin': workspace:*
chalk: ^4.1.2 chalk: ^4.1.2
eslint: ^8.12.0
glob: ^7.2.0 glob: ^7.2.0
lodash: ^4.17.21 lodash: ^4.17.21
mkdirp: ^1.0.4 mkdirp: ^1.0.4
devDependencies: devDependencies:
'@babel/core': 7.12.9 '@babel/core': 7.12.9
'@woocommerce/eslint-plugin': link:../eslint-plugin
chalk: 4.1.2 chalk: 4.1.2
eslint: 8.12.0
glob: 7.2.0 glob: 7.2.0
lodash: 4.17.21 lodash: 4.17.21
mkdirp: 1.0.4 mkdirp: 1.0.4
@ -628,11 +637,13 @@ importers:
'@babel/preset-env': 7.12.7 '@babel/preset-env': 7.12.7
'@jest/globals': ^26.4.2 '@jest/globals': ^26.4.2
'@woocommerce/e2e-builds': workspace:* '@woocommerce/e2e-builds': workspace:*
'@woocommerce/eslint-plugin': workspace:*
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3 '@wordpress/babel-plugin-import-jsx-pragma': 1.1.3
'@wordpress/babel-preset-default': 3.0.2 '@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': ^4.1.0 '@wordpress/browserslist-config': ^4.1.0
'@wordpress/deprecated': ^3.2.3 '@wordpress/deprecated': ^3.2.3
config: 3.3.3 config: 3.3.3
eslint: ^8.12.0
dependencies: dependencies:
'@jest/globals': 26.6.2 '@jest/globals': 26.6.2
'@wordpress/deprecated': 3.2.3 '@wordpress/deprecated': 3.2.3
@ -647,9 +658,11 @@ importers:
'@babel/polyfill': 7.12.1 '@babel/polyfill': 7.12.1
'@babel/preset-env': 7.12.7_@babel+core@7.12.9 '@babel/preset-env': 7.12.7_@babel+core@7.12.9
'@woocommerce/e2e-builds': link:../e2e-builds '@woocommerce/e2e-builds': link:../e2e-builds
'@woocommerce/eslint-plugin': link:../eslint-plugin
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3_@babel+core@7.12.9 '@wordpress/babel-plugin-import-jsx-pragma': 1.1.3_@babel+core@7.12.9
'@wordpress/babel-preset-default': 3.0.2 '@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': 4.1.0 '@wordpress/browserslist-config': 4.1.0
eslint: 8.12.0
packages/js/e2e-environment: packages/js/e2e-environment:
specifiers: specifiers:
@ -666,11 +679,11 @@ importers:
'@slack/web-api': ^6.1.0 '@slack/web-api': ^6.1.0
'@woocommerce/api': ^0.2.0 '@woocommerce/api': ^0.2.0
'@woocommerce/e2e-builds': workspace:* '@woocommerce/e2e-builds': workspace:*
'@woocommerce/eslint-plugin': workspace:*
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3 '@wordpress/babel-plugin-import-jsx-pragma': 1.1.3
'@wordpress/babel-preset-default': 3.0.2 '@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': ^4.1.0 '@wordpress/browserslist-config': ^4.1.0
'@wordpress/e2e-test-utils': ^4.16.1 '@wordpress/e2e-test-utils': ^4.16.1
'@wordpress/eslint-plugin': 7.3.0
'@wordpress/jest-preset-default': ^7.1.3 '@wordpress/jest-preset-default': ^7.1.3
app-root-path: ^3.0.0 app-root-path: ^3.0.0
commander: 4.1.1 commander: 4.1.1
@ -716,10 +729,10 @@ importers:
'@babel/polyfill': 7.12.1 '@babel/polyfill': 7.12.1
'@babel/preset-env': 7.12.7_@babel+core@7.12.9 '@babel/preset-env': 7.12.7_@babel+core@7.12.9
'@woocommerce/e2e-builds': link:../e2e-builds '@woocommerce/e2e-builds': link:../e2e-builds
'@woocommerce/eslint-plugin': link:../eslint-plugin
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3_@babel+core@7.12.9 '@wordpress/babel-plugin-import-jsx-pragma': 1.1.3_@babel+core@7.12.9
'@wordpress/babel-preset-default': 3.0.2 '@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': 4.1.0 '@wordpress/browserslist-config': 4.1.0
'@wordpress/eslint-plugin': 7.3.0_eslint@8.2.0+typescript@4.2.4
eslint: 8.2.0 eslint: 8.2.0
ndb: 1.1.5 ndb: 1.1.5
semver: 7.3.5 semver: 7.3.5
@ -13960,22 +13973,6 @@ packages:
- typescript - typescript
dev: true dev: true
/@typescript-eslint/experimental-utils/2.34.0_eslint@8.2.0+typescript@4.2.4:
resolution: {integrity: sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==}
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
peerDependencies:
eslint: '*'
dependencies:
'@types/json-schema': 7.0.9
'@typescript-eslint/typescript-estree': 2.34.0_typescript@4.2.4
eslint: 8.2.0
eslint-scope: 5.1.1
eslint-utils: 2.1.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/experimental-utils/3.10.1_eslint@6.8.0+typescript@3.9.7: /@typescript-eslint/experimental-utils/3.10.1_eslint@6.8.0+typescript@3.9.7:
resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==} resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==}
engines: {node: ^10.12.0 || >=12.0.0} engines: {node: ^10.12.0 || >=12.0.0}
@ -14316,27 +14313,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/typescript-estree/2.34.0_typescript@4.2.4:
resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==}
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
debug: 4.3.3
eslint-visitor-keys: 1.3.0
glob: 7.2.0
is-glob: 4.0.3
lodash: 4.17.21
semver: 7.3.5
tsutils: 3.21.0_typescript@4.2.4
typescript: 4.2.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/typescript-estree/2.34.0_typescript@4.6.2: /@typescript-eslint/typescript-estree/2.34.0_typescript@4.6.2:
resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==} resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==}
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
@ -15881,30 +15857,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@wordpress/eslint-plugin/7.3.0_eslint@8.2.0+typescript@4.2.4:
resolution: {integrity: sha512-7wIFzzc14E1XuuT9haBuhoA9FRUGWlbD4Oek+XkiZlzNVqZI3slgbtIFJ6/Mfij1V18rv6Ns9a1cPJLtCU8JHQ==}
peerDependencies:
eslint: ^6 || ^7
dependencies:
'@wordpress/prettier-config': 0.4.0
babel-eslint: 10.1.0_eslint@8.2.0
cosmiconfig: 7.0.1
eslint: 8.2.0
eslint-config-prettier: 6.15.0_eslint@8.2.0
eslint-plugin-jest: 23.20.0_eslint@8.2.0+typescript@4.2.4
eslint-plugin-jsdoc: 30.7.13_eslint@8.2.0
eslint-plugin-jsx-a11y: 6.5.1_eslint@8.2.0
eslint-plugin-prettier: 3.4.1_1786dfa66f4aafe2692523a4f07ad974
eslint-plugin-react: 7.29.4_eslint@8.2.0
eslint-plugin-react-hooks: 4.3.0_eslint@8.2.0
globals: 12.4.0
prettier: /wp-prettier/2.0.5
requireindex: 1.2.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@wordpress/eslint-plugin/7.4.0_eslint@7.32.0+typescript@4.6.2: /@wordpress/eslint-plugin/7.4.0_eslint@7.32.0+typescript@4.6.2:
resolution: {integrity: sha512-HJpDYz2drtC9rY8MiYtYJ3cimioEIweGyb3P2DQTjUZ3sC4AGg+97PhXLHUdKfsFQ31JRxyLS9kKuGdDVBwWww==} resolution: {integrity: sha512-HJpDYz2drtC9rY8MiYtYJ3cimioEIweGyb3P2DQTjUZ3sC4AGg+97PhXLHUdKfsFQ31JRxyLS9kKuGdDVBwWww==}
engines: {node: '>=10', npm: '>=6.9'} engines: {node: '>=10', npm: '>=6.9'}
@ -17437,24 +17389,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/babel-eslint/10.1.0_eslint@8.2.0:
resolution: {integrity: sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==}
engines: {node: '>=6'}
deprecated: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.
peerDependencies:
eslint: '>= 4.12.1'
dependencies:
'@babel/code-frame': 7.16.0
'@babel/parser': 7.16.4
'@babel/traverse': 7.16.3
'@babel/types': 7.16.0
eslint: 8.2.0
eslint-visitor-keys: 1.3.0
resolve: 1.20.0
transitivePeerDependencies:
- supports-color
dev: true
/babel-generator/6.26.1: /babel-generator/6.26.1:
resolution: {integrity: sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==} resolution: {integrity: sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==}
dependencies: dependencies:
@ -21613,16 +21547,6 @@ packages:
get-stdin: 6.0.0 get-stdin: 6.0.0
dev: true dev: true
/eslint-config-prettier/6.15.0_eslint@8.2.0:
resolution: {integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==}
hasBin: true
peerDependencies:
eslint: '>=3.14.1'
dependencies:
eslint: 8.2.0
get-stdin: 6.0.0
dev: true
/eslint-config-prettier/8.5.0: /eslint-config-prettier/8.5.0:
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
hasBin: true hasBin: true
@ -21801,19 +21725,6 @@ packages:
- typescript - typescript
dev: true dev: true
/eslint-plugin-jest/23.20.0_eslint@8.2.0+typescript@4.2.4:
resolution: {integrity: sha512-+6BGQt85OREevBDWCvhqj1yYA4+BFK4XnRZSGJionuEYmcglMZYLNNBBemwzbqUAckURaHdJSBcjHPyrtypZOw==}
engines: {node: '>=8'}
peerDependencies:
eslint: '>=5'
dependencies:
'@typescript-eslint/experimental-utils': 2.34.0_eslint@8.2.0+typescript@4.2.4
eslint: 8.2.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/eslint-plugin-jest/25.7.0_6bef967891becc1ab6057e2949a5834f: /eslint-plugin-jest/25.7.0_6bef967891becc1ab6057e2949a5834f:
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@ -21896,24 +21807,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/eslint-plugin-jsdoc/30.7.13_eslint@8.2.0:
resolution: {integrity: sha512-YM4WIsmurrp0rHX6XiXQppqKB8Ne5ATiZLJe2+/fkp9l9ExXFr43BbAbjZaVrpCT+tuPYOZ8k1MICARHnURUNQ==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0
dependencies:
comment-parser: 0.7.6
debug: 4.3.4
eslint: 8.2.0
jsdoctypeparser: 9.0.0
lodash: 4.17.21
regextras: 0.7.1
semver: 7.3.5
spdx-expression-parse: 3.0.1
transitivePeerDependencies:
- supports-color
dev: true
/eslint-plugin-jsdoc/37.9.7: /eslint-plugin-jsdoc/37.9.7:
resolution: {integrity: sha512-8alON8yYcStY94o0HycU2zkLKQdcS+qhhOUNQpfONHHwvI99afbmfpYuPqf6PbLz5pLZldG3Te5I0RbAiTN42g==} resolution: {integrity: sha512-8alON8yYcStY94o0HycU2zkLKQdcS+qhhOUNQpfONHHwvI99afbmfpYuPqf6PbLz5pLZldG3Te5I0RbAiTN42g==}
engines: {node: ^12 || ^14 || ^16 || ^17} engines: {node: ^12 || ^14 || ^16 || ^17}
@ -22051,27 +21944,6 @@ packages:
language-tags: 1.0.5 language-tags: 1.0.5
minimatch: 3.1.2 minimatch: 3.1.2
/eslint-plugin-jsx-a11y/6.5.1_eslint@8.2.0:
resolution: {integrity: sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==}
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
'@babel/runtime': 7.17.7
aria-query: 4.2.2
array-includes: 3.1.4
ast-types-flow: 0.0.7
axe-core: 4.3.5
axobject-query: 2.2.0
damerau-levenshtein: 1.0.7
emoji-regex: 9.2.2
eslint: 8.2.0
has: 1.0.3
jsx-ast-utils: 3.2.1
language-tags: 1.0.5
minimatch: 3.1.2
dev: true
/eslint-plugin-markdown/1.0.2: /eslint-plugin-markdown/1.0.2:
resolution: {integrity: sha512-BfvXKsO0K+zvdarNc801jsE/NTLmig4oKhZ1U3aSUgTf2dB/US5+CrfGxMsCK2Ki1vS1R3HPok+uYpufFndhzw==} resolution: {integrity: sha512-BfvXKsO0K+zvdarNc801jsE/NTLmig4oKhZ1U3aSUgTf2dB/US5+CrfGxMsCK2Ki1vS1R3HPok+uYpufFndhzw==}
engines: {node: ^6.14.0 || ^8.10.0 || >=9.10.0} engines: {node: ^6.14.0 || ^8.10.0 || >=9.10.0}
@ -22081,23 +21953,6 @@ packages:
unified: 6.2.0 unified: 6.2.0
dev: true dev: true
/eslint-plugin-prettier/3.4.1_1786dfa66f4aafe2692523a4f07ad974:
resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==}
engines: {node: '>=6.0.0'}
peerDependencies:
eslint: '>=5.0.0'
eslint-config-prettier: '*'
prettier: '>=1.13.0'
peerDependenciesMeta:
eslint-config-prettier:
optional: true
dependencies:
eslint: 8.2.0
eslint-config-prettier: 6.15.0_eslint@8.2.0
prettier: /wp-prettier/2.0.5
prettier-linter-helpers: 1.0.0
dev: true
/eslint-plugin-prettier/3.4.1_41158af9eda640c62e4773187c5a8429: /eslint-plugin-prettier/3.4.1_41158af9eda640c62e4773187c5a8429:
resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==} resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@ -22206,15 +22061,6 @@ packages:
dependencies: dependencies:
eslint: 8.12.0 eslint: 8.12.0
/eslint-plugin-react-hooks/4.3.0_eslint@8.2.0:
resolution: {integrity: sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
dependencies:
eslint: 8.2.0
dev: true
/eslint-plugin-react/7.29.4: /eslint-plugin-react/7.29.4:
resolution: {integrity: sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==} resolution: {integrity: sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -22305,29 +22151,6 @@ packages:
semver: 6.3.0 semver: 6.3.0
string.prototype.matchall: 4.0.6 string.prototype.matchall: 4.0.6
/eslint-plugin-react/7.29.4_eslint@8.2.0:
resolution: {integrity: sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
array-includes: 3.1.4
array.prototype.flatmap: 1.2.5
doctrine: 2.1.0
eslint: 8.2.0
estraverse: 5.3.0
jsx-ast-utils: 3.2.1
minimatch: 3.1.2
object.entries: 1.1.5
object.fromentries: 2.0.5
object.hasown: 1.1.0
object.values: 1.1.5
prop-types: 15.8.1
resolve: 2.0.0-next.3
semver: 6.3.0
string.prototype.matchall: 4.0.6
dev: true
/eslint-plugin-testing-library/5.1.0_eslint@8.12.0+typescript@4.6.2: /eslint-plugin-testing-library/5.1.0_eslint@8.12.0+typescript@4.6.2:
resolution: {integrity: sha512-YSNzasJUbyhOTe14ZPygeOBvcPvcaNkwHwrj4vdf+uirr2D32JTDaKi6CP5Os2aWtOcvt4uBSPXp9h5xGoqvWQ==} resolution: {integrity: sha512-YSNzasJUbyhOTe14ZPygeOBvcPvcaNkwHwrj4vdf+uirr2D32JTDaKi6CP5Os2aWtOcvt4uBSPXp9h5xGoqvWQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'}
@ -37310,7 +37133,7 @@ packages:
serialize-javascript: 6.0.0 serialize-javascript: 6.0.0
source-map: 0.6.1 source-map: 0.6.1
terser: 5.10.0_acorn@8.7.0 terser: 5.10.0_acorn@8.7.0
webpack: 5.70.0 webpack: 5.70.0_webpack-cli@3.3.12
transitivePeerDependencies: transitivePeerDependencies:
- acorn - acorn
dev: true dev: true