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.
@ -53,10 +53,10 @@ const couponLines = {
/**
* Builds an example order request.
*
* @returns {Object} Sample Order payload.
* @return {Object} Sample Order payload.
*/
const getOrderExample = () => {
let orderExample = {
const orderExample = {
id: 0,
payment_method: 'cod',
payment_method_title: 'Cash on Delivery',
@ -73,7 +73,7 @@ const getOrderExample = () => {
coupon_lines: [ couponLines ],
};
return orderExample;
}
};
module.exports = {
order,

View File

@ -6,40 +6,53 @@ const {
postRequest,
putRequest,
deleteRequest,
} = require('../utils/request');
} = require( '../utils/request' );
const getProducts = ( params = {} ) => getRequest( 'products', params );
const createProduct = ( data ) => postRequest( 'products', data );
const createProductVariations = ( parentId, variations ) => postRequest(
`products/${ parentId }/variations/batch`,
{
const createProductVariations = ( parentId, variations ) =>
postRequest( `products/${ parentId }/variations/batch`, {
create: variations,
}
)
const createProducts = ( products ) => postRequest( 'products/batch', { create: products } );
const createProductCategory = ( data ) => postRequest( 'products/categories', data );
const createProductAttribute = ( name ) => postRequest( 'products/attributes', { name } );
const createProductAttributeTerms = ( parentId, termNames ) => postRequest(
`products/attributes/${ parentId }/terms/batch`,
{
create: termNames.map( name => ( { name } ) )
}
);
const createProductReview = ( productId, review ) => postRequest( 'products/reviews', {
product_id: productId,
...review,
} );
const updateProductReview = ( reviewId, data = {} ) => putRequest( `products/reviews/${ reviewId }`, data );
} );
const createProducts = ( products ) =>
postRequest( 'products/batch', { create: products } );
const createProductCategory = ( data ) =>
postRequest( 'products/categories', data );
const createProductAttribute = ( name ) =>
postRequest( 'products/attributes', { name } );
const createProductAttributeTerms = ( parentId, termNames ) =>
postRequest( `products/attributes/${ parentId }/terms/batch`, {
create: termNames.map( ( name ) => ( { name } ) ),
} );
const createProductReview = ( productId, review ) =>
postRequest( 'products/reviews', {
product_id: productId,
...review,
} );
const updateProductReview = ( reviewId, data = {} ) =>
putRequest( `products/reviews/${ reviewId }`, data );
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 createSampleCategories = async () => {
const { body: clothing } = await createProductCategory( { name: 'Clothing' } );
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: clothing } = await createProductCategory( {
name: 'Clothing',
} );
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: music } = await createProductCategory( { name: 'Music' } );
@ -56,8 +69,18 @@ const createSampleCategories = async () => {
const createSampleAttributes = async () => {
const { body: color } = await createProductAttribute( 'Color' );
const { body: size } = await createProductAttribute( 'Size' );
const { body: colors } = await createProductAttributeTerms( color.id, [ 'Blue', 'Gray', 'Green', 'Red', 'Yellow' ] );
const { body: sizes } = await createProductAttributeTerms( size.id, [ 'Large', 'Medium', 'Small' ] );
const { body: colors } = await createProductAttributeTerms( color.id, [
'Blue',
'Gray',
'Green',
'Red',
'Yellow',
] );
const { body: sizes } = await createProductAttributeTerms( size.id, [
'Large',
'Medium',
'Small',
] );
return {
color,
@ -73,7 +96,7 @@ const createSampleTags = async () => {
return {
cool,
};
}
};
const createSampleShippingClasses = async () => {
const { body: freight } = await createShippingClass( 'Freight' );
@ -81,7 +104,7 @@ const createSampleShippingClasses = async () => {
return {
freight,
};
}
};
const createSampleTaxClasses = async () => {
const { body: reducedRate } = await createTaxClass( 'Reduced Rate' );
@ -89,12 +112,13 @@ const createSampleTaxClasses = async () => {
return {
reducedRate,
};
}
};
const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
const description = '<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. Donec eu libero sit amet quam egestas semper. '
+ 'Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const description =
'<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. Donec eu libero sit amet quam egestas semper. ' +
'Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const { body: simpleProducts } = await createProducts( [
{
@ -151,15 +175,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Red' ]
}
options: [ 'Red' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 62, 63, 61, 60 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'T-Shirt with Logo',
@ -215,15 +239,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Gray' ]
}
options: [ 'Gray' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 59, 67, 66, 56 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Single',
@ -249,8 +273,9 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
{
id: '2579cf07-8b08-4c25-888a-b6258dd1f035',
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_expiry: 1,
@ -285,7 +310,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [],
menu_order: 0,
related_ids: [ 68 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Album',
@ -311,13 +336,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
{
id: 'cc10249f-1de2-44d4-93d3-9f88ae629f76',
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',
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_expiry: 1,
@ -352,7 +379,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [],
menu_order: 0,
related_ids: [ 69 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Polo',
@ -408,15 +435,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Blue' ]
}
options: [ 'Blue' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 59, 56, 66, 76 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Long Sleeve Tee',
@ -472,15 +499,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Green' ]
}
options: [ 'Green' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 59, 56, 76, 67 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Hoodie with Zipper',
@ -536,7 +563,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [],
menu_order: 0,
related_ids: [ 57, 58 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Hoodie with Pocket',
@ -592,15 +619,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Gray' ]
}
options: [ 'Gray' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 65, 57, 58 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Sunglasses',
@ -656,7 +683,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [],
menu_order: 0,
related_ids: [ 60, 62, 77, 61 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Cap',
@ -712,15 +739,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Yellow' ]
}
options: [ 'Yellow' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 60, 77, 61, 63 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Belt',
@ -776,7 +803,7 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
grouped_products: [],
menu_order: 0,
related_ids: [ 63, 77, 62, 60 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'Beanie',
@ -832,15 +859,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Red' ]
}
options: [ 'Red' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 63, 62, 61, 77 ],
stock_status: 'instock'
stock_status: 'instock',
},
{
name: 'T-Shirt',
@ -896,15 +923,15 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Gray' ]
}
options: [ 'Gray' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 67, 76, 56, 66 ],
stock_status: 'onbackorder'
stock_status: 'onbackorder',
},
{
name: 'Hoodie with Logo',
@ -960,16 +987,16 @@ const createSampleSimpleProducts = async ( categories, attributes, tags ) => {
position: 0,
visible: true,
variation: false,
options: [ 'Blue' ]
}
options: [ 'Blue' ],
},
],
default_attributes: [],
variations: [],
grouped_products: [],
menu_order: 0,
related_ids: [ 57, 65 ],
stock_status: 'instock'
}
stock_status: 'instock',
},
] );
return simpleProducts.create;
@ -985,9 +1012,9 @@ const createSampleExternalProducts = async ( categories ) => {
featured: false,
catalog_visibility: 'visible',
description:
'<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. '
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n',
'<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. ' +
'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',
sku: 'wp-pennant',
price: '11.05',
@ -1003,7 +1030,8 @@ const createSampleExternalProducts = async ( categories ) => {
downloads: [],
download_limit: 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!',
tax_status: 'taxable',
tax_class: '',
@ -1034,7 +1062,7 @@ const createSampleExternalProducts = async ( categories ) => {
grouped_products: [],
menu_order: 0,
related_ids: [],
stock_status: 'instock'
stock_status: 'instock',
},
] );
@ -1056,9 +1084,9 @@ const createSampleGroupedProduct = async ( categories ) => {
featured: false,
catalog_visibility: 'visible',
description:
'<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. '
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n',
'<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. ' +
'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',
sku: 'logo-collection',
price: '18',
@ -1102,10 +1130,10 @@ const createSampleGroupedProduct = async ( categories ) => {
attributes: [],
default_attributes: [],
variations: [],
grouped_products: logoProducts.map( p => p.id ),
grouped_products: logoProducts.map( ( p ) => p.id ),
menu_order: 0,
related_ids: [],
stock_status: 'instock'
stock_status: 'instock',
},
] );
@ -1113,9 +1141,10 @@ const createSampleGroupedProduct = async ( categories ) => {
};
const createSampleVariableProducts = async ( categories, attributes ) => {
const description = '<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. '
+ 'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const description =
'<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. ' +
'Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n';
const { body: hoodie } = await createProduct( {
name: 'Hoodie',
date_created_gmt: '2021-09-18T15:50:19',
@ -1170,7 +1199,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 0,
visible: true,
variation: true,
options: [ 'Blue', 'Green', 'Red' ]
options: [ 'Blue', 'Green', 'Red' ],
},
{
id: 0,
@ -1178,165 +1207,168 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 1,
visible: true,
variation: true,
options: [ 'Yes', 'No' ]
}
options: [ 'Yes', 'No' ],
},
],
default_attributes: [],
grouped_products: [],
menu_order: 0,
stock_status: 'instock'
stock_status: 'instock',
} );
const variationDescription =
'<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. '
+ '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. '
+ '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';
'<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. ' +
'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. ' +
'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';
const { body: hoodieVariations } = await createProductVariations( hoodie.id, [
{
date_created_gmt: '2021-09-19T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-blue-logo',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Blue' },
{ id: 0, name: 'Logo', option: 'Yes' }
],
menu_order: 0
},
{
date_created_gmt: '2021-09-20T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-blue',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Blue' },
{ id: 0, name: 'Logo', option: 'No' }
],
menu_order: 3
},
{
date_created_gmt: '2021-09-21T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-green',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Green' },
{ id: 0, name: 'Logo', option: 'No' }
],
menu_order: 2
},
{
date_created_gmt: '2021-09-22T15:50:19',
description: variationDescription,
sku: 'woo-hoodie-red',
price: '42',
regular_price: '45',
sale_price: '42',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: true,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Red' },
{ id: 0, name: 'Logo', option: 'No' }
],
menu_order: 1
}
] );
const { body: hoodieVariations } = await createProductVariations(
hoodie.id,
[
{
date_created_gmt: '2021-09-19T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-blue-logo',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Blue' },
{ id: 0, name: 'Logo', option: 'Yes' },
],
menu_order: 0,
},
{
date_created_gmt: '2021-09-20T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-blue',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Blue' },
{ id: 0, name: 'Logo', option: 'No' },
],
menu_order: 3,
},
{
date_created_gmt: '2021-09-21T15:50:20',
description: variationDescription,
sku: 'woo-hoodie-green',
price: '45',
regular_price: '45',
sale_price: '',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: false,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Green' },
{ id: 0, name: 'Logo', option: 'No' },
],
menu_order: 2,
},
{
date_created_gmt: '2021-09-22T15:50:19',
description: variationDescription,
sku: 'woo-hoodie-red',
price: '42',
regular_price: '45',
sale_price: '42',
date_on_sale_from_gmt: null,
date_on_sale_to_gmt: null,
on_sale: true,
status: 'publish',
purchasable: true,
virtual: false,
downloadable: false,
downloads: [],
download_limit: 0,
download_expiry: 0,
tax_status: 'taxable',
tax_class: '',
manage_stock: false,
stock_quantity: null,
stock_status: 'instock',
backorders: 'no',
backorders_allowed: false,
backordered: false,
low_stock_amount: null,
weight: '1.5',
dimensions: { length: '10', width: '8', height: '3' },
shipping_class: '',
attributes: [
{ id: attributes.color.id, option: 'Red' },
{ id: 0, name: 'Logo', option: 'No' },
],
menu_order: 1,
},
]
);
const { body: vneck } = await createProduct( {
name: 'V-Neck T-Shirt',
@ -1392,20 +1424,20 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
position: 0,
visible: true,
variation: true,
options: [ 'Blue', 'Green', 'Red' ]
options: [ 'Blue', 'Green', 'Red' ],
},
{
id: attributes.size.id,
position: 1,
visible: true,
variation: true,
options: [ 'Large', 'Medium', 'Small' ]
}
options: [ 'Large', 'Medium', 'Small' ],
},
],
default_attributes: [],
grouped_products: [],
menu_order: 0,
stock_status: 'instock'
stock_status: 'instock',
} );
const { body: vneckVariations } = await createProductVariations( vneck.id, [
@ -1439,7 +1471,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Blue' } ],
menu_order: 0
menu_order: 0,
},
{
date_created_gmt: '2021-09-25T15:50:19',
@ -1471,7 +1503,7 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Green' } ],
menu_order: 0
menu_order: 0,
},
{
date_created_gmt: '2021-09-26T15:50:19',
@ -1503,8 +1535,8 @@ const createSampleVariableProducts = async ( categories, attributes ) => {
dimensions: { length: '24', width: '1', height: '2' },
shipping_class: '',
attributes: [ { id: attributes.color.id, option: 'Red' } ],
menu_order: 0
}
menu_order: 0,
},
] );
return {
@ -1530,15 +1562,15 @@ const createSampleHierarchicalProducts = async () => {
return {
parent,
child,
}
};
};
const createSampleProductReviews = async ( simpleProducts ) => {
const cap = simpleProducts.find( p => p.name === 'Cap' );
const shirt = simpleProducts.find( p => p.name === 'T-Shirt' );
const sunglasses = simpleProducts.find( p => p.name === 'Sunglasses' );
const cap = simpleProducts.find( ( p ) => p.name === 'Cap' );
const shirt = simpleProducts.find( ( p ) => p.name === 'T-Shirt' );
const sunglasses = simpleProducts.find( ( p ) => p.name === 'Sunglasses' );
let { body: review1 } = await createProductReview( cap.id, {
const { body: review1 } = await createProductReview( cap.id, {
rating: 3,
review: 'Decent cap.',
reviewer: 'John Doe',
@ -1549,7 +1581,7 @@ const createSampleProductReviews = async ( simpleProducts ) => {
// See: https://github.com/woocommerce/woocommerce/issues/29906.
await updateProductReview( review1.id );
let { body: review2 } = await createProductReview( shirt.id, {
const { body: review2 } = await createProductReview( shirt.id, {
rating: 5,
review: 'The BEST shirt ever!!',
reviewer: 'Shannon Smith',
@ -1557,7 +1589,7 @@ const createSampleProductReviews = async ( simpleProducts ) => {
} );
await updateProductReview( review2.id );
let { body: review3 } = await createProductReview( sunglasses.id, {
const { body: review3 } = await createProductReview( sunglasses.id, {
rating: 1,
review: 'These are way too expensive.',
reviewer: 'Tim Frugalman',
@ -1569,9 +1601,11 @@ const createSampleProductReviews = async ( simpleProducts ) => {
};
const createSampleProductOrders = async ( simpleProducts ) => {
const single = simpleProducts.find( p => p.name === 'Single' );
const beanie = simpleProducts.find( p => p.name === 'Beanie with Logo' );
const shirt = simpleProducts.find( p => p.name === 'T-Shirt' );
const single = simpleProducts.find( ( p ) => p.name === 'Single' );
const beanie = simpleProducts.find(
( p ) => p.name === 'Beanie with Logo'
);
const shirt = simpleProducts.find( ( p ) => p.name === 'T-Shirt' );
const { body: order } = await postRequest( 'orders', {
set_paid: true,
@ -1602,10 +1636,17 @@ const createSampleData = async () => {
const shippingClasses = await createSampleShippingClasses();
const taxClasses = await createSampleTaxClasses();
const simpleProducts = await createSampleSimpleProducts( categories, attributes, tags );
const simpleProducts = await createSampleSimpleProducts(
categories,
attributes,
tags
);
const externalProducts = await createSampleExternalProducts( categories );
const groupedProducts = await createSampleGroupedProduct( categories );
const variableProducts = await createSampleVariableProducts( categories, attributes );
const variableProducts = await createSampleVariableProducts(
categories,
attributes
);
const hierarchicalProducts = await createSampleHierarchicalProducts();
const reviewIds = await createSampleProductReviews( simpleProducts );
@ -1642,19 +1683,15 @@ const deleteSampleData = async ( sampleData ) => {
orders,
} = sampleData;
const productIds = [].concat(
simpleProducts.map( p => p.id )
).concat(
externalProducts.map( p => p.id )
).concat(
groupedProducts.map( p => p.id )
).concat( [
variableProducts.hoodie.id,
variableProducts.vneck.id,
] ).concat( [
hierarchicalProducts.parent.id,
hierarchicalProducts.child.id,
] );
const productIds = []
.concat( simpleProducts.map( ( p ) => p.id ) )
.concat( externalProducts.map( ( p ) => p.id ) )
.concat( groupedProducts.map( ( p ) => p.id ) )
.concat( [ variableProducts.hoodie.id, variableProducts.vneck.id ] )
.concat( [
hierarchicalProducts.parent.id,
hierarchicalProducts.child.id,
] );
orders.forEach( async ( { id } ) => {
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.
*
* @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 {Object} payload The batch payload object. Defaults to an empty object.
* @returns {Object} The payload to send to the batch endpoint.
* @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 {Object} payload The batch payload object. Defaults to an empty object.
* @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 ) ) {
return;
}

View File

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

View File

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

View File

@ -6,7 +6,9 @@
* - `flat_rate`
* - `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 shippingMethodExample = {
@ -15,7 +17,7 @@ const getShippingMethodExample = ( methodId, cost ) => {
if ( cost !== undefined ) {
shippingMethodExample.settings = {
cost: cost,
cost,
};
}

View File

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

View File

@ -1,8 +1,13 @@
/**
* Internal dependencies
*/
const { getRequest, postRequest, putRequest, deleteRequest } = require('../utils/request');
const { coupon, shared } = require('../data');
const {
getRequest,
postRequest,
putRequest,
deleteRequest,
} = require( '../utils/request' );
const { coupon, shared } = require( '../data' );
/**
* WooCommerce Coupon endpoints.
@ -17,14 +22,15 @@ const couponsApi = {
path: 'coupons',
responseCode: 201,
payload: coupon,
coupon: async ( couponDetails ) => postRequest( 'coupons', couponDetails ),
coupon: async ( couponDetails ) =>
postRequest( 'coupons', couponDetails ),
},
retrieve: {
name: 'Retrieve a coupon',
method: 'GET',
path: 'coupons/<id>',
responseCode: 200,
coupon: async ( couponId ) => getRequest( `coupons/${couponId}` ),
coupon: async ( couponId ) => getRequest( `coupons/${ couponId }` ),
},
listAll: {
name: 'List all coupons',
@ -39,7 +45,8 @@ const couponsApi = {
path: 'coupons/<id>',
responseCode: 200,
payload: coupon,
coupon: async ( couponId, couponDetails ) => putRequest( `coupons/${couponId}`, couponDetails ),
coupon: async ( couponId, couponDetails ) =>
putRequest( `coupons/${ couponId }`, couponDetails ),
},
delete: {
name: 'Delete a coupon',
@ -47,9 +54,10 @@ const couponsApi = {
path: 'coupons/<id>',
responseCode: 200,
payload: {
force: false
force: false,
},
coupon: async ( couponId, deletePermanently ) => deleteRequest( `coupons/${couponId}`, deletePermanently ),
coupon: async ( couponId, deletePermanently ) =>
deleteRequest( `coupons/${ couponId }`, deletePermanently ),
},
batch: {
name: 'Batch update coupons',
@ -57,8 +65,9 @@ const couponsApi = {
path: 'coupons/batch',
responseCode: 200,
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
*/
const { getRequest, postRequest, putRequest, deleteRequest } = require('../utils/request');
const {
getRequest,
postRequest,
putRequest,
deleteRequest,
} = require( '../utils/request' );
/**
* WooCommerce Products endpoints.
@ -15,28 +20,31 @@ const productsApi = {
method: 'POST',
path: 'products',
responseCode: 201,
product: async ( productDetails ) => postRequest( 'products', productDetails ),
product: async ( productDetails ) =>
postRequest( 'products', productDetails ),
},
retrieve: {
name: 'Retrieve a product',
method: 'GET',
path: 'products/<id>',
responseCode: 200,
product: async ( productId ) => getRequest( `products/${productId}` ),
product: async ( productId ) => getRequest( `products/${ productId }` ),
},
listAll: {
name: 'List all products',
method: 'GET',
path: 'products',
responseCode: 200,
products: async ( productsQuery = {} ) => getRequest( 'products', productsQuery ),
products: async ( productsQuery = {} ) =>
getRequest( 'products', productsQuery ),
},
update: {
name: 'Update a product',
method: 'PUT',
path: 'products/<id>',
responseCode: 200,
product: async ( productId, productDetails ) => putRequest( `products/${productId}`, productDetails ),
product: async ( productId, productDetails ) =>
putRequest( `products/${ productId }`, productDetails ),
},
delete: {
name: 'Delete a product',
@ -44,16 +52,18 @@ const productsApi = {
path: 'products/<id>',
responseCode: 200,
payload: {
force: false
force: false,
},
product: async ( productId, deletePermanently ) => deleteRequest( `products/${productId}`, deletePermanently ),
product: async ( productId, deletePermanently ) =>
deleteRequest( `products/${ productId }`, deletePermanently ),
},
batch: {
name: 'Batch update products',
method: 'POST',
path: 'products/batch',
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 = {
refundsApi: refundsApi,
refundsApi,
};

View File

@ -9,7 +9,9 @@
"test:api": "jest --group=api",
"test:hello": "jest --group=hello",
"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": {
"type": "git",
@ -29,6 +31,10 @@
"postman-collection": "^4.1.0",
"supertest": "^6.1.4"
},
"devDependencies": {
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0"
},
"publishConfig": {
"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.
@ -6,16 +6,14 @@ const { getRequest } = require('../../utils/request');
* @group hello
*
*/
describe('Test API connectivity', () => {
it('can access a non-authenticated endpoint', async () => {
describe( 'Test API connectivity', () => {
it( 'can access a non-authenticated endpoint', async () => {
const result = await getRequest( '' );
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' );
expect( result.statusCode ).toEqual( 200 );
});
});
} );
} );

View File

@ -341,39 +341,36 @@ describe( 'Orders API tests', () => {
};
const verifyOrderPrecision = ( order, dp ) => {
expectPrecisionToMatch( order[ 'discount_total' ], dp );
expectPrecisionToMatch( order[ 'discount_tax' ], dp );
expectPrecisionToMatch( order[ 'shipping_total' ], dp );
expectPrecisionToMatch( order[ 'shipping_tax' ], dp );
expectPrecisionToMatch( order[ 'cart_tax' ], dp );
expectPrecisionToMatch( order[ 'total' ], dp );
expectPrecisionToMatch( order[ 'total_tax' ], dp );
expectPrecisionToMatch( order.discount_total, dp );
expectPrecisionToMatch( order.discount_tax, dp );
expectPrecisionToMatch( order.shipping_total, dp );
expectPrecisionToMatch( order.shipping_tax, dp );
expectPrecisionToMatch( order.cart_tax, dp );
expectPrecisionToMatch( order.total, dp );
expectPrecisionToMatch( order.total_tax, dp );
order[ 'line_items' ].forEach( ( lineItem ) => {
expectPrecisionToMatch( lineItem[ 'total' ], dp );
expectPrecisionToMatch( lineItem[ 'total_tax' ], dp );
order.line_items.forEach( ( lineItem ) => {
expectPrecisionToMatch( lineItem.total, dp );
expectPrecisionToMatch( lineItem.total_tax, dp );
} );
order[ 'tax_lines' ].forEach( ( taxLine ) => {
expectPrecisionToMatch( taxLine[ 'tax_total' ], dp );
expectPrecisionToMatch(
taxLine[ 'shipping_tax_total' ],
dp
);
order.tax_lines.forEach( ( taxLine ) => {
expectPrecisionToMatch( taxLine.tax_total, dp );
expectPrecisionToMatch( taxLine.shipping_tax_total, dp );
} );
order[ 'shipping_lines' ].forEach( ( shippingLine ) => {
expectPrecisionToMatch( shippingLine[ 'total' ], dp );
expectPrecisionToMatch( shippingLine[ 'total_tax' ], dp );
order.shipping_lines.forEach( ( shippingLine ) => {
expectPrecisionToMatch( shippingLine.total, dp );
expectPrecisionToMatch( shippingLine.total_tax, dp );
} );
order[ 'fee_lines' ].forEach( ( feeLine ) => {
expectPrecisionToMatch( feeLine[ 'total' ], dp );
expectPrecisionToMatch( feeLine[ 'total_tax' ], dp );
order.fee_lines.forEach( ( feeLine ) => {
expectPrecisionToMatch( feeLine.total, dp );
expectPrecisionToMatch( feeLine.total_tax, dp );
} );
order[ 'refunds' ].forEach( ( refund ) => {
expectPrecisionToMatch( refund[ 'total' ], dp );
order.refunds.forEach( ( refund ) => {
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 { Collection, ItemGroup, Item } = require('postman-collection');
require('dotenv').config();
const {
BASE_URL,
USER_KEY,
USER_SECRET,
USE_INDEX_PERMALINKS
} = process.env;
const fs = require( 'fs' );
const { Collection, ItemGroup, Item } = require( 'postman-collection' );
require( 'dotenv' ).config();
const { BASE_URL, USER_KEY, USER_SECRET, USE_INDEX_PERMALINKS } = process.env;
/**
* Build a Postman collection using the API testing objects.
@ -17,10 +12,10 @@ const {
// Set up our empty collection
if ( typeof USER_KEY === 'undefined' ) {
console.log('No USER_KEY was defined.');
console.log( 'No USER_KEY was defined.' );
}
if ( typeof USER_SECRET === 'undefined' ) {
console.log('No USER_SECRET was defined.');
console.log( 'No USER_SECRET was defined.' );
}
const postmanCollection = new Collection( {
@ -30,74 +25,72 @@ const postmanCollection = new Collection( {
{
key: 'username',
value: USER_KEY,
type: 'string'
type: 'string',
},
{
key: 'password',
value: USER_SECRET,
type: 'string'
type: 'string',
},
]
],
},
info: {
name: 'WooCommerce API - v3'
name: 'WooCommerce API - v3',
},
} );
// Get the API url
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
const useIndexPermalinks = ( USE_INDEX_PERMALINKS === 'true' );
let apiPath = `${BASE_URL}/?rest_route=/wc/v3`;
const useIndexPermalinks = USE_INDEX_PERMALINKS === 'true';
let apiPath = `${ BASE_URL }/?rest_route=/wc/v3`;
if ( useIndexPermalinks ) {
apiPath = `${BASE_URL}/wp-json/wc/v3`;
apiPath = `${ BASE_URL }/wp-json/wc/v3`;
}
// Set this here for use in `request.js`
global.API_PATH = `${apiPath}/`;
global.API_PATH = `${ apiPath }/`;
// Add the API path has a collection variable
postmanCollection.variables.add({
postmanCollection.variables.add( {
id: 'apiBaseUrl',
value: apiPath,
type: 'string',
});
} );
// Get the API request data
const resources = require('../../endpoints');
const resources = require( '../../endpoints' );
resourceKeys = Object.keys( resources );
// Add the requests to folders in the collection
for ( const key in resources ) {
const folder = new ItemGroup( {
name: resources[key].name,
items: []
name: resources[ key ].name,
items: [],
} );
for ( const endpoint in resources[key] ) {
let api = resources[key][endpoint];
for ( const endpoint in resources[ key ] ) {
const api = resources[ key ][ endpoint ];
// If there is no name defined, continue
if ( !api.name ) {
if ( ! api.name ) {
continue;
}
const request = new Item( {
name: api.name,
request: {
url: `{{apiBaseUrl}}/${api.path}`,
method: api.method,
body: {
mode: 'raw',
raw: JSON.stringify( api.payload ),
options: {
raw: { language: 'json' }
}
},
url: `{{apiBaseUrl}}/${ api.path }`,
method: api.method,
body: {
mode: 'raw',
raw: JSON.stringify( api.payload ),
options: {
raw: { language: 'json' },
},
},
},
} );
folder.items.add( request );
@ -110,9 +103,13 @@ for ( const key in resources ) {
const collectionJSON = postmanCollection.toJSON();
// Create a colleciton.json file. It can be imported to postman
fs.writeFile('./collection.json', JSON.stringify( collectionJSON ), ( err ) => {
if ( err ) {
console.log( err );
fs.writeFile(
'./collection.json',
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 request = require('supertest')( API_PATH );
const request = require( 'supertest' )( API_PATH );
/**
* Make a GET request.
*
* @param {string} requestPath The path of the request.
* @param {object} queryString Optional. An object of one or more `key: value` query string parameters.
* @returns {Response}
* @param {Object} queryString Optional. An object of one or more `key: value` query string parameters.
* @return {Response}
*/
const getRequest = async ( requestPath, queryString = {} ) => {
const response = await request
@ -22,8 +22,8 @@ const getRequest = async ( requestPath, queryString = {} ) => {
* Make a POST request.
*
* @param {string} requestPath The path of the request.
* @param {object} requestBody The body of the request to submit.
* @returns {Response}
* @param {Object} requestBody The body of the request to submit.
* @return {Response}
*/
const postRequest = async ( requestPath, requestBody ) => {
const response = await request
@ -38,8 +38,8 @@ const postRequest = async ( requestPath, requestBody ) => {
* Make a PUT request.
*
* @param {string} requestPath The path of the request.
* @param {object} requestBody The body of the request to submit.
* @returns {Request}
* @param {Object} requestBody The body of the request to submit.
* @return {Request}
*/
const putRequest = async ( requestPath, requestBody ) => {
const response = await request
@ -53,12 +53,12 @@ const putRequest = async ( requestPath, requestBody ) => {
/**
* 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.
* @returns {Response}
* @return {Response}
*/
const deleteRequest = async ( requestPath, deletePermanently = false ) => {
const requestBody = deletePermanently ? { force: true } : {}
const requestBody = deletePermanently ? { force: true } : {};
const response = await request
.delete( requestPath )
.set( 'Accept', 'application/json' )
@ -67,4 +67,4 @@ const deleteRequest = async ( requestPath, deletePermanently = false ) => {
return response;
};
module.exports = { getRequest, postRequest, putRequest, deleteRequest }
module.exports = { getRequest, postRequest, putRequest, deleteRequest };

View File

@ -1,4 +1,3 @@
module.exports = {
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",
"version": "0.1.0",
"description": "Utility build files for e2e packages",
"private": "true",
"main": "build.js",
"bin": {
"e2e-builds": "./build.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/woocommerce/woocommerce.git"
},
"license": "GPL-3.0+",
"bugs": {
"url": "https://github.com/woocommerce/woocommerce/issues"
},
"homepage": "https://github.com/woocommerce/woocommerce#readme",
"devDependencies": {
"@babel/core": "7.12.9",
"chalk": "^4.1.2",
"glob": "^7.2.0",
"mkdirp": "^1.0.4",
"lodash": "^4.17.21"
"name": "@woocommerce/e2e-builds",
"version": "0.1.0",
"description": "Utility build files for e2e packages",
"private": "true",
"main": "build.js",
"bin": {
"e2e-builds": "./build.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/woocommerce/woocommerce.git"
},
"license": "GPL-3.0+",
"bugs": {
"url": "https://github.com/woocommerce/woocommerce/issues"
},
"homepage": "https://github.com/woocommerce/woocommerce#readme",
"scripts": {
"lint": "eslint build.js",
"lint:fix": "eslint build.js --fix"
},
"devDependencies": {
"@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"
},
"devDependencies": {
"@babel/cli": "7.12.8",
"@babel/core": "7.12.9",
"@babel/plugin-proposal-async-generator-functions": "^7.16.4",
"@babel/plugin-proposal-object-rest-spread": "^7.16.0",
"@babel/plugin-transform-react-jsx": "^7.16.0",
"@babel/plugin-transform-runtime": "^7.16.4",
"@babel/polyfill": "7.12.1",
"@babel/preset-env": "7.12.7",
"@woocommerce/e2e-builds": "workspace:*",
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0"
"@babel/cli": "7.12.8",
"@babel/core": "7.12.9",
"@babel/plugin-proposal-async-generator-functions": "^7.16.4",
"@babel/plugin-proposal-object-rest-spread": "^7.16.0",
"@babel/plugin-transform-react-jsx": "^7.16.0",
"@babel/plugin-transform-runtime": "^7.16.4",
"@babel/polyfill": "7.12.1",
"@babel/preset-env": "7.12.7",
"@woocommerce/e2e-builds": "workspace:*",
"@woocommerce/eslint-plugin": "workspace:*",
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0",
"eslint": "^8.12.0"
},
"peerDependencies": {
"@woocommerce/api": "^0.2.0",
@ -46,14 +48,16 @@
},
"scripts": {
"postinstall": "composer install",
"prepack": "pnpm run build",
"prepare": "pnpm run build",
"clean": "rm -rf ./build ./build-module",
"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": {
"*.(t|j)s?(x)": [
"eslint --fix"
"pnpm lint:fix"
]
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -5,14 +5,17 @@
// Setup and onboarding tests
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' );
// Shopper tests
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 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 runMyAccountPageTest = require( './shopper/front-end-my-account.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' );
// Merchant tests
const runAddNewShippingZoneTest = require ( './merchant/wp-admin-settings-shipping-zones.test' );
const runAddShippingClassesTest = require('./merchant/wp-admin-settings-shipping-classes.test')
const runAddNewShippingZoneTest = require( './merchant/wp-admin-settings-shipping-zones.test' );
const runAddShippingClassesTest = require( './merchant/wp-admin-settings-shipping-classes.test' );
const runCreateCouponTest = require( './merchant/wp-admin-coupon-new.test' );
const runCreateOrderTest = require( './merchant/wp-admin-order-new.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 runProductSettingsTest = require( './merchant/wp-admin-settings-product.test' );
const runTaxSettingsTest = require( './merchant/wp-admin-settings-tax.test' );
@ -105,7 +111,7 @@ const runMerchantTests = () => {
runAnalyticsPageLoadsTest();
runInitiateWccomConnectionTest();
runAdminPageLoadTests();
}
};
const runApiTests = () => {
runExternalProductAPITest();
@ -114,7 +120,7 @@ const runApiTests = () => {
runCouponApiTest();
runOrderApiTest();
runTelemetryAPITest();
}
};
module.exports = {
runActivationTest,

View File

@ -2,7 +2,7 @@
/**
* Internal dependencies
*/
const {
const {
merchant,
waitForSelectorWithoutThrow,
} = require( '@woocommerce/e2e-utils' );
@ -10,67 +10,67 @@
/**
* External dependencies
*/
const {
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const { it, describe, beforeAll } = require( '@jest/globals' );
import deprecated from '@wordpress/deprecated';
/**
* Quick check for page title and no data message.
*
* @param pageTitle Page title in H1.
* @param element Defaults to '.d3-chart__empty-message'
* @param elementText Defaults to 'No data for the selected date range'
* @param pageTitle Page title in H1.
* @param element Defaults to '.d3-chart__empty-message'
* @param elementText Defaults to 'No data for the selected date range'
*/
const checkHeadingAndElement = async (
pageTitle, element = '.d3-chart__empty-message', elementText = 'No data for the selected date range') => {
await expect(page).toMatchElement('h1', {text: pageTitle});
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.
const found = await waitForSelectorWithoutThrow( element );
if ( found ) {
await expect(page).toMatchElement(element, {text: elementText});
await expect( page ).toMatchElement( element, { text: elementText } );
} 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 = [
['Overview'],
['Products'],
['Revenue'],
['Orders'],
['Variations'],
['Categories'],
['Coupons'],
['Taxes'],
['Downloads'],
['Stock', '.components-button > span', 'Product / Variation'],
['Settings', 'h2', 'Analytics Settings']
[ 'Overview' ],
[ 'Products' ],
[ 'Revenue' ],
[ 'Orders' ],
[ 'Variations' ],
[ 'Categories' ],
[ 'Coupons' ],
[ 'Taxes' ],
[ 'Downloads' ],
[ 'Stock', '.components-button > span', 'Product / Variation' ],
[ 'Settings', 'h2', 'Analytics Settings' ],
];
const runAnalyticsPageLoadsTest = () => {
describe('Analytics > Opening Top Level Pages', () => {
beforeAll(async () => {
describe( 'Analytics > Opening Top Level Pages', () => {
beforeAll( async () => {
await merchant.login();
});
} );
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',
async (pageTitle, element, elementText) => {
async ( pageTitle, element, elementText ) => {
// Go to the desired page and verify it
await merchant.openAnalyticsPage(pageTitle.toLowerCase());
await checkHeadingAndElement(pageTitle, element, elementText);
await merchant.openAnalyticsPage( pageTitle.toLowerCase() );
await checkHeadingAndElement( pageTitle, element, elementText );
}
);
});
}
} );
};
module.exports = runAnalyticsPageLoadsTest;

View File

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

View File

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

View File

@ -12,55 +12,79 @@ const {
} = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
const discountedPrice = simpleProductPrice - 5.00;
const simpleProductPrice = config.has( 'products.simple.price' )
? 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 orderId;
let productId;
const runOrderApplyCouponTest = () => {
describe('WooCommerce Orders > Apply coupon', () => {
beforeAll(async () => {
describe( 'WooCommerce Orders > Apply coupon', () => {
beforeAll( async () => {
productId = await createSimpleProduct();
couponCode = await createCoupon();
orderId = await createOrder( { productId, status: 'pending' } );
await merchant.login();
await merchant.goToOrder( orderId );
await page.removeAllListeners('dialog');
await page.removeAllListeners( 'dialog' );
// 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 () => {
await page.waitForSelector('button.add-coupon');
const couponDialog = await expect(page).toDisplayDialog(async () => {
await evalAndClick('button.add-coupon');
});
expect(couponDialog.message()).toMatch(couponDialogMessage);
it( 'can apply a coupon', async () => {
await page.waitForSelector( 'button.add-coupon' );
const couponDialog = await expect( page ).toDisplayDialog(
async () => {
await evalAndClick( 'button.add-coupon' );
}
);
expect( couponDialog.message() ).toMatch( couponDialogMessage );
// Accept the dialog with the coupon code
await couponDialog.accept(couponCode);
await couponDialog.accept( couponCode );
await uiUnblocked();
// Verify the coupon list is showing
await page.waitForSelector('.wc-used-coupons');
await expect(page).toMatchElement('.wc_coupon_list', { text: 'Coupon(s)' });
await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() });
await page.waitForSelector( '.wc-used-coupons' );
await expect( page ).toMatchElement( '.wc_coupon_list', {
text: 'Coupon(s)',
} );
await expect( page ).toMatchElement(
'.wc_coupon_list li.code.editable',
{
text: couponCode.toLowerCase(),
}
);
// Check that the coupon has been applied
await expect(page).toMatchElement('.wc-order-item-discount', { text: '5.00' });
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: discountedPrice });
});
await expect( page ).toMatchElement( '.wc-order-item-discount', {
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
await page.waitForSelector('.wc-used-coupons');
await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() });
await page.waitForSelector( '.wc-used-coupons' );
await expect( page ).toMatchElement(
'.wc_coupon_list li.code.editable',
{
text: couponCode.toLowerCase(),
}
);
await evalAndClick( 'a.remove-coupon' );
await uiUnblocked();
@ -68,16 +92,32 @@ const runOrderApplyCouponTest = () => {
await utils.waitForTimeout( 2000 ); // to avoid flakyness
// 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('.wc-order-item-discount', { text: '5.00' });
await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: discountedPrice });
await expect( page ).not.toMatchElement(
'.wc_coupon_list li.code.editable',
{
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
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;

View File

@ -3,72 +3,99 @@ const { createSimpleProduct } = require( '@woocommerce/e2e-utils' );
/**
* Internal dependencies
*/
const {
merchant,
createOrder,
} = require( '@woocommerce/e2e-utils' );
const { merchant, createOrder } = require( '@woocommerce/e2e-utils' );
// TODO create a function for the logic below getConfigSimpleProduct(), see: https://github.com/woocommerce/woocommerce/issues/29072
const config = require( 'config' );
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 = () => {
let orderId;
let productId;
describe('WooCommerce Merchant Flow: Orders > Customer Payment Page', () => {
beforeAll(async () => {
describe( 'WooCommerce Merchant Flow: Orders > Customer Payment Page', () => {
beforeAll( async () => {
productId = await createSimpleProduct();
orderId = await createOrder( { productId } );
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 );
// 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
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
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
await Promise.all([
expect(page).toClick( 'label[for=order_status] > a' ),
await Promise.all( [
expect( page ).toClick( 'label[for=order_status] > a' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
// Verify we landed on the customer payment page
await expect(page).toMatchElement( 'h1.entry-title' , { text: 'Pay for order' });
await expect(page).toMatchElement( 'td.product-name' , { text: simpleProductName });
await expect(page).toMatchElement( 'span.woocommerce-Price-amount.amount' , { text: simpleProductPrice });
});
await expect( page ).toMatchElement( 'h1.entry-title', {
text: 'Pay for order',
} );
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
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
await Promise.all([
expect(page).toClick( '#place_order'),
await Promise.all( [
expect( page ).toClick( '#place_order' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
// Verify we landed on the order received page
await expect(page).toMatchElement( 'h1.entry-title', { text: 'Order received' });
await expect(page).toMatchElement( 'li.woocommerce-order-overview__order.order' , { text: orderId.toString() });
await expect(page).toMatchElement( 'span.woocommerce-Price-amount.amount' , { text: simpleProductPrice });
});
});
await expect( page ).toMatchElement( 'h1.entry-title', {
text: 'Order received',
} );
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;

View File

@ -15,63 +15,71 @@ let orderId;
const orderStatus = {
processing: 'processing',
completed: 'completed'
completed: 'completed',
};
const runEditOrderTest = () => {
describe('WooCommerce Orders > Edit order', () => {
beforeAll(async () => {
describe( 'WooCommerce Orders > Edit order', () => {
beforeAll( async () => {
orderId = await createOrder( { status: orderStatus.processing } );
await merchant.login();
});
} );
afterAll( async () => {
await withRestApi.deleteOrder( orderId );
});
} );
it('can view single order', async () => {
it( 'can view single order', async () => {
// Go to "orders" page
await merchant.openAllOrdersView();
// 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
await merchant.goToOrder(orderId);
await merchant.goToOrder( orderId );
// 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
await merchant.goToOrder(orderId);
await merchant.goToOrder( orderId );
// 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`
await merchant.updateOrderStatus(orderId, 'Completed');
await merchant.updateOrderStatus( orderId, 'Completed' );
// 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(
'#woocommerce-order-notes .note_content',
{
text: 'Order status changed from Processing to Completed.',
}
);
});
} );
it('can update order details', async () => {
it( 'can update order details', async () => {
//Open order we created
await merchant.goToOrder(orderId);
await merchant.goToOrder( orderId );
// 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
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
await utils.waitForTimeout( 2000 );
@ -80,10 +88,15 @@ const runEditOrderTest = () => {
await orderPageSaveChanges();
// Verify
await expect( page ).toMatchElement( '#message', { text: 'Order updated.' } );
await verifyValueOfInputField( 'input[name=order_date]' , '2018-12-14' );
});
});
await expect( page ).toMatchElement( '#message', {
text: 'Order updated.',
} );
await verifyValueOfInputField(
'input[name=order_date]',
'2018-12-14'
);
} );
} );
describe( 'WooCommerce Orders > Edit order > Downloadable product permissions', () => {
const productName = 'TDP 001';
@ -97,12 +110,12 @@ const runEditOrderTest = () => {
await merchant.login();
} );
beforeEach(async () => {
beforeEach( async () => {
productId = await createSimpleDownloadableProduct( productName );
orderId = await createOrder( {
productId,
customerBilling ,
status: orderStatus.processing
customerBilling,
status: orderStatus.processing,
} );
} );
@ -115,7 +128,7 @@ const runEditOrderTest = () => {
// Create order without product
const newOrderId = await createOrder( {
customerBilling,
status: orderStatus.processing
status: orderStatus.processing,
} );
// Open order we created
@ -125,7 +138,7 @@ const runEditOrderTest = () => {
await merchant.addDownloadableProductPermission( productName );
// Verify new downloadable product permission details
await merchant.verifyDownloadableProductPermission( productName )
await merchant.verifyDownloadableProductPermission( productName );
// Remove order
await withRestApi.deleteOrder( newOrderId );
@ -134,7 +147,9 @@ const runEditOrderTest = () => {
it( 'can add downloadable product permissions to order with product', async () => {
// Create new downloadable product
const newProductName = 'TDP 002';
const newProductId = await createSimpleDownloadableProduct( newProductName );
const newProductId = await createSimpleDownloadableProduct(
newProductName
);
// Open order we created
await merchant.goToOrder( orderId );
@ -143,7 +158,9 @@ const runEditOrderTest = () => {
await merchant.addDownloadableProductPermission( newProductName );
// Verify new downloadable product permission details
await merchant.verifyDownloadableProductPermission( newProductName )
await merchant.verifyDownloadableProductPermission(
newProductName
);
// Remove product
await withRestApi.deleteProduct( newProductId );
@ -180,21 +197,28 @@ const runEditOrderTest = () => {
await merchant.revokeDownloadableProductPermission( productName );
// Verify
await expect( page ).not.toMatchElement( 'div.order_download_permissions', {
text: productName
} );
await expect( page ).not.toMatchElement(
'div.order_download_permissions',
{
text: productName,
}
);
} );
it( 'should not allow downloading a product if download attempts are exceeded', async () => {
// 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
const newProductId = await createSimpleDownloadableProduct( productName, 0 );
const newProductId = await createSimpleDownloadableProduct(
productName,
0
);
const newOrderId = await createOrder( {
productId: newProductId,
customerBilling,
status: orderStatus.processing
status: orderStatus.processing,
} );
// Open order we created
@ -204,7 +228,10 @@ const runEditOrderTest = () => {
const downloadPage = await merchant.openDownloadLink();
// Verify file download cannot start
await merchant.verifyCannotDownloadFromBecause( downloadPage, expectedReason );
await merchant.verifyCannotDownloadFromBecause(
downloadPage,
expectedReason
);
// Remove data
await withRestApi.deleteOrder( newOrderId );
@ -220,15 +247,21 @@ const runEditOrderTest = () => {
// Update permission so that the expiration date has already passed
// 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
const downloadPage = await merchant.openDownloadLink();
// Verify file download cannot start
await merchant.verifyCannotDownloadFromBecause( downloadPage, expectedReason );
await merchant.verifyCannotDownloadFromBecause(
downloadPage,
expectedReason
);
} );
} );
}
};
module.exports = runEditOrderTest;

View File

@ -11,14 +11,15 @@ const {
const config = require( 'config' );
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';
let orderId;
const runMerchantOrderEmailsTest = () => {
describe('Merchant > Order Action emails received', () => {
describe( 'Merchant > Order Action emails received', () => {
beforeAll( async () => {
await merchant.login();
@ -29,7 +30,9 @@ const runMerchantOrderEmailsTest = () => {
await Promise.all( [
// 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 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
it('can receive new order email', async () => {
it( 'can receive new order email', async () => {
await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } );
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } );
await expect( page ).toMatchElement( '.column-receiver', {
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 selectOrderAction( 'send_order_details_admin' );
await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } );
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } );
await expect( page ).toMatchElement( '.column-receiver', {
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 selectOrderAction( 'send_order_details' );
await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } );
await expect( page ).toMatchElement( '.column-subject', { text: `Invoice for order #${orderId} on ${storeName}` } );
await expect( page ).toMatchElement( '.column-receiver', {
text: customerEmail,
} );
await expect( page ).toMatchElement( '.column-subject', {
text: `Invoice for order #${ orderId } on ${ storeName }`,
} );
} );
} );
}
};
module.exports = runMerchantOrderEmailsTest;

View File

@ -14,7 +14,7 @@ const {
GroupedProduct,
SimpleProduct,
ProductVariation,
ExternalProduct
ExternalProduct,
} = require( '@woocommerce/api' );
const taxClasses = [
@ -33,31 +33,35 @@ const taxRates = [
{
name: 'Tax Rate Simple',
rate: '10.0000',
class: 'tax-class-simple'
class: 'tax-class-simple',
},
{
name: 'Tax Rate Variable',
rate: '20.0000',
class: 'tax-class-variable'
class: 'tax-class-variable',
},
{
name: 'Tax Rate External',
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 apiUrl = config.get('url');
const adminUsername = config.get('users.admin.username');
const adminPassword = config.get('users.admin.password');
const httpClient = HTTPClientFactory.build(apiUrl)
.withBasicAuth(adminUsername, adminPassword)
const apiUrl = config.get( 'url' );
const adminUsername = config.get( 'users.admin.username' );
const adminPassword = config.get( 'users.admin.password' );
const httpClient = HTTPClientFactory.build( apiUrl )
.withBasicAuth( adminUsername, adminPassword )
.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.addTaxRates( taxRates );
@ -67,7 +71,7 @@ const initProducts = async () => {
const simpleProduct = {
name: 'Simple Product 273722',
regularPrice: '100',
taxClass: 'Tax Class Simple'
taxClass: 'Tax Class Simple',
};
return await repo.create( simpleProduct );
};
@ -79,65 +83,65 @@ const initProducts = async () => {
attributes: [
{
name: 'Size',
option: 'Small'
option: 'Small',
},
{
name: 'Colour',
option: 'Yellow'
}
option: 'Yellow',
},
],
taxClass: 'Tax Class Variable'
taxClass: 'Tax Class Variable',
},
{
regularPrice: '300',
attributes: [
{
name: 'Size',
option: 'Medium'
option: 'Medium',
},
{
name: 'Colour',
option: 'Magenta'
}
option: 'Magenta',
},
],
taxClass: 'Tax Class Variable'
}
taxClass: 'Tax Class Variable',
},
];
const variableProductData = {
name: 'Variable Product 024611',
type: 'variable',
taxClass: 'Tax Class Variable'
taxClass: 'Tax Class Variable',
};
const variationRepo = ProductVariation.restRepository(httpClient);
const productRepo = VariableProduct.restRepository(httpClient);
const variableProduct = await productRepo.create(variableProductData);
for (const v of variations) {
await variationRepo.create(variableProduct.id, v);
const variationRepo = ProductVariation.restRepository( httpClient );
const productRepo = VariableProduct.restRepository( httpClient );
const variableProduct = await productRepo.create( variableProductData );
for ( const v of variations ) {
await variationRepo.create( variableProduct.id, v );
}
return variableProduct;
};
const initGroupedProduct = async () => {
const groupedRepo = GroupedProduct.restRepository(httpClient);
const defaultGroupedData = config.get('products.grouped');
const groupedRepo = GroupedProduct.restRepository( httpClient );
const defaultGroupedData = config.get( 'products.grouped' );
const groupedProductData = {
...defaultGroupedData,
name: 'Grouped Product 858012'
name: 'Grouped Product 858012',
};
return await groupedRepo.create(groupedProductData);
return await groupedRepo.create( groupedProductData );
};
const initExternalProduct = async () => {
const repo = ExternalProduct.restRepository(httpClient);
const defaultProps = config.get('products.external');
const repo = ExternalProduct.restRepository( httpClient );
const defaultProps = config.get( 'products.external' );
const props = {
...defaultProps,
name: 'External product 786794',
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
@ -146,66 +150,78 @@ const initProducts = async () => {
const groupedProduct = await initGroupedProduct();
const externalProduct = await initExternalProduct();
return [simpleProduct, variableProduct, groupedProduct, externalProduct];
return [ simpleProduct, variableProduct, groupedProduct, externalProduct ];
};
let products;
const runCreateOrderTest = () => {
describe('WooCommerce Orders > Add new order', () => {
beforeAll(async () => {
describe( 'WooCommerce Orders > Add new order', () => {
beforeAll( async () => {
// Initialize products for each product type
products = await initProducts();
// Login
await merchant.login();
});
} );
it('can create new order', async () => {
it( 'can create new order', async () => {
// Go to "add order" page
await merchant.openNewOrder();
// 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
await expect(page).toSelect('#order_status', 'Processing');
await expect(page).toFill('input[name=order_date]', '2018-12-13');
await expect(page).toFill('input[name=order_date_hour]', '18');
await expect(page).toFill('input[name=order_date_minute]', '55');
await expect( page ).toSelect( '#order_status', 'Processing' );
await expect( page ).toFill(
'input[name=order_date]',
'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.
const orderEdit = new AdminEdit();
await orderEdit.verifyPublish(
'.order_actions li .save_order',
'#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(
'#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
await merchant.openNewOrder();
// Open modal window for adding line items
await expect(page).toClick('button.add-line-item');
await expect(page).toClick('button.add-order-item');
await page.waitForSelector('.wc-backbone-modal-header');
await expect( page ).toClick( 'button.add-line-item' );
await expect( page ).toClick( 'button.add-order-item' );
await page.waitForSelector( '.wc-backbone-modal-header' );
// Search for each product to add, then verify that they are saved
for (const { name } of products) {
await expect(page).toClick(
for ( const { name } of products ) {
await expect( page ).toClick(
'.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',
name
);
@ -213,52 +229,60 @@ const runCreateOrderTest = () => {
'li[data-selected]'
);
await firstResult.click();
await expect(page).toMatchElement(
await expect( page ).toMatchElement(
'.wc-backbone-modal-content tr:nth-last-child(2) .wc-product-search option',
name
);
}
// 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();
// Recalculate taxes
await expect(page).toDisplayDialog(async () => {
await expect(page).toClick('.calculate-action');
});
await page.waitForSelector('th.line_tax');
await expect( page ).toDisplayDialog( async () => {
await expect( page ).toClick( '.calculate-action' );
} );
await page.waitForSelector( 'th.line_tax' );
// Save the order and verify line items
await expect(page).toClick('button.save_order');
await expect( page ).toClick( 'button.save_order' );
await page.waitForNavigation();
for (const { name } of products) {
await expect(page).toMatchElement('.wc-order-item-name', {
text: name
});
for ( const { name } of products ) {
await expect( page ).toMatchElement( '.wc-order-item-name', {
text: name,
} );
}
// Verify that the names of each tax class were shown
for (const taxRate of taxRates) {
await expect(page).toMatchElement('th.line_tax', {
text: taxRate.name
});
await expect(page).toMatchElement('.wc-order-totals td.label', {
text: taxRate.name
});
for ( const taxRate of taxRates ) {
await expect( page ).toMatchElement( 'th.line_tax', {
text: taxRate.name,
} );
await expect( page ).toMatchElement(
'.wc-order-totals td.label',
{
text: taxRate.name,
}
);
}
// Verify tax amounts
for (const amount of taxTotals) {
await expect(page).toMatchElement('td.line_tax', {
text: amount
});
await expect(page).toMatchElement('.wc-order-totals td.total', {
text: amount
});
for ( const amount of taxTotals ) {
await expect( page ).toMatchElement( 'td.line_tax', {
text: amount,
} );
await expect( page ).toMatchElement(
'.wc-order-totals td.total',
{
text: amount,
}
);
}
});
});
} );
} );
};
module.exports = runCreateOrderTest;

View File

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

View File

@ -12,84 +12,121 @@ const {
} = require( '@woocommerce/e2e-utils' );
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 = () => {
describe('WooCommerce Orders > Refund an order', () => {
describe( 'WooCommerce Orders > Refund an order', () => {
let productId;
let orderId;
let currencySymbol;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
orderId = await createOrder( {
productId,
status: 'completed'
status: 'completed',
} );
await merchant.login();
await merchant.goToOrder( orderId );
// Get the currency symbol for the store's selected currency
await page.waitForSelector('.woocommerce-Price-currencySymbol');
let currencyElement = await page.$('.woocommerce-Price-currencySymbol');
currencySymbol = await page.evaluate(el => el.textContent, currencyElement);
});
await page.waitForSelector( '.woocommerce-Price-currencySymbol' );
const currencyElement = await page.$(
'.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
await expect(page).toClick('button.refund-items');
await expect( page ).toClick( 'button.refund-items' );
// Verify the refund section shows
await page.waitForSelector('div.wc-order-refund-items', { visible: true });
await verifyCheckboxIsSet('#restock_refunded_items');
await page.waitForSelector( 'div.wc-order-refund-items', {
visible: true,
} );
await verifyCheckboxIsSet( '#restock_refunded_items' );
// Initiate a refund
await expect(page).toFill('.refund_order_item_qty', '1');
await expect(page).toFill('#refund_reason', 'No longer wanted');
await expect( page ).toFill( '.refund_order_item_qty', '1' );
await expect( page ).toFill( '#refund_reason', 'No longer wanted' );
await Promise.all([
verifyValueOfInputField('.refund_line_total', simpleProductPrice),
verifyValueOfInputField('#refund_amount', simpleProductPrice),
expect(page).toMatchElement('.do-manual-refund', { text: `Refund ${currencySymbol + simpleProductPrice} manually` }),
]);
await Promise.all( [
verifyValueOfInputField(
'.refund_line_total',
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 Promise.all([
await Promise.all( [
// Verify the product line item shows the refunded quantity and amount
expect(page).toMatchElement('.quantity .refunded', { text: '-1' }),
expect(page).toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }),
expect( page ).toMatchElement( '.quantity .refunded', {
text: '-1',
} ),
expect( page ).toMatchElement( '.line_cost .refunded', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// Verify the refund shows in the list with the amount
expect(page).toMatchElement('.refund .description', { text: 'No longer wanted' }),
expect(page).toMatchElement('.refund > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }),
expect( page ).toMatchElement( '.refund .description', {
text: 'No longer wanted',
} ),
expect( page ).toMatchElement( '.refund > .line_cost', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// 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 uiUnblocked();
await Promise.all([
await Promise.all( [
// 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
expect(page).not.toMatchElement('.quantity .refunded', { text: '-1' }),
expect(page).not.toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }),
expect( page ).not.toMatchElement( '.quantity .refunded', {
text: '-1',
} ),
expect( page ).not.toMatchElement( '.line_cost .refunded', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
// 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 > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }),
]);
});
});
expect( page ).not.toMatchElement( '.refund .description', {
text: 'No longer wanted',
} ),
expect( page ).not.toMatchElement( '.refund > .line_cost', {
text: `-${ currencySymbol + simpleProductPrice }`,
} ),
] );
} );
} );
};
module.exports = runRefundOrderTest;

View File

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

View File

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

View File

@ -11,7 +11,7 @@ const { it, describe, beforeAll } = require( '@jest/globals' );
const runPageLoadTest = () => {
describe.each( MENUS )(
' %s > Opening Top Level Pages',
'%s > Opening Top Level Pages',
( menuTitle, menuElement, subMenus ) => {
beforeAll( async () => {
await merchant.login();
@ -34,9 +34,11 @@ const runPageLoadTest = () => {
// Click sub-menu item and wait for the page to finish loading
if ( subMenuElement.length ) {
await Promise.all([
await Promise.all( [
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;
const runProductEditDetailsTest = () => {
describe('Products > Edit Product', () => {
beforeAll(async () => {
describe( 'Products > Edit Product', () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await merchant.login();
});
} );
it('can edit a product and save the changes', async () => {
await merchant.goToProduct(productId);
it( 'can edit a product and save the changes', async () => {
await merchant.goToProduct( productId );
// Clear the input fields first, then add the new values
await expect(page).toFill('#title', '');
await expect(page).toFill('#_regular_price', '');
await expect( page ).toFill( '#title', '' );
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
await expect(page).toClick('#content-html');
await expect(page).toFill('.wp-editor-area', 'This product is pretty awesome.');
await expect( page ).toClick( '#content-html' );
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
await verifyAndPublish('Product updated.');
await verifyAndPublish( 'Product updated.' );
await uiUnblocked();
// Verify the changes saved
await expect(page).toMatchElement('#title', 'Awesome product');
await expect(page).toMatchElement('.wp-editor-area', 'This product is pretty awesome.');
await expect(page).toMatchElement('#_regular_price', '100.05');
});
});
}
await expect( page ).toMatchElement( '#title', 'Awesome product' );
await expect( page ).toMatchElement(
'.wp-editor-area',
'This product is pretty awesome.'
);
await expect( page ).toMatchElement( '#_regular_price', '100.05' );
} );
} );
};
module.exports = runProductEditDetailsTest;

View File

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

View File

@ -13,22 +13,19 @@ const {
waitForSelector,
waitForSelectorWithoutThrow,
} = require( '@woocommerce/e2e-utils' );
const {
waitAndClick,
} = require( '@woocommerce/e2e-environment' );
const { waitAndClick } = require( '@woocommerce/e2e-environment' );
/**
* External dependencies
*/
const {
it,
describe,
} = require( '@jest/globals' );
const { it, describe } = require( '@jest/globals' );
const config = require( 'config' );
const VirtualProductName = '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 openNewProductAndVerify = async () => {
@ -36,13 +33,13 @@ const openNewProductAndVerify = async () => {
await merchant.openNewProduct();
// 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.
*
* @param action item you selected from the variation actions menu
* @param action item you selected from the variation actions menu
*/
const selectVariationAction = async ( action ) => {
await waitForSelector( page, 'select.variation_actions:not(:disabled)' );
@ -67,9 +64,8 @@ const expandVariations = async () => {
};
const runAddSimpleProductTest = () => {
describe('Add New Simple Product Page', () => {
it('can create simple virtual product and add it to the cart', async () => {
describe( 'Add New Simple Product Page', () => {
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
await setBrowserViewport( {
width: 970,
@ -80,30 +76,35 @@ const runAddSimpleProductTest = () => {
await openNewProductAndVerify();
// Set product data and publish the product
await expect(page).toFill('#title', VirtualProductName);
await expect(page).toClick('#_virtual');
await clickTab('General');
await expect(page).toFill('#_regular_price', simpleProductPrice);
await expect( page ).toFill( '#title', VirtualProductName );
await expect( page ).toClick( '#_virtual' );
await clickTab( 'General' );
await expect( page ).toFill(
'#_regular_price',
simpleProductPrice
);
await verifyAndPublish();
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
await shopper.goToShop();
await shopper.addToCartFromShopPage(VirtualProductName);
await shopper.addToCartFromShopPage( VirtualProductName );
await shopper.goToCart();
await shopper.productIsInCart(VirtualProductName);
await shopper.productIsInCart( VirtualProductName );
// 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
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
await setBrowserViewport( {
width: 960,
@ -114,74 +115,104 @@ const runAddSimpleProductTest = () => {
await openNewProductAndVerify();
// Set product data and publish the product
await expect(page).toFill('#title', NonVirtualProductName);
await clickTab('General');
await expect(page).toFill('#_regular_price', simpleProductPrice);
await expect( page ).toFill( '#title', NonVirtualProductName );
await clickTab( 'General' );
await expect( page ).toFill(
'#_regular_price',
simpleProductPrice
);
await verifyAndPublish();
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
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.productIsInCart(NonVirtualProductName);
await shopper.productIsInCart( NonVirtualProductName );
// Assert that the page does contain shipping calculation button
await page.waitForSelector('a.shipping-calculator-button');
await expect(page).toMatchElement('a.shipping-calculator-button');
await page.waitForSelector( 'a.shipping-calculator-button' );
await expect( page ).toMatchElement(
'a.shipping-calculator-button'
);
// Remove product from cart
await shopper.removeFromCart(NonVirtualProductName);
});
});
await shopper.removeFromCart( NonVirtualProductName );
} );
} );
};
const runAddVariableProductTest = () => {
describe('Add New Variable Product Page', () => {
it('can create product with variations', async () => {
describe( 'Add New Variable Product Page', () => {
it( 'can create product with variations', async () => {
await merchant.login();
await openNewProductAndVerify();
// Set product data
await expect(page).toFill('#title', 'Variable Product with Three Variations');
await expect(page).toSelect('#product-type', 'Variable product');
});
it('can create set variable product attributes', async () => {
await expect( page ).toFill(
'#title',
'Variable Product with Three Variations'
);
await expect( page ).toSelect(
'#product-type',
'Variable product'
);
} );
it( 'can create set variable product attributes', async () => {
// Create attributes for variations
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++ ) {
await expect(page).toClick( 'button.add_attribute', {text: 'Add'} );
await expect( page ).toClick( 'button.add_attribute', {
text: 'Add',
} );
// Wait for attribute form to load
await uiUnblocked();
await page.focus(`input[name="attribute_names[${i}]"]`);
await expect(page).toFill(`input[name="attribute_names[${i}]"]`, 'attr #' + (i + 1));
await expect(page).toFill(`textarea[name="attribute_values[${i}]"]`, 'val1 | val2');
await waitAndClick( page, `input[name="attribute_variation[${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(
`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)
await uiUnblocked();
await uiUnblocked();
});
} );
it('can create variations from all attributes', async () => {
it( 'can create variations from all attributes', async () => {
// Create variations from attributes
await waitForSelector( page, '.variations_tab' );
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
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
await uiUnblocked();
await uiUnblocked();
});
} );
it('can add variation attributes', async () => {
it( 'can add variation attributes', async () => {
await waitAndClick( page, '.variations_tab a' );
await uiUnblocked();
await waitForSelector(
page,
'select[name="attribute_attr-1[0]"]',
{
visible: true,
timeout: 5000
}
);
await waitForSelector( page, 'select[name="attribute_attr-1[0]"]', {
visible: true,
timeout: 5000,
} );
// Verify that variations were created
for ( let index = 0; index < 8; index++ ) {
@ -215,34 +244,67 @@ const runAddVariableProductTest = () => {
const val2 = { text: 'val2' };
// odd / even
let attr3 = !! ( index % 2 );
const attr3 = !! ( index % 2 );
// 0-1,4-5 / 2-3,6-7
let attr2 = ( index % 4 ) > 1;
const attr2 = index % 4 > 1;
// 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( `select[name="attribute_attr-2[${index}]"]`, attr2 ? val2 : val1 );
await expect( page ).toMatchElement( `select[name="attribute_attr-3[${index}]"]`, attr3 ? val2 : val1 );
await expect( page ).toMatchElement(
`select[name="attribute_attr-1[${ index }]"]`,
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 waitForSelectorWithoutThrow( 'input[name="variable_is_virtual[0]"]', 5 );
await setCheckbox('input[name="variable_is_virtual[0]"]');
await expect(page).toFill('input[name="variable_regular_price[0]"]', '9.99');
await waitForSelectorWithoutThrow(
'input[name="variable_is_virtual[0]"]',
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 expect(page).toFill('input[name="variable_regular_price[1]"]', '11.99');
await setCheckbox( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toFill(
'input[name="variable_regular_price[1]"]',
'11.99'
);
await setCheckbox('input[name="variable_manage_stock[2]"]');
await expect(page).toFill('input[name="variable_regular_price[2]"]', '20');
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 setCheckbox( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toFill(
'input[name="variable_regular_price[2]"]',
'20'
);
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();
});
} );
it( 'can bulk-edit variations', async () => {
// Verify that all 'Downloadable' checkboxes are UNCHECKED.
@ -258,7 +320,7 @@ const runAddVariableProductTest = () => {
}
// Perform the 'Toggle "Downloadable"' bulk action
await selectVariationAction('Toggle "Downloadable"');
await selectVariationAction( 'Toggle "Downloadable"' );
await clickGoButton();
await uiUnblocked();
@ -407,7 +469,7 @@ const runAddVariableProductTest = () => {
const variationsCount = await page.$$( '.woocommerce_variation' );
expect( variationsCount ).toHaveLength( 0 );
} );
});
} );
};
module.exports = {

View File

@ -3,76 +3,88 @@
/**
* Internal dependencies
*/
const {
merchant,
createSimpleProduct
} = require( '@woocommerce/e2e-utils' );
const { merchant, createSimpleProduct } = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
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 = () => {
describe('Products > Search and View a product', () => {
beforeAll(async () => {
describe( 'Products > Search and View a product', () => {
beforeAll( async () => {
await createSimpleProduct();
// 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();
});
} );
beforeEach(async () => {
beforeEach( async () => {
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
let searchString = simpleProductName.substring(0, (simpleProductName.length / 2));
await expect(page).toFill('#post-search-input', searchString);
const searchString = simpleProductName.substring(
0,
simpleProductName.length / 2
);
await expect( page ).toFill( '#post-search-input', searchString );
// Click search and wait for the page to finish loading
await Promise.all( [
page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
// 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 () => {
await expect(page).toFill('#post-search-input', simpleProductName);
it( "can view a product's details after search", async () => {
await expect( page ).toFill(
'#post-search-input',
simpleProductName
);
await Promise.all( [
page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
// Click to view the product and wait for the page to finish loading
await Promise.all( [
expect(page).toClick('.row-title', simpleProductName),
expect( page ).toClick( '.row-title', simpleProductName ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
await expect(page).toMatchElement('#title', simpleProductName);
await expect(page).toMatchElement('#_regular_price', simpleProductPrice);
});
await expect( page ).toMatchElement( '#title', simpleProductName );
await expect( page ).toMatchElement(
'#_regular_price',
simpleProductPrice
);
} );
it('returns no results for non-existent product search', async () => {
await expect(page).toFill('#post-search-input', 'abcd1234');
it( 'returns no results for non-existent product search', async () => {
await expect( page ).toFill( '#post-search-input', 'abcd1234' );
// Click search and wait for the page to finish loading
await Promise.all( [
page.click( '#search-submit' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
] );
// 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;

View File

@ -11,74 +11,126 @@ const {
/**
* External dependencies
*/
const {
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const { it, describe, beforeAll } = require( '@jest/globals' );
const runUpdateGeneralSettingsTest = () => {
describe('WooCommerce General Settings', () => {
beforeAll(async () => {
describe( 'WooCommerce General Settings', () => {
beforeAll( async () => {
await merchant.login();
});
} );
it('can update settings', async () => {
it( 'can update settings', async () => {
// Go to general settings page
await merchant.openSettings('general');
await merchant.openSettings( 'general' );
// 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,
// 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();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to all countries'}),
]);
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
expect( page ).toMatchElement(
'#woocommerce_allowed_countries',
{ text: 'Sell to all countries' }
),
] );
// 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();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).toMatchElement('select[name="woocommerce_default_country"]', {text: 'United States (US) — California'}),
]);
await Promise.all( [
expect( page ).toMatchElement( '#message', {
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).
// This will makes specific countries option appears.
await expect(page).toSelect('#woocommerce_allowed_countries', 'Sell to specific countries');
await expect(page).toSelect('select[name="woocommerce_specific_allowed_countries[]"]', 'United States (US)');
await expect( page ).toSelect(
'#woocommerce_allowed_countries',
'Sell to specific countries'
);
await expect( page ).toSelect(
'select[name="woocommerce_specific_allowed_countries[]"]',
'United States (US)'
);
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to specific countries'}),
expect(page).toMatchElement('select[name="woocommerce_specific_allowed_countries[]"]', {text: 'United States (US)'}),
]);
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
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.
await expect(page).toFill('#woocommerce_price_thousand_sep', ',');
await expect(page).toFill('#woocommerce_price_decimal_sep', '.');
await expect(page).toFill('#woocommerce_price_num_decimals', '2');
await expect( page ).toFill(
'#woocommerce_price_thousand_sep',
','
);
await expect( page ).toFill(
'#woocommerce_price_decimal_sep',
'.'
);
await expect( page ).toFill(
'#woocommerce_price_num_decimals',
'2'
);
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
verifyValueOfInputField('#woocommerce_price_thousand_sep', ','),
verifyValueOfInputField('#woocommerce_price_decimal_sep', '.'),
verifyValueOfInputField('#woocommerce_price_num_decimals', '2'),
]);
});
});
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
verifyValueOfInputField(
'#woocommerce_price_thousand_sep',
','
),
verifyValueOfInputField(
'#woocommerce_price_decimal_sep',
'.'
),
verifyValueOfInputField(
'#woocommerce_price_num_decimals',
'2'
),
] );
} );
} );
};
module.exports = runUpdateGeneralSettingsTest;

View File

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

View File

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

View File

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

View File

@ -14,165 +14,254 @@ const {
/**
* External dependencies
*/
const {
it,
describe,
beforeAll,
} = require( '@jest/globals' );
const { it, describe, beforeAll } = require( '@jest/globals' );
const runTaxSettingsTest = () => {
describe('WooCommerce Tax Settings', () => {
beforeAll(async () => {
describe( 'WooCommerce Tax Settings', () => {
beforeAll( async () => {
await merchant.login();
});
} );
it('can enable tax calculation', async () => {
it( 'can enable tax calculation', async () => {
// Go to general settings page
await merchant.openSettings('general');
await merchant.openSettings( 'general' );
// 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
await setCheckbox('input[name="woocommerce_calc_taxes"]');
await setCheckbox( 'input[name="woocommerce_calc_taxes"]' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
verifyCheckboxIsSet('#woocommerce_calc_taxes'),
]);
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
// 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
await merchant.openSettings('tax');
await merchant.openSettings( 'tax' );
// 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
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
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
await expect(page).toSelect('#woocommerce_shipping_tax_class', 'Standard');
await expect( page ).toSelect(
'#woocommerce_shipping_tax_class',
'Standard'
);
// Leave rounding unchecked (no-op)
// 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
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
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();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
verifyValueOfInputField('input[name="woocommerce_prices_include_tax"][value="no"]', 'no'),
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'}),
]);
});
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
verifyValueOfInputField(
'input[name="woocommerce_prices_include_tax"][value="no"]',
'no'
),
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
await merchant.openSettings('tax');
await merchant.openSettings( 'tax' );
// 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
await clearAndFillInput('#woocommerce_tax_classes', '');
await clearAndFillInput( '#woocommerce_tax_classes', '' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).toMatchElement('#woocommerce_tax_classes', {text: ''}),
]);
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
expect( page ).toMatchElement( '#woocommerce_tax_classes', {
text: '',
} ),
] );
// Add a "fancy" tax class
await clearAndFillInput('#woocommerce_tax_classes', 'Fancy');
await clearAndFillInput( '#woocommerce_tax_classes', 'Fancy' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).toMatchElement('ul.subsubsub > li > a', {text: 'Fancy rates'}),
]);
});
await Promise.all( [
expect( page ).toMatchElement( '#message', {
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
await merchant.openSettings('tax', 'fancy');
await merchant.openSettings( 'tax', 'fancy' );
// 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('ul.subsubsub > li > a.current', {text: 'Fancy rates'});
await expect( page ).toMatchElement( 'a.nav-tab-active', {
text: 'Tax',
} );
await expect( page ).toMatchElement(
'ul.subsubsub > li > a.current',
{
text: 'Fancy rates',
}
);
// Create a state tax
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('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');
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(
'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
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('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"]');
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(
'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)
await expect(page).toClick('button.woocommerce-save-button');
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 2 tax rates
expect(await page.$$('#rates tr')).toHaveLength(2);
expect( await page.$$( '#rates tr' ) ).toHaveLength( 2 );
// Delete federal rate
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( '#rates tr:nth-child(2) input' );
await expect( page ).toClick( '.wc_tax_rates a.remove_tax_rates' );
// Save changes (AJAX here)
await expect(page).toClick('button.woocommerce-save-button');
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 1 rate
expect(await page.$$('#rates tr')).toHaveLength(1);
await expect(page).toMatchElement(
expect( await page.$$( '#rates tr' ) ).toHaveLength( 1 );
await expect( page ).toMatchElement(
'#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
await merchant.openSettings('tax');
await merchant.openSettings( 'tax' );
// 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
await clearAndFillInput('#woocommerce_tax_classes', ' ');
await clearAndFillInput( '#woocommerce_tax_classes', ' ' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all([
expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}),
expect(page).not.toMatchElement('ul.subsubsub > li > a', {text: 'Fancy rates'}),
]);
});
});
await Promise.all( [
expect( page ).toMatchElement( '#message', {
text: 'Your settings have been saved.',
} ),
expect( page ).not.toMatchElement( 'ul.subsubsub > li > a', {
text: 'Fancy rates',
} ),
] );
} );
} );
};
module.exports = runTaxSettingsTest;

View File

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

View File

@ -16,56 +16,79 @@ const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
const { it, describe, beforeAll } = require( '@jest/globals' );
const runCartApplyCouponsTest = () => {
describe('Cart applying coupons', () => {
describe( 'Cart applying coupons', () => {
let productId;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await shopper.emptyCart();
await shopper.goToShop();
await shopper.addToCartFromShopPage( productId );
await uiUnblocked();
await shopper.goToCart();
});
} );
it.each( getCouponsTable() )( 'allows cart to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
const coupon = await getCouponId( couponType );
await applyCoupon(coupon);
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
it.each( getCouponsTable() )(
'allows cart to apply %s coupon',
async ( couponType, cartDiscount, orderTotal ) => {
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
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.cart-discount .amount', cartDiscount);
await expect(page).toMatchElement('.order-total .amount', orderTotal);
await removeCoupon(coupon);
});
// Verify discount applied and order total
await page.waitForSelector( '.order-total' );
await expect( page ).toMatchElement(
'.cart-discount .amount',
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' );
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 );
// Verify only one discount 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('.order-total .amount', {text: '$4.99'});
});
await expect( page ).toMatchElement( '.cart-discount .amount', {
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 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
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
});
await page.waitForSelector( '.order-total' );
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 product' ) );
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
});
});
await expect( page ).toMatchElement( '.order-total .amount', {
text: '$9.99',
} );
} );
} );
};
module.exports = runCartApplyCouponsTest;

View File

@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
const {
const {
shopper,
merchant,
createSimpleProduct,
@ -14,63 +14,59 @@
/**
* External dependencies
*/
const {
it,
describe,
beforeAll,
afterAll,
} = require( '@jest/globals' );
const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
const runCartRedirectionTest = () => {
describe('Cart > Redirect to cart from shop', () => {
describe( 'Cart > Redirect to cart from shop', () => {
let simplePostIdValue;
beforeAll(async () => {
beforeAll( async () => {
simplePostIdValue = await createSimpleProduct();
// Set checkbox in settings to enable cart redirection
await merchant.login();
await merchant.openSettings('products');
await setCheckbox('#woocommerce_cart_redirect_after_add');
await merchant.openSettings( 'products' );
await setCheckbox( '#woocommerce_cart_redirect_after_add' );
await settingsPageSaveChanges();
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();
// Add to cart from shop page
const addToCartXPath = `//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 addToCartXPath =
`//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 + ']' );
addToCartButton.click();
await utils.waitForTimeout( 1000 ); // to avoid flakiness
await shopper.productIsInCart(simpleProductName);
await shopper.productIsInCart( simpleProductName );
await shopper.removeFromCart( simplePostIdValue );
});
} );
it('can redirect user to cart from detail page', async () => {
await shopper.goToProduct(simplePostIdValue);
it( 'can redirect user to cart from detail page', async () => {
await shopper.goToProduct( simplePostIdValue );
// Add to cart from detail page
await shopper.addToCart();
await utils.waitForTimeout( 1000 ); // to avoid flakiness
await shopper.productIsInCart(simpleProductName);
await shopper.productIsInCart( simpleProductName );
await shopper.removeFromCart( simplePostIdValue );
});
} );
afterAll(async () => {
afterAll( async () => {
await merchant.login();
await merchant.openSettings('products');
await unsetCheckbox('#woocommerce_cart_redirect_after_add');
await merchant.openSettings( 'products' );
await unsetCheckbox( '#woocommerce_cart_redirect_after_add' );
await settingsPageSaveChanges();
});
});
} );
} );
};
module.exports = runCartRedirectionTest;

View File

@ -15,83 +15,95 @@ const { it, describe, beforeAll } = require( '@jest/globals' );
const config = require( 'config' );
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 runCartPageTest = () => {
describe('Cart page', () => {
describe( 'Cart page', () => {
let productId;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault( 'general', false );
await withRestApi.resetSettingsGroupToDefault( 'products', 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 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.addToCartFromShopPage( productId );
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.addToCartFromShopPage( productId );
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.setCartQuantity(simpleProductName, 4);
await expect(page).toClick('button', {text: 'Update cart'});
await shopper.setCartQuantity( simpleProductName, 4 );
await expect( page ).toClick( 'button', { text: 'Update cart' } );
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.removeFromCart( productId );
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.addToCartFromShopPage( productId );
await shopper.goToCart();
await shopper.productIsInCart(simpleProductName, 1);
await expect(page).toMatchElement('.cart-subtotal .amount', {text: `$${ singleProductPrice }`});
await shopper.productIsInCart( simpleProductName, 1 );
await expect( page ).toMatchElement( '.cart-subtotal .amount', {
text: `$${ singleProductPrice }`,
} );
await shopper.setCartQuantity(simpleProductName, 2);
await expect(page).toClick('button', {text: 'Update cart'});
await shopper.setCartQuantity( simpleProductName, 2 );
await expect( page ).toClick( 'button', { text: 'Update cart' } );
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 Promise.all([
page.waitForNavigation({waitUntil: 'networkidle0'}),
expect(page).toClick('.checkout-button', {text: 'Proceed to checkout'}),
]);
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
expect( page ).toClick( '.checkout-button', {
text: 'Proceed to checkout',
} ),
] );
await expect(page).toMatchElement('#order_review');
});
});
await expect( page ).toMatchElement( '#order_review' );
} );
} );
};
module.exports = runCartPageTest;

View File

@ -16,12 +16,11 @@ const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
*/
const { it, describe, beforeAll } = require( '@jest/globals' );
const runCheckoutApplyCouponsTest = () => {
describe('Checkout coupons', () => {
describe( 'Checkout coupons', () => {
let productId;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await shopper.emptyCart();
await shopper.goToShop();
@ -29,48 +28,71 @@ const runCheckoutApplyCouponsTest = () => {
await shopper.addToCartFromShopPage( productId );
await uiUnblocked();
await shopper.goToCheckout();
});
} );
it.each( getCouponsTable() )( 'allows checkout to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
const coupon = await getCouponId( couponType );
await applyCoupon(coupon);
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
it.each( getCouponsTable() )(
'allows checkout to apply %s coupon',
async ( couponType, cartDiscount, orderTotal ) => {
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
await page.waitForSelector('.order-total');
// Wait for page to expand total calculations to avoid flakyness
await page.waitForSelector( '.order-total' );
// Verify discount applied and order total
await expect(page).toMatchElement('.cart-discount .amount', cartDiscount);
await expect(page).toMatchElement('.order-total .amount', orderTotal);
await removeCoupon(coupon);
});
// Verify discount applied and order total
await expect( page ).toMatchElement(
'.cart-discount .amount',
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' );
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 );
// Verify only one discount 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('.order-total .amount', {text: '$4.99'});
});
await expect( page ).toMatchElement( '.cart-discount .amount', {
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 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
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
});
await page.waitForSelector( '.order-total' );
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 product' ) );
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
});
});
await expect( page ).toMatchElement( '.order-total .amount', {
text: '$9.99',
} );
} );
} );
};
module.exports = runCheckoutApplyCouponsTest;

View File

@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
const {
const {
shopper,
merchant,
createSimpleProduct,
@ -21,21 +21,28 @@ const config = require( 'config' );
const customerBilling = config.get( 'addresses.customer.billing' );
const runCheckoutCreateAccountTest = () => {
describe('Shopper Checkout Create Account', () => {
describe( 'Shopper Checkout Create Account', () => {
let productId;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await withRestApi.deleteCustomerByEmail( customerBilling.email );
// Set checkbox for creating an account during checkout
await merchant.login();
await merchant.openSettings('account');
await setCheckbox('#woocommerce_enable_signup_and_login_from_checkout');
await merchant.openSettings( 'account' );
await setCheckbox(
'#woocommerce_enable_signup_and_login_from_checkout'
);
await settingsPageSaveChanges();
// 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();
@ -46,25 +53,29 @@ const runCheckoutCreateAccountTest = () => {
await shopper.goToCheckout();
}, 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
await shopper.fillBillingDetails( customerBilling );
await uiUnblocked();
// Set checkbox for creating account during checkout
await setCheckbox('#createaccount');
await setCheckbox( '#createaccount' );
// Place an order
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.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;

View File

@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
const {
const {
shopper,
merchant,
createSimpleProduct,
@ -15,19 +15,19 @@
*/
const { it, describe, beforeAll } = require( '@jest/globals' );
const config = require('config');
const config = require( 'config' );
const runCheckoutLoginAccountTest = () => {
describe('Shopper Checkout Login Account', () => {
describe( 'Shopper Checkout Login Account', () => {
let productId;
beforeAll(async () => {
beforeAll( async () => {
productId = await createSimpleProduct();
// Set checkbox for logging to account during checkout
await merchant.login();
await merchant.openSettings('account');
await setCheckbox('#woocommerce_enable_checkout_login_reminder');
await merchant.openSettings( 'account' );
await setCheckbox( '#woocommerce_enable_checkout_login_reminder' );
await settingsPageSaveChanges();
await merchant.logout();
@ -36,42 +36,54 @@ const runCheckoutLoginAccountTest = () => {
await shopper.addToCartFromShopPage( productId );
await uiUnblocked();
await shopper.goToCheckout();
});
} );
afterAll( async () => {
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
await page.waitForSelector('.woocommerce-form-login-toggle');
await expect(page).toClick('.woocommerce-info > a.showlogin');
await page.waitForSelector( '.woocommerce-form-login-toggle' );
await expect( page ).toClick( '.woocommerce-info > a.showlogin' );
// Fill shopper's login credentials and proceed further
await page.type( '#username', config.get('users.customer.username') );
await page.type( '#password', config.get('users.customer.password') );
await page.type(
'#username',
config.get( 'users.customer.username' )
);
await page.type(
'#password',
config.get( 'users.customer.password' )
);
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle0'}),
page.click('button[name="login"]'),
]);
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button[name="login"]' ),
] );
// Place an order
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
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
await shopper.gotoMyAccount();
await Promise.all( [
await expect(page.url()).toMatch('my-account/'),
await expect(page).toMatchElement('h1', {text: 'My account'}),
await expect( page.url() ).toMatch( 'my-account/' ),
await expect( page ).toMatchElement( 'h1', {
text: 'My account',
} ),
] );
});
});
} );
} );
};
module.exports = runCheckoutLoginAccountTest;

View File

@ -16,7 +16,9 @@ const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
const config = require( 'config' );
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 threeProductPrice = singleProductPrice * 3;
const fourProductPrice = singleProductPrice * 4;
@ -28,128 +30,233 @@ let customerOrderId;
const runCheckoutPageTest = () => {
let productId;
describe('Checkout page', () => {
beforeAll(async () => {
describe( 'Checkout page', () => {
beforeAll( async () => {
productId = await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault( 'general', false );
await withRestApi.resetSettingsGroupToDefault( 'products', false );
await withRestApi.resetSettingsGroupToDefault( 'tax', false );
// 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.
await withRestApi.updateSettingOption( 'general', 'woocommerce_default_country', { value: 'US:CA' } );
await withRestApi.updateSettingOption(
'general',
'woocommerce_default_country',
{ value: 'US:CA' }
);
// 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
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
// Enable BACS payment method
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true }, false );
await withRestApi.updatePaymentGateway(
'bacs',
{ enabled: true },
false
);
// 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 );
});
} );
it('should display cart items in order review', async () => {
it( 'should display cart items in order review', async () => {
await shopper.goToShop();
await shopper.addToCartFromShopPage( productId );
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.addToCartFromShopPage( productId );
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', {text: 'Cash on delivery'});
});
await expect( page ).toClick( '.wc_payment_method label', {
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.addToCartFromShopPage( productId );
await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `3`, threeProductPrice, threeProductPrice);
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
});
await shopper.productIsInCheckout(
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.addToCartFromShopPage( productId );
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
await page.evaluate(() => {
document.querySelector('#ship-to-different-address-checkbox').click();
});
await page.evaluate( () => {
document
.querySelector( '#ship-to-different-address-checkbox' )
.click();
} );
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.addToCartFromShopPage( productId );
await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `5`, fiveProductPrice, fiveProductPrice);
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
await shopper.productIsInCheckout(
simpleProductName,
`5`,
fiveProductPrice,
fiveProductPrice
);
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked();
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'});
await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'});
await expect( page ).toClick( '.wc_payment_method label', {
text: 'Cash on delivery',
} );
await expect( page ).toMatchElement( '.payment_method_cod', {
text: 'Pay with cash upon delivery.',
} );
await uiUnblocked();
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
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order');
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement);
guestOrderId = orderReceivedText.split(/(\s+)/)[6].toString();
});
const orderReceivedHtmlElement = await page.$(
'.woocommerce-order-overview__order.order'
);
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.goToShop();
await shopper.addToCartFromShopPage( productId );
await shopper.goToCheckout();
await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice);
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
await shopper.productIsInCheckout(
simpleProductName,
`1`,
singleProductPrice,
singleProductPrice
);
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked();
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'});
await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'});
await expect( page ).toClick( '.wc_payment_method label', {
text: 'Cash on delivery',
} );
await expect( page ).toMatchElement( '.payment_method_cod', {
text: 'Pay with cash upon delivery.',
} );
await uiUnblocked();
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
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order');
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement);
customerOrderId = orderReceivedText.split(/(\s+)/)[6].toString();
});
const orderReceivedHtmlElement = await page.$(
'.woocommerce-order-overview__order.order'
);
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.verifyOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice);
await merchant.verifyOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true);
});
});
await merchant.verifyOrder(
guestOrderId,
simpleProductName,
singleProductPrice,
5,
fiveProductPrice
);
await merchant.verifyOrder(
customerOrderId,
simpleProductName,
singleProductPrice,
1,
singleProductPrice,
true
);
} );
} );
};
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 runMyAccountCreateAccountTest = () => {
describe('Shopper My Account Create Account', () => {
beforeAll(async () => {
describe( 'Shopper My Account Create Account', () => {
beforeAll( async () => {
await withRestApi.deleteCustomerByEmail( customerEmailAddress );
await merchant.login();
// Set checkbox in the settings to enable registration in my account
await merchant.openSettings('account');
await setCheckbox('#woocommerce_enable_myaccount_registration');
await merchant.openSettings( 'account' );
await setCheckbox( '#woocommerce_enable_myaccount_registration' );
await settingsPageSaveChanges();
await merchant.logout();
});
} );
afterAll( async () => {
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 page.waitForSelector('.woocommerce-form-register');
await expect(page).toFill('input#reg_email', customerEmailAddress);
await expect(page).toClick('button[name="register"]');
await page.waitForNavigation({waitUntil: 'networkidle0'});
await expect(page).toMatchElement('h1', 'My account');
await page.waitForSelector( '.woocommerce-form-register' );
await expect( page ).toFill(
'input#reg_email',
customerEmailAddress
);
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
await merchant.login();
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;

View File

@ -6,7 +6,7 @@ const {
shopper,
merchant,
createSimpleProduct,
uiUnblocked
uiUnblocked,
} = require( '@woocommerce/e2e-utils' );
/**
@ -20,39 +20,46 @@ const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
const runMyAccountPayOrderTest = () => {
describe('Customer can pay for their order through My Account', () => {
beforeAll(async () => {
describe( 'Customer can pay for their order through My Account', () => {
beforeAll( async () => {
simplePostIdValue = await createSimpleProduct();
await shopper.login();
await shopper.goToProduct(simplePostIdValue);
await shopper.addToCart(simpleProductName);
await shopper.goToProduct( simplePostIdValue );
await shopper.addToCart( simpleProductName );
await shopper.goToCheckout();
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
await shopper.fillBillingDetails(
config.get( 'addresses.customer.billing' )
);
await uiUnblocked();
await shopper.placeOrder();
// 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.updateOrderStatus(orderNum, 'Pending payment');
await merchant.updateOrderStatus( orderNum, 'Pending payment' );
await merchant.logout();
});
} );
afterAll( async () => {
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.goToOrders();
await expect(page).toClick('a.woocommerce-button.button.pay');
await page.waitForNavigation({waitUntil: 'networkidle0'});
await expect(page).toMatchElement('.entry-title', {text: 'Pay for order'});
await expect( page ).toClick( 'a.woocommerce-button.button.pay' );
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
await expect( page ).toMatchElement( '.entry-title', {
text: 'Pay for order',
} );
await shopper.placeOrder();
await expect(page).toMatch('Order received');
});
});
}
await expect( page ).toMatch( 'Order received' );
} );
} );
};
module.exports = runMyAccountPayOrderTest;

View File

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

View File

@ -2,7 +2,7 @@
/**
* Internal dependencies
*/
const {
const {
shopper,
merchant,
createSimpleProduct,
@ -23,38 +23,45 @@ const customerEmail = config.get( 'addresses.customer.billing.email' );
const storeName = 'WooCommerce Core E2E Test Suite';
const runOrderEmailReceivingTest = () => {
describe('Shopper Order Email Receiving', () => {
beforeAll(async () => {
describe( 'Shopper Order Email Receiving', () => {
beforeAll( async () => {
simplePostIdValue = await createSimpleProduct();
await merchant.login();
await deleteAllEmailLogs();
await merchant.logout();
});
} );
afterAll( async () => {
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();
// Go to the shop and purchase an item
await shopper.goToProduct(simplePostIdValue);
await shopper.addToCart(simpleProductName);
await shopper.goToProduct( simplePostIdValue );
await shopper.addToCart( simpleProductName );
await shopper.goToCheckout();
await uiUnblocked();
await shopper.placeOrder();
// 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
await merchant.login();
await merchant.openEmailLog();
await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } );
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } );
});
});
await expect( page ).toMatchElement( '.column-receiver', {
text: customerEmail,
} );
await expect( page ).toMatchElement( '.column-subject', {
text: `[${ storeName }]: New order #${ orderId }`,
} );
} );
} );
};
module.exports = runOrderEmailReceivingTest;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
module.exports = {
extends: [
'plugin:jest/recommended',
],
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
env: {
'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",
"app-root-path": "^3.0.0",
"commander": "4.1.1",
"config": "3.3.3",
"config": "3.3.3",
"jest": "^25.1.0",
"jest-circus": "25.1.0",
"jest-circus": "25.1.0",
"jest-each": "25.5.0",
"jest-puppeteer": "^4.4.0",
"node-stream-zip": "^1.13.6",
@ -53,7 +53,7 @@
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/browserslist-config": "^4.1.0",
"@wordpress/eslint-plugin": "7.3.0",
"@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.1.0",
"ndb": "^1.1.5",
"semver": "^7.3.2"
@ -66,7 +66,7 @@
"clean": "rm -rf ./build ./build-module",
"compile": "e2e-builds",
"build": "pnpm run clean && pnpm run compile",
"prepack": "pnpm run build",
"prepare": "pnpm run build",
"docker:up": "./bin/docker-compose.sh up",
"docker:wait": "bash ./bin/wait-for-build.sh",
"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-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",
"lint": "eslint src"
"lint": "eslint src --ext=js,ts,tsx",
"lint:fix": "eslint src --ext=js,ts,tsx --fix"
},
"bin": {
"wc-e2e": "bin/wc-e2e.sh"
},
"lint-staged": {
"*.(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.
*
* @param testName
* @param callback
* @param testName
* @param callback
* @return {Promise<void>}
*/
const screenshotTest = async ( testName, callback ) => {

View File

@ -67,7 +67,7 @@ const initializeSlack = () => {
/**
* Post a message to a Slack channel for a failed test.
*
* @param testName
* @param testName
* @return {Promise<void>}
*/
async function sendFailedTestMessageToSlack( testName ) {
@ -131,7 +131,7 @@ async function sendFailedTestMessageToSlack( testName ) {
/**
* Post a screenshot to a Slack channel for a failed test.
*
* @param screenshotOfFailedTest
* @param screenshotOfFailedTest
* @return {Promise<void>}
*/
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",
"compile": "e2e-builds",
"build": "pnpm run clean && pnpm run compile",
"prepack": "pnpm run build",
"lint": "eslint src"
"prepare": "pnpm run build",
"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
*
* @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 ) => {
// Wait for auto save
@ -53,7 +53,7 @@ const verifyAndPublish = async ( noticeText ) => {
/**
* 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>}
*/
const waitAndClickPrimary = async ( waitForNetworkIdle = true ) => {
@ -231,8 +231,8 @@ const completeOnboardingWizard = async () => {
/**
* Create simple product.
*
* @param {string} productTitle Defaults to Simple Product. Customizable title.
* @param {string} productPrice Defaults to $9.99. Customizable pricing.
* @param {string} productTitle Defaults to Simple Product. Customizable title.
* @param {string} productPrice Defaults to $9.99. Customizable pricing.
* @param {Object} additionalProps Defaults to nothing. Additional product properties.
*/
const createSimpleProduct = async (
@ -252,9 +252,9 @@ const createSimpleProduct = async (
/**
* Create simple product with categories
*
* @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 categoryName Product's category 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 categoryName Product's category which can be changed when writing a test
*/
const createSimpleProductWithCategory = async (
productName,
@ -281,10 +281,10 @@ const createSimpleProductWithCategory = async (
/**
* Create simple downloadable product
*
* @param name Product's name. Defaults to 'Simple Product' (see createSimpleProduct definition).
* @param downloadLimit Product's download limit. Defaults to '-1' (unlimited).
* @param downloadName Product's download name. Defaults to 'Single'.
* @param price Product's price. Defaults to '$9.99' (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 downloadName Product's download name. Defaults to 'Single'.
* @param price Product's price. Defaults to '$9.99' (see createSimpleProduct definition).
*/
const createSimpleDownloadableProduct = async (
name,
@ -312,7 +312,7 @@ const createSimpleDownloadableProduct = async (
* Create variable product.
* 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
*/
const createVariableProduct = async ( varProduct = defaultVariableProduct ) => {
@ -383,7 +383,7 @@ const createVariableProduct = async ( varProduct = defaultVariableProduct ) => {
/**
* 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
*/
const createGroupedProduct = async (
@ -446,7 +446,7 @@ const createOrder = async ( orderOptions = {} ) => {
/**
* 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' ) => {
// 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`
* using the "Batch Create Order" API.
*
* @param statuses Array of order statuses
* @param statuses Array of order statuses
*/
const batchCreateOrders = async ( statuses ) => {
const defaultOrder = config.get( 'orders.basicPaidOrder' );
@ -505,8 +505,8 @@ const batchCreateOrders = async ( statuses ) => {
/**
* Adds a product to an order in the merchant.
*
* @param orderId ID of the order to add the product to.
* @param productName Name of the product being added to the order.
* @param orderId ID of the order to add the product to.
* @param productName Name of the product being added to the order.
*/
const addProductToOrder = async ( orderId, productName ) => {
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.
*
* @param couponAmount Amount to be applied. Defaults to 5.
* @param discountType Type of a coupon. Defaults to Fixed cart discount.
* @param couponAmount Amount to be applied. Defaults to 5.
* @param discountType Type of a coupon. Defaults to Fixed cart discount.
*/
const createCoupon = async (
couponAmount = '5',
@ -576,10 +576,10 @@ const createCoupon = async (
/**
* Adds a shipping zone along with a shipping method.
*
* @param zoneName Shipping zone name.
* @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 zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup)
* @param zoneName Shipping zone name.
* @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 zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup)
*/
const addShippingZoneAndMethod = async (
zoneName,
@ -626,8 +626,8 @@ const addShippingZoneAndMethod = async (
/**
* Click the Update button on the order details page.
*
* @param noticeText The text that appears in the notice after updating the order.
* @param waitForSave Optionally wait for auto save.
* @param noticeText The text that appears in the notice after updating the order.
* @param waitForSave Optionally wait for auto save.
*/
const clickUpdateOrder = async ( noticeText, waitForSave = false ) => {
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.
*
* @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`.
*/
deactivatePlugin: async ( pluginName, deletePlugin = false ) => {

View File

@ -2,9 +2,9 @@
* Take a string name and generate the slug for it.
* 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 ) => {
text = text.trim().toLowerCase();
@ -39,7 +39,7 @@ export const itIf = ( condition ) => ( condition ? it : it.skip );
/**
* Wait for a timeout in milliseconds
*
* @param timeout delay time in milliseconds
* @param timeout delay time in milliseconds
* @return {Promise<void>}
*/
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.
*
* @param repository
* @param defaultObjectId
* @param statuses Status of the object to check
* @param repository
* @param defaultObjectId
* @param statuses Status of the object to check
* @return {Promise<void>}
*/
const deleteAllRepositoryObjects = async (
@ -226,13 +226,13 @@ export const withRestApi = {
/**
* Adds a shipping zone along with a shipping method using the API.
*
* @param zoneName Shipping zone name.
* @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 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 additionalZoneMethods Array of additional zone methods to add to the shipping zone.
* @param {boolean} testResponse Test the response status code.
* @param zoneName Shipping zone name.
* @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 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 additionalZoneMethods Array of additional zone methods to add to the shipping zone.
* @param {boolean} testResponse Test the response status code.
*/
addShippingZoneAndMethod: async (
zoneName,
@ -367,7 +367,7 @@ export const withRestApi = {
/**
* 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>}
*/
deleteCustomerByEmail: async ( emailAddress ) => {
@ -394,8 +394,8 @@ export const withRestApi = {
/**
* Reset a settings group to default values except selects.
*
* @param settingsGroup
* @param {boolean} testResponse Test the response status code.
* @param settingsGroup
* @param {boolean} testResponse Test the response status code.
* @return {Promise<void>}
*/
resetSettingsGroupToDefault: async (
@ -437,8 +437,8 @@ export const withRestApi = {
* Update a setting to the supplied value.
*
* @param {string} settingsGroup The settings group to update.
* @param {string} settingId The setting ID to update
* @param {Object} payload An object with a key/value pair to update.
* @param {string} settingId The setting ID to update
* @param {Object} payload An object with a key/value pair to update.
*/
updateSettingOption: async ( settingsGroup, settingId, payload = {} ) => {
const settingsClient = Setting.restRepository( client );
@ -447,9 +447,9 @@ export const withRestApi = {
/**
* Update a payment gateway.
*
* @param {string} paymentGatewayId The ID of the payment gateway to update.
* @param {Object} payload An object with the key/value pair to update.
* @param {boolean} testResponse Test the response status code.
* @param {string} paymentGatewayId The ID of the payment gateway to update.
* @param {Object} payload An object with the key/value pair to update.
* @param {boolean} testResponse Test the response status code.
*/
updatePaymentGateway: async (
paymentGatewayId,
@ -467,7 +467,7 @@ export const withRestApi = {
/**
* 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.
*/
batchCreateOrders: async ( orders, testResponse = true ) => {

View File

@ -114,8 +114,8 @@ export const backboneUnblocked = async () => {
/**
* Conditionally wait for a selector without throwing an error.
*
* @param selector
* @param timeoutInSeconds
* @param selector
* @param timeoutInSeconds
* @return {Promise<boolean>}
*/
export const waitForSelectorWithoutThrow = async (
@ -136,7 +136,7 @@ export const waitForSelectorWithoutThrow = async (
/**
* 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} publishVerification
* @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).
*
* @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 ) => {
await page.focus( selector );
@ -249,7 +249,7 @@ export const evalAndClick = async ( selector ) => {
/**
* 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
*/
export const selectOptionInSelect2 = async (
@ -266,8 +266,8 @@ export const selectOptionInSelect2 = async (
/**
* Search by any term for an order
*
* @param {string} value Value to be entered into the search field
* @param {string} orderId Order ID
* @param {string} value Value to be entered into the search field
* @param {string} orderId Order ID
* @param {string} customerName Customer's full name attached to order ID.
*/
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.
* 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>}
*/
export const applyCoupon = async ( couponCode ) => {
@ -310,7 +310,7 @@ export const applyCoupon = async ( couponCode ) => {
/**
* Remove one coupon within cart or checkout.
*
* @param couponCode Coupon name.
* @param couponCode Coupon name.
* @return {Promise<void>}
*/
export const removeCoupon = async ( couponCode ) => {
@ -348,7 +348,7 @@ export const selectOrderAction = async ( action ) => {
*
* @param {string} buttonSelector Selector of button to 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>}
*/
export const clickAndWaitForSelector = async (
@ -367,9 +367,9 @@ export const clickAndWaitForSelector = async (
* Behavior can be modified with @param options. Possible keys: `visible`, `hidden`, `timeout`.
* More details at: https://pptr.dev/#?product=Puppeteer&show=api-pagewaitforselectorselector-options
*
* @param {Puppeteer.Page} page Puppeteer representation of the page.
* @param {string} selector CSS selector of the element
* @param {Object} options Custom options to modify function behavior.
* @param {Puppeteer.Page} page Puppeteer representation of the page.
* @param {string} selector CSS selector of the element
* @param {Object} options Custom options to modify function behavior.
*/
export async function waitForSelector( page, selector, options = {} ) {
// set up default options
@ -384,7 +384,7 @@ export async function waitForSelector( page, selector, options = {} ) {
* Retrieves the desired HTML attribute from a selector.
* 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.
* @return {Promise<string>}
*/
@ -399,8 +399,8 @@ export async function getSelectorAttribute( selector, attribute ) {
/**
* Asserts the value of the desired HTML attribute of a selector.
*
* @param {string} selector Selector of the element you want to verify.
* @param {string} attribute The desired HTML attribute.
* @param {string} selector Selector of the element you want to verify.
* @param {string} attribute The desired HTML attribute.
* @param {string} expectedValue The expected value.
*/
export async function verifyValueOfElementAttribute(

View File

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

View File

@ -187,8 +187,10 @@ importers:
packages/js/api-core-tests:
specifiers:
'@woocommerce/eslint-plugin': workspace:*
allure-commandline: ^2.17.2
dotenv: ^10.0.0
eslint: ^8.12.0
jest: ^25.1.0
jest-allure: ^0.1.3
jest-runner-groups: ^2.1.0
@ -202,6 +204,9 @@ importers:
jest-runner-groups: 2.1.0
postman-collection: 4.1.0
supertest: 6.1.6
devDependencies:
'@woocommerce/eslint-plugin': link:../eslint-plugin
eslint: 8.12.0
packages/js/components:
specifiers:
@ -605,13 +610,17 @@ importers:
packages/js/e2e-builds:
specifiers:
'@babel/core': 7.12.9
'@woocommerce/eslint-plugin': workspace:*
chalk: ^4.1.2
eslint: ^8.12.0
glob: ^7.2.0
lodash: ^4.17.21
mkdirp: ^1.0.4
devDependencies:
'@babel/core': 7.12.9
'@woocommerce/eslint-plugin': link:../eslint-plugin
chalk: 4.1.2
eslint: 8.12.0
glob: 7.2.0
lodash: 4.17.21
mkdirp: 1.0.4
@ -628,11 +637,13 @@ importers:
'@babel/preset-env': 7.12.7
'@jest/globals': ^26.4.2
'@woocommerce/e2e-builds': workspace:*
'@woocommerce/eslint-plugin': workspace:*
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3
'@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': ^4.1.0
'@wordpress/deprecated': ^3.2.3
config: 3.3.3
eslint: ^8.12.0
dependencies:
'@jest/globals': 26.6.2
'@wordpress/deprecated': 3.2.3
@ -647,9 +658,11 @@ importers:
'@babel/polyfill': 7.12.1
'@babel/preset-env': 7.12.7_@babel+core@7.12.9
'@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-preset-default': 3.0.2
'@wordpress/browserslist-config': 4.1.0
eslint: 8.12.0
packages/js/e2e-environment:
specifiers:
@ -666,11 +679,11 @@ importers:
'@slack/web-api': ^6.1.0
'@woocommerce/api': ^0.2.0
'@woocommerce/e2e-builds': workspace:*
'@woocommerce/eslint-plugin': workspace:*
'@wordpress/babel-plugin-import-jsx-pragma': 1.1.3
'@wordpress/babel-preset-default': 3.0.2
'@wordpress/browserslist-config': ^4.1.0
'@wordpress/e2e-test-utils': ^4.16.1
'@wordpress/eslint-plugin': 7.3.0
'@wordpress/jest-preset-default': ^7.1.3
app-root-path: ^3.0.0
commander: 4.1.1
@ -716,10 +729,10 @@ importers:
'@babel/polyfill': 7.12.1
'@babel/preset-env': 7.12.7_@babel+core@7.12.9
'@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-preset-default': 3.0.2
'@wordpress/browserslist-config': 4.1.0
'@wordpress/eslint-plugin': 7.3.0_eslint@8.2.0+typescript@4.2.4
eslint: 8.2.0
ndb: 1.1.5
semver: 7.3.5
@ -13960,22 +13973,6 @@ packages:
- typescript
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:
resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==}
engines: {node: ^10.12.0 || >=12.0.0}
@ -14316,27 +14313,6 @@ packages:
- supports-color
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:
resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==}
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
@ -15881,30 +15857,6 @@ packages:
- supports-color
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:
resolution: {integrity: sha512-HJpDYz2drtC9rY8MiYtYJ3cimioEIweGyb3P2DQTjUZ3sC4AGg+97PhXLHUdKfsFQ31JRxyLS9kKuGdDVBwWww==}
engines: {node: '>=10', npm: '>=6.9'}
@ -17437,24 +17389,6 @@ packages:
- supports-color
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:
resolution: {integrity: sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==}
dependencies:
@ -21613,16 +21547,6 @@ packages:
get-stdin: 6.0.0
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:
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
hasBin: true
@ -21801,19 +21725,6 @@ packages:
- typescript
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:
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@ -21896,24 +21807,6 @@ packages:
- supports-color
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:
resolution: {integrity: sha512-8alON8yYcStY94o0HycU2zkLKQdcS+qhhOUNQpfONHHwvI99afbmfpYuPqf6PbLz5pLZldG3Te5I0RbAiTN42g==}
engines: {node: ^12 || ^14 || ^16 || ^17}
@ -22051,27 +21944,6 @@ packages:
language-tags: 1.0.5
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:
resolution: {integrity: sha512-BfvXKsO0K+zvdarNc801jsE/NTLmig4oKhZ1U3aSUgTf2dB/US5+CrfGxMsCK2Ki1vS1R3HPok+uYpufFndhzw==}
engines: {node: ^6.14.0 || ^8.10.0 || >=9.10.0}
@ -22081,23 +21953,6 @@ packages:
unified: 6.2.0
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:
resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==}
engines: {node: '>=6.0.0'}
@ -22206,15 +22061,6 @@ packages:
dependencies:
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:
resolution: {integrity: sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==}
engines: {node: '>=4'}
@ -22305,29 +22151,6 @@ packages:
semver: 6.3.0
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:
resolution: {integrity: sha512-YSNzasJUbyhOTe14ZPygeOBvcPvcaNkwHwrj4vdf+uirr2D32JTDaKi6CP5Os2aWtOcvt4uBSPXp9h5xGoqvWQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'}
@ -37310,7 +37133,7 @@ packages:
serialize-javascript: 6.0.0
source-map: 0.6.1
terser: 5.10.0_acorn@8.7.0
webpack: 5.70.0
webpack: 5.70.0_webpack-cli@3.3.12
transitivePeerDependencies:
- acorn
dev: true