Merge branch 'trunk' into oginomizuho-patch-1

This commit is contained in:
Ron Rennick 2023-11-03 16:26:11 -03:00
commit c87551b6c0
14 changed files with 799 additions and 11 deletions

26
.github/workflows/milestoned.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Milestone Manager
on:
pull_request_target:
types: [milestoned]
permissions: {}
jobs:
remove-milestone-from-unmerged-prs:
name: "Remove Milestone from Unmerged PRs"
if: github.event.pull_request.merged != true
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/github-script@v6
with:
script: |
github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
milestone: null,
});

View File

@ -38,9 +38,7 @@ jobs:
with: with:
php-version: '7.4' php-version: '7.4'
- name: 'Run the script to assign a milestone' - name: 'Run the script to assign a milestone'
if: | if: github.event.pull_request.base.ref == 'trunk'
!github.event.pull_request.milestone &&
github.event.pull_request.base.ref == 'trunk'
run: php assign-milestone-to-merged-pr.php run: php assign-milestone-to-merged-pr.php
env: env:
PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }}

29
.github/workflows/scripts/stalebot.js vendored Normal file
View File

@ -0,0 +1,29 @@
/**
* Set the stalebot start date given a cron schedule.
*/
// You need to install this dependency as part of your workflow.
const core = require( '@actions/core' );
const ScheduleStartDate = () => {
let scheduleStartDate;
switch ( process.env.CRON_SCHEDULE ) {
case '21 1 * * *':
scheduleStartDate = '2022-01-01';
break;
case '31 2 * * *':
scheduleStartDate = '2023-01-01';
break;
case '41 3 * * *':
scheduleStartDate = '2023-08-01';
break;
default:
scheduleStartDate = '2018-01-01';
break;
}
core.setOutput( 'stale-start-date', scheduleStartDate );
};
ScheduleStartDate();

View File

@ -1,7 +1,10 @@
name: 'Close stale needs-feedback issues' name: 'Process stale needs-feedback issues'
on: on:
schedule: schedule:
- cron: '21 0 * * *' - cron: '11 0 * * *'
- cron: '21 1 * * *'
- cron: '31 2 * * *'
- cron: '41 3 * * *'
permissions: {} permissions: {}
@ -13,10 +16,20 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@v8 - name: Install Actions Core
run: npm --prefix .github/workflows/scripts install @actions/core
- name: Get start date
id: startdate
run: node .github/workflows/scripts/stalebot.js
env:
CRON_SCHEDULE: ${{ github.event.schedule }}
- name: Scan issues
uses: actions/stale@v8
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 40 operations-per-run: 8
start-date: steps.startdate.outputs.stale-start-date
stale-issue-message: "As a part of this repository's maintenance, this issue is being marked as stale due to inactivity. Please feel free to comment on it in case we missed something.\n\n###### After 7 days with no activity this issue will be automatically be closed." stale-issue-message: "As a part of this repository's maintenance, this issue is being marked as stale due to inactivity. Please feel free to comment on it in case we missed something.\n\n###### After 7 days with no activity this issue will be automatically be closed."
close-issue-message: 'This issue was closed because it has been 14 days with no activity.' close-issue-message: 'This issue was closed because it has been 14 days with no activity.'
days-before-issue-stale: 7 days-before-issue-stale: 7

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Allow plugins to access PostTypeContext and blocks (through core/block-editor data store).

View File

@ -5,6 +5,7 @@ import { synchronizeBlocksWithTemplate, Template } from '@wordpress/blocks';
import { createElement, useMemo, useLayoutEffect } from '@wordpress/element'; import { createElement, useMemo, useLayoutEffect } from '@wordpress/element';
import { useDispatch, useSelect, select as WPSelect } from '@wordpress/data'; import { useDispatch, useSelect, select as WPSelect } from '@wordpress/data';
import { uploadMedia } from '@wordpress/media-utils'; import { uploadMedia } from '@wordpress/media-utils';
import { PluginArea } from '@wordpress/plugins';
import { import {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet. // @ts-ignore No types for this exist yet.
@ -32,6 +33,7 @@ import {
*/ */
import { useConfirmUnsavedProductChanges } from '../../hooks/use-confirm-unsaved-product-changes'; import { useConfirmUnsavedProductChanges } from '../../hooks/use-confirm-unsaved-product-changes';
import { ProductEditorContext } from '../../types'; import { ProductEditorContext } from '../../types';
import { PostTypeContext } from '../../contexts/post-type-context';
type BlockEditorProps = { type BlockEditorProps = {
context: Partial< ProductEditorContext >; context: Partial< ProductEditorContext >;
@ -120,6 +122,11 @@ export function BlockEditor( {
<BlockList className="woocommerce-product-block-editor__block-list" /> <BlockList className="woocommerce-product-block-editor__block-list" />
</ObserveTyping> </ObserveTyping>
</BlockTools> </BlockTools>
{ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ }
<PostTypeContext.Provider value={ context.postType! }>
{ /* @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated. */ }
<PluginArea scope="woocommerce-product-block-editor" />
</PostTypeContext.Provider>
</BlockEditorProvider> </BlockEditorProvider>
</BlockContextProvider> </BlockContextProvider>
</div> </div>

View File

@ -7,7 +7,6 @@ import {
Fragment, Fragment,
useState, useState,
} from '@wordpress/element'; } from '@wordpress/element';
import { PluginArea } from '@wordpress/plugins';
import { import {
LayoutContextProvider, LayoutContextProvider,
useExtendLayout, useExtendLayout,
@ -90,8 +89,6 @@ export function Editor( {
postId: product.id, postId: product.id,
} } } }
/> />
{ /* @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated. */ }
<PluginArea scope="woocommerce-product-block-editor" />
</> </>
} }
/> />

View File

@ -0,0 +1,6 @@
/**
* External dependencies
*/
import { createContext } from '@wordpress/element';
export const PostTypeContext = createContext( 'product' );

View File

@ -20,5 +20,6 @@ export * from './utils';
* Hooks * Hooks
*/ */
export * from './hooks'; export * from './hooks';
export { PostTypeContext } from './contexts/post-type-context';
export { useValidation, useValidations } from './contexts/validation-context'; export { useValidation, useValidations } from './contexts/validation-context';
export * from './contexts/validation-context/types'; export * from './contexts/validation-context/types';

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Add stalebot schedules to allow processing of all issues

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Adds e2e tests for tax display in store, cart and checkout

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fixed warning on wc_get_product_variation_attributes when product does not exist

View File

@ -685,7 +685,7 @@ function wc_get_product_id_by_sku( $sku ) {
*/ */
function wc_get_product_variation_attributes( $variation_id ) { function wc_get_product_variation_attributes( $variation_id ) {
// Build variation data from meta. // Build variation data from meta.
$all_meta = get_post_meta( $variation_id ); $all_meta = is_array( get_post_meta( $variation_id ) ) ? get_post_meta( $variation_id ) : array();
$parent_id = wp_get_post_parent_id( $variation_id ); $parent_id = wp_get_post_parent_id( $variation_id );
$parent_attributes = array_filter( (array) get_post_meta( $parent_id, '_product_attributes', true ) ); $parent_attributes = array_filter( (array) get_post_meta( $parent_id, '_product_attributes', true ) );
$found_parent_attributes = array(); $found_parent_attributes = array();

View File

@ -0,0 +1,695 @@
const { test, expect } = require( '@playwright/test' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
const { admin, customer } = require( '../../test-data/data' );
const productName = 'Taxed products are awesome';
const productPrice = '100.00';
const messyProductPrice = '13.47';
const secondProductName = 'Other products are also awesome';
let productId, productId2, nastyTaxId, seventeenTaxId, sixTaxId, countryTaxId, stateTaxId, cityTaxId, zipTaxId, shippingTaxId, shippingZoneId, shippingMethodId;
test.describe( 'Shopper Tax Display Tests', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'yes',
} );
await api.post( 'products', {
name: productName,
type: 'simple',
regular_price: productPrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
await api.post( 'taxes', {
"country": "US",
"state": "*",
"cities": "*",
"postcodes": "*",
"rate": "25",
"name": "Nasty Tax",
"shipping": false
} )
.then( ( response ) => {
nastyTaxId = response.data.id;
} );
} );
test.beforeEach( async ( { page, context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
// all tests use the first product
await page.goto( `/shop/?add-to-cart=${ productId }`, { waitUntil: 'networkidle' } );
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl'
} );
await api.put( 'settings/tax/woocommerce_price_display_suffix', {
value: '',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'no',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await api.delete( `products/${ productId }`, {
force: true,
} );
await api.delete( `taxes/${ nastyTaxId }`, {
force: true,
} );
} );
test( 'checks that taxes are calculated properly on totals, inclusive tax displayed properly', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'incl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'incl'
} );
await test.step( 'Load shop page and confirm price display', async() => {
await page.goto( '/shop/' );
await expect( page.getByRole( 'heading', { name: 'Shop' } ) ).toBeVisible();
await expect( page.getByRole('link', { name: 'Placeholder Taxed products are awesome $125.00' }).first() ).toBeVisible();
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$125.00 (incl. tax)' } ) ).toHaveCount(2);
await expect( page.getByRole( 'row', { name: 'Subtotal $125.00 (incl. tax)'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $125.00 (includes $25.00 Nasty Tax)' } ) ).toBeVisible();
} );
await test.step( 'Load checkout page and confirm price display', async() => {
await page.goto( '/checkout/' );
await expect( page.getByRole( 'heading', { name: 'Checkout' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Taxed products are awesome × 1 $125.00 (incl. tax)' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Subtotal $125.00 (incl. tax)' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $125.00 (includes $25.00 Nasty Tax)'} ) ).toBeVisible();
} );
} );
test( 'checks that taxes are calculated and displayed correctly exclusive on shop, cart and checkout', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl'
} );
await test.step( 'Load shop page and confirm price display', async() => {
await page.goto( '/shop/' );
await expect( page.getByRole( 'heading', { name: 'Shop' } ) ).toBeVisible();
await expect( page.getByRole('link', { name: 'Placeholder Taxed products are awesome $100.00' }).first() ).toBeVisible();
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$100.00' } ) ).toHaveCount(3);
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Tax $25.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $125.00' } ) ).toBeVisible();
} );
await test.step( 'Load checkout page and confirm price display', async() => {
await page.goto( '/checkout/' );
await expect( page.getByRole( 'heading', { name: 'Checkout' } ) ).toBeVisible();
await page.locator( '#billing_first_name' ).fill( customer.billing.us.first_name );
await page.locator( '#billing_last_name' ).fill( customer.billing.us.last_name );
await page.locator( '#billing_address_1' ).fill( customer.billing.us.address );
await page.locator( '#billing_city' ).fill( customer.billing.us.city );
await page.locator( '#billing_country' ).selectOption( customer.billing.us.country );
await page.locator( '#billing_state' ).selectOption( customer.billing.us.state );
await page.locator( '#billing_postcode' ).fill( customer.billing.us.zip );
await page.locator( '#billing_phone' ).fill( customer.billing.us.phone );
await page.locator( '#billing_email' ).fill( customer.billing.us.email );
await expect( page.getByRole( 'row', { name: 'Taxed products are awesome × 1 $100.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Tax $25.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $125.00' } ) ).toBeVisible();
} );
} );
test( 'checks that display suffix is shown', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_price_display_suffix', {
value: 'excluding VAT',
} );
await test.step( 'Load shop page and confirm price suffix display', async() => {
await page.goto( '/shop/' );
await expect( page.getByRole( 'heading', { name: 'Shop' } ) ).toBeVisible();
await expect( page.getByRole('link', { name: 'Placeholder Taxed products are awesome $100.00 excluding VAT' }).first() ).toBeVisible();
} );
} );
} );
test.describe( 'Shopper Tax Rounding', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'yes',
} );
await api.post( 'products', {
name: productName,
type: 'simple',
regular_price: messyProductPrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
await api.post( 'products', {
name: secondProductName,
type: 'simple',
regular_price: messyProductPrice,
} )
.then( ( response ) => {
productId2 = response.data.id;
} );
await api.post( 'taxes', {
"country": "US",
"state": "*",
"cities": "*",
"postcodes": "*",
"rate": "17",
"name": "Seventeen Tax",
"shipping": false,
"compound": true,
"priority": 1
} )
.then( ( response ) => {
seventeenTaxId = response.data.id;
} );
await api.post( 'taxes', {
"country": "US",
"state": "*",
"cities": "*",
"postcodes": "*",
"rate": "6",
"name": "Six Tax",
"shipping": false,
"compound": true,
"priority": 2
} )
.then( ( response ) => {
sixTaxId = response.data.id;
} );
} );
test.beforeEach( async ( { page, context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
// all tests use the first product
await page.goto( `/shop/?add-to-cart=${ productId }`, { waitUntil: 'networkidle' } );
await page.goto( `/shop/?add-to-cart=${ productId2 }`, { waitUntil: 'networkidle' } );
await page.goto( `/shop/?add-to-cart=${ productId2 }`, { waitUntil: 'networkidle' } );
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl'
} );
await api.put( 'settings/tax/woocommerce_tax_round_at_subtotal', {
value: 'no',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'no',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized'
} );
await api.delete( `products/${ productId }`, {
force: true,
} );
await api.delete( `products/${ productId2 }`, {
force: true,
} );
await api.delete( `taxes/${ seventeenTaxId }`, {
force: true,
} );
await api.delete( `taxes/${ sixTaxId }`, {
force: true,
} );
} );
test( 'checks rounding at subtotal level', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_round_at_subtotal', {
value: 'yes',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'single',
} );
await test.step( 'Load shop page and confirm price display', async() => {
await page.goto( '/shop/' );
await expect( page.getByRole( 'heading', { name: 'Shop' } ) ).toBeVisible();
await expect( page.getByRole('link', { name: 'Placeholder Taxed products are awesome $13.47' }).first() ).toBeVisible();
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$13.47' } ) ).toHaveCount(3);
await expect( page.getByRole( 'row', { name: 'Subtotal $40.41'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Tax $9.71 ' } ) ).toBeVisible()
await expect( page.getByRole( 'row', { name: 'Total $50.12 ' } ) ).toBeVisible();
} );
} );
test( 'checks rounding off at subtotal level', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_display_shop', {
value: 'excl',
} );
await api.put( 'settings/tax/woocommerce_tax_round_at_subtotal', {
value: 'no',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await test.step( 'Load shop page and confirm price display', async() => {
await page.goto( '/shop/' );
await expect( page.getByRole( 'heading', { name: 'Shop' } ) ).toBeVisible();
await expect( page.getByRole('link', { name: 'Placeholder Taxed products are awesome $13.47' }).first() ).toBeVisible();
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$13.47' } ) ).toHaveCount(3);
await expect( page.getByRole( 'row', { name: 'Subtotal $40.41'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Seventeen Tax $6.87 ' } ) ).toBeVisible()
await expect( page.getByRole( 'row', { name: 'Six Tax $2.84 ' } ) ).toBeVisible()
await expect( page.getByRole( 'row', { name: 'Total $50.12 ' } ) ).toBeVisible();
} );
} );
} );
test.describe( 'Shopper Tax Levels', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'yes',
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.post( 'products', {
name: productName,
type: 'simple',
regular_price: productPrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
await api.post( 'taxes', {
"country": "US",
"state": "*",
"cities": "*",
"postcodes": "*",
"rate": "10",
"name": "Country Tax",
"shipping": false,
"priority": 1
} )
.then( ( response ) => {
countryTaxId = response.data.id;
} );
await api.post( 'taxes', {
"country": "*",
"state": "CA",
"cities": "*",
"postcodes": "*",
"rate": "5",
"name": "State Tax",
"shipping": false,
"priority": 2
} )
.then( ( response ) => {
stateTaxId = response.data.id;
} );
await api.post( 'taxes', {
"country": "*",
"state": "*",
"cities": "Sacramento",
"postcodes": "*",
"rate": "2.5",
"name": "City Tax",
"shipping": false,
"priority": 3
} )
.then( ( response ) => {
cityTaxId = response.data.id;
} );
await api.post( 'taxes', {
"country": "*",
"state": "*",
"cities": "*",
"postcodes": "55555",
"rate": "1.25",
"name": "Zip Tax",
"shipping": false,
"priority": 4
} )
.then( ( response ) => {
zipTaxId = response.data.id;
} );
} );
test.beforeEach( async ( { page, context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
// all tests use the first product
await page.goto( `/shop/?add-to-cart=${ productId }`, { waitUntil: 'networkidle' } );
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized'
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await api.delete( `products/${ productId }`, {
force: true,
} );
await api.delete( `taxes/${ countryTaxId }`, {
force: true,
} );
await api.delete( `taxes/${ stateTaxId }`, {
force: true,
} );
await api.delete( `taxes/${ cityTaxId }`, {
force: true,
} );
await api.delete( `taxes/${ zipTaxId }`, {
force: true,
} );
} );
test( 'checks applying taxes of 4 different levels', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$100.00' } ) ).toHaveCount(3);
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Country Tax $10.00 ' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'State Tax $5.00 ' } ) ).toBeVisible()
await expect( page.getByRole( 'row', { name: 'Total $115.00 ' } ) ).toBeVisible();
} );
await test.step( 'Load checkout page and confirm taxes displayed', async() => {
await page.goto( '/checkout/' );
await expect( page.getByRole( 'heading', { name: 'Checkout', exact: true } ) ).toBeVisible();
await page.getByLabel('First name *').first().fill( customer.billing.us.first_name );
await page.getByLabel('Last name *').first().fill( customer.billing.us.last_name );
await page.getByPlaceholder('House number and street name').first().fill( customer.billing.us.address );
await page.getByLabel('Town / City *').first().pressSequentially( 'Sacramento' );
await page.getByLabel('ZIP Code *').first().pressSequentially( '55555' );
await page.getByLabel('Phone *').first().fill( customer.billing.us.phone );
await page.getByLabel('Email address *').first().fill( customer.billing.us.email );
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Country Tax $10.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'State Tax $5.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'City Tax $2.50' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Zip Tax $1.25' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $118.75 ' } ) ).toBeVisible();
} );
} );
test( 'checks applying taxes of 2 different levels (2 excluded)', async ( { page, baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$100.00' } ) ).toHaveCount(3);
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Country Tax $10.00 ' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'State Tax $5.00 ' } ) ).toBeVisible()
await expect( page.getByRole( 'row', { name: 'Total $115.00 ' } ) ).toBeVisible();
} );
await test.step( 'Load checkout page and confirm taxes displayed', async() => {
await page.goto( '/checkout/' );
await expect( page.getByRole( 'heading', { name: 'Checkout', exact: true } ) ).toBeVisible();
await page.getByLabel('First name *').first().fill( customer.billing.us.first_name );
await page.getByLabel('Last name *').first().fill( customer.billing.us.last_name );
await page.getByPlaceholder('House number and street name').first().fill( customer.billing.us.address );
await page.getByLabel('Town / City *').first().pressSequentially( customer.billing.us.city );
await page.getByLabel('ZIP Code *').first().pressSequentially( customer.billing.us.zip );
await page.getByLabel('Phone *').first().fill( customer.billing.us.phone );
await page.getByLabel('Email address *').first().fill( customer.billing.us.email );
await expect( page.getByRole( 'row', { name: 'Subtotal $100.00'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Country Tax $10.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'State Tax $5.00' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'City Tax $2.50' } ) ).not.toBeVisible();
await expect( page.getByRole( 'row', { name: 'Zip Tax $1.25' } ) ).not.toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $115.00 ' } ) ).toBeVisible();
} );
} );
} );
test.describe( 'Shipping Tax', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'yes',
} );
await api.post( 'products', {
name: productName,
type: 'simple',
regular_price: productPrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
await api.post( 'taxes', {
"country": "US",
"state": "*",
"cities": "*",
"postcodes": "*",
"rate": "15",
"name": "Shipping Tax",
"shipping": true
} )
.then( ( response ) => {
shippingTaxId = response.data.id;
} );
await api.post( 'shipping/zones', {
name: 'All',
} )
.then( ( response ) => {
shippingZoneId = response.data.id;
} );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'flat_rate',
} )
.then( ( response ) => {
shippingMethodId = response.data.id;
} );
await api.put( `shipping/zones/${ shippingZoneId }/methods/${ shippingMethodId }`, {
settings: {
cost: '20.00',
}
} );
await api.put( 'payment_gateways/cod' , {
enabled: true
} );
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'incl',
} );
} );
test.beforeEach( async ( { page, context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
// all tests use the first product
await page.goto( `/shop/?add-to-cart=${ productId }`, { waitUntil: 'networkidle' } );
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.put( 'settings/general/woocommerce_calc_taxes', {
value: 'no',
} );
await api.delete( `products/${ productId }`, {
force: true,
} );
await api.delete( `taxes/${ shippingTaxId }`, {
force: true,
} );
await api.put( 'payment_gateways/cod' , {
enabled: false
} );
await api.delete( `shipping/zones/${ shippingZoneId }`, {
force: true,
} );
} );
test( 'checks that tax is applied to shipping as well as order', async ( { page, baseURL } ) => {
await test.step( 'Load cart page and confirm price display', async() => {
await page.goto( '/cart/' );
await expect( page.getByRole( 'heading', { name: 'Cart', exact: true } ) ).toBeVisible();
await expect( page.getByRole( 'cell', { name: '$115.00 (incl. tax)' } ) ).toHaveCount(2);
await expect( page.getByRole( 'row', { name: 'Subtotal $115.00 (incl. tax)'} ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Shipping Flat rate: $23.00 (incl. tax) Shipping to CA.' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $138.00 (includes $18.00 Shipping Tax)' } ) ).toBeVisible();
} );
await test.step( 'Load checkout page and confirm price display', async() => {
await page.goto( '/checkout/' );
await expect( page.getByRole( 'heading', { name: 'Checkout' } ) ).toBeVisible();
await page.getByRole('textbox', { name: 'First name *' }).fill( customer.billing.us.first_name );
await page.getByRole('textbox', { name: 'Last name *' }).fill( customer.billing.us.last_name );
await page.getByRole('textbox', { name: 'Street address *' }).fill( customer.billing.us.address );
await page.getByRole('textbox', { name: 'Town / City *' }).type( customer.billing.us.city );
await page.getByRole('textbox', { name: 'ZIP Code *' }).type( customer.billing.us.zip );
await page.getByLabel('Phone *').fill( customer.billing.us.phone );
await page.getByLabel('Email address *').fill( customer.billing.us.email );
await expect( page.getByRole( 'row', { name: 'Taxed products are awesome × 1 $115.00 (incl. tax)' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Subtotal $115.00 (incl. tax)' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Shipping Flat rate: $23.00 (incl. tax)' } ) ).toBeVisible();
await expect( page.getByRole( 'row', { name: 'Total $138.00 (includes $18.00 Shipping Tax)'} ) ).toBeVisible();
} );
} );
} );