diff --git a/tests/e2e-tests/specs/wp-admin/wp-admin-settings-tax.test.js b/tests/e2e-tests/specs/wp-admin/wp-admin-settings-tax.test.js new file mode 100644 index 00000000000..f2384a7db55 --- /dev/null +++ b/tests/e2e-tests/specs/wp-admin/wp-admin-settings-tax.test.js @@ -0,0 +1,175 @@ +/** + * @format + */ + +/** + * External dependencies + */ +import { activatePlugin } from '@wordpress/e2e-test-utils'; + +/** + * Internal dependencies + */ +import { StoreOwnerFlow } from '../../utils/flows'; +import { + clearAndFillInput, + setCheckbox, + settingsPageSaveChanges, + uiUnblocked, + verifyCheckboxIsSet, + verifyValueOfInputField +} from "../../utils"; + +describe( 'WooCommerce Tax Settings', () => { + beforeAll( async () => { + await activatePlugin( 'woocommerce' ); + } ); + + it( 'can enable tax calculation', async() => { + // Go to general settings page + await StoreOwnerFlow.openSettings( 'general' ); + + // Make sure the general tab is active + await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } ); + + // Enable tax calculation + 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' ), + ] ); + + // Verify that tax settings are now present + await expect( page ).toMatchElement( 'a.nav-tab', { text: 'Tax' } ); + } ); + + it( 'can set tax options', async () => { + // Go to tax settings page + await StoreOwnerFlow.openSettings( 'tax' ); + + // Make sure the tax tab is active + 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"]' ); + // 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' ); + // Leave rounding unchecked (no-op) + // Display prices 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' ); + // Display a single tax 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' } ), + ] ); + } ); + + it( 'can add tax classes', async () => { + // Go to tax settings page + await StoreOwnerFlow.openSettings( 'tax' ); + + // Make sure the tax tab is active + await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } ); + + // Remove additional 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: '' } ), + ] ); + + // Add a "fancy" tax class + 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' } ), + ] ); + } ); + + it( 'can set rate settings', async () => { + // Go to "fancy" rates tax settings page + await StoreOwnerFlow.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' } ); + + // 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' ); + + // 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"]' ); + + // Save changes (AJAX here) + await expect( page ).toClick( 'button.woocommerce-save-button' ); + await uiUnblocked(); + + // Verify 2 tax rates + 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' ); + + // Save changes (AJAX here) + await expect( page ).toClick( 'button.woocommerce-save-button' ); + await uiUnblocked(); + + // Verify 1 rate + 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 () => { + // Go to tax settings page + await StoreOwnerFlow.openSettings( 'tax' ); + + // Make sure the tax tab is active + await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } ); + + // Remove "fancy" tax class + 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 page.waitFor( 10000 ); + } ); +} ); diff --git a/tests/e2e-tests/utils/flows.js b/tests/e2e-tests/utils/flows.js index 535d3371e00..fc5d06720f6 100644 --- a/tests/e2e-tests/utils/flows.js +++ b/tests/e2e-tests/utils/flows.js @@ -5,6 +5,7 @@ const baseUrl = process.env.WP_BASE_URL; const WP_ADMIN_NEW_PRODUCT = baseUrl + '/wp-admin/post-new.php?post_type=product'; +const WP_ADMIN_WC_SETTINGS = baseUrl + '/wp-admin/admin.php?page=wc-settings&tab='; const StoreOwnerFlow = { openNewProduct: async () => { @@ -12,6 +13,18 @@ const StoreOwnerFlow = { waitUntil: 'networkidle0', } ); }, + + openSettings: async ( tab, section = null ) => { + let settingsUrl = WP_ADMIN_WC_SETTINGS + tab; + + if ( section ) { + settingsUrl += `§ion=${ section }`; + } + + await page.goto( settingsUrl, { + waitUntil: 'networkidle0', + } ); + }, }; export { StoreOwnerFlow }; diff --git a/tests/e2e-tests/utils/index.js b/tests/e2e-tests/utils/index.js index 7a5a3d64e29..d79b004b789 100644 --- a/tests/e2e-tests/utils/index.js +++ b/tests/e2e-tests/utils/index.js @@ -1,8 +1,25 @@ +/** + * External dependencies + */ +import { pressKeyWithModifier } from '@wordpress/e2e-test-utils'; + /** * Internal dependencies */ const flows = require( './flows' ); +/** + * Perform a "select all" and then fill a input. + * + * @param {string} selector + * @param {string} value + */ +const clearAndFillInput = async ( selector, value ) => { + await page.focus( selector ); + await pressKeyWithModifier( 'primary', 'a' ); + await page.type( selector, value ); +}; + /** * Click a tab (on post type edit screen). * @@ -12,6 +29,45 @@ const clickTab = async ( tabName ) => { await expect( page ).toClick( '.wc-tabs > li > a', { text: tabName } ); }; +/** + * Save changes on a WooCommerce settings page. + */ +const settingsPageSaveChanges = async () => { + await page.focus( 'button.woocommerce-save-button' ); + await Promise.all( [ + page.waitForNavigation( { waitUntil: 'networkidle0' } ), + page.click( 'button.woocommerce-save-button' ), + ] ); +}; + +/** + * Set checkbox. + * + * @param {string} selector + */ +const setCheckbox = async( selector ) => { + await page.focus( selector ); + const checkbox = await page.$( selector ); + const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() ); + if ( checkboxStatus !== true ) { + await page.click( selector ); + } +}; + +/** + * Unset checkbox. + * + * @param {string} selector + */ +const unsetCheckbox = async( selector ) => { + await page.focus( selector ); + const checkbox = await page.$( selector ); + const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() ); + if ( checkboxStatus === true ) { + await page.click( selector ); + } +}; + /** * Wait for UI blocking to end. */ @@ -19,8 +75,52 @@ const uiUnblocked = async () => { await page.waitForFunction( () => ! Boolean( document.querySelector( '.blockUI' ) ) ); }; +/** + * Verify that checkbox is set. + * + * @param {string} selector Selector of the checkbox that needs to be verified. + */ +const verifyCheckboxIsSet = async( selector ) => { + await page.focus( selector ); + const checkbox = await page.$( selector ); + const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() ); + await expect( checkboxStatus ).toBe( true ); +}; + +/** + * Verify that checkbox is unset. + * + * @param {string} selector Selector of the checkbox that needs to be verified. + */ +const verifyCheckboxIsUnset = async( selector ) => { + await page.focus( selector ); + const checkbox = await page.$( selector ); + const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() ); + await expect( checkboxStatus ).not.toBe( true ); +}; + +/** + * 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. + */ +const verifyValueOfInputField = async( selector, value ) => { + await page.focus( selector ); + const field = await page.$( selector ); + const fieldValue = ( await ( await field.getProperty( 'value' ) ).jsonValue() ); + await expect( fieldValue ).toBe( value ); +}; + module.exports = { ...flows, + clearAndFillInput, clickTab, + settingsPageSaveChanges, + setCheckbox, + unsetCheckbox, uiUnblocked, + verifyCheckboxIsSet, + verifyCheckboxIsUnset, + verifyValueOfInputField, };