From 6f6e5ef8da3ced05658a0ee1fa84ff8205b9dbcf Mon Sep 17 00:00:00 2001 From: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:49:49 +0100 Subject: [PATCH] Migrated taxes tests (#4628) * taxes tests * Create rude-clocks-attack.md * change locator for checkbox in exception country list * Trigger Build * locator fix * changed test name * Update rude-clocks-attack.md --- .changeset/rude-clocks-attack.md | 9 + playwright/data/e2eTestData.ts | 19 +++ playwright/data/url.ts | 2 + playwright/pages/configurationPage.ts | 3 + .../pages/dialogs/addCountriesDialog.ts | 30 ++++ playwright/pages/taxesPage.ts | 160 ++++++++++++++++++ playwright/tests/channels.spec.ts | 2 +- playwright/tests/taxes.spec.ts | 77 +++++++++ .../TaxCountryDialog/TaxCountryDialog.tsx | 3 + src/taxes/components/TaxInput/TaxInput.tsx | 1 + .../TaxChannelsMenu/TaxChannelsMenu.tsx | 1 + .../pages/TaxChannelsPage/TaxChannelsPage.tsx | 4 + .../TaxCountryExceptionListItem.tsx | 11 +- .../TaxSettingsCard/TaxSettingsCard.tsx | 8 +- .../TaxClassesMenu/TaxClassesMenu.tsx | 3 + .../pages/TaxClassesPage/TaxClassesPage.tsx | 6 + .../TaxCountriesMenu/TaxCountriesMenu.tsx | 7 +- .../TaxCountriesPage/TaxCountriesPage.tsx | 5 + 18 files changed, 345 insertions(+), 6 deletions(-) create mode 100644 .changeset/rude-clocks-attack.md create mode 100644 playwright/pages/dialogs/addCountriesDialog.ts create mode 100644 playwright/pages/taxesPage.ts create mode 100644 playwright/tests/taxes.spec.ts diff --git a/.changeset/rude-clocks-attack.md b/.changeset/rude-clocks-attack.md new file mode 100644 index 00000000000..9addedd63fa --- /dev/null +++ b/.changeset/rude-clocks-attack.md @@ -0,0 +1,9 @@ +--- +"saleor-dashboard": minor +--- + +Migrated taxes tests: +- Change taxes in channel to use tax app +- Change taxes in channel: enter prices without tax, do not show gross price, add country exception +- Add new country and tax rates to it +- Add new class with metadata and set tax rate for single country diff --git a/playwright/data/e2eTestData.ts b/playwright/data/e2eTestData.ts index a2f542a5d0e..f32975ec1d4 100644 --- a/playwright/data/e2eTestData.ts +++ b/playwright/data/e2eTestData.ts @@ -67,6 +67,19 @@ export const COLLECTIONS = { names: ["Collection to be deleted 1/2", "Collection to be deleted 2/2"], }, }; +export const COUNTRIES = { + afghanistan: { + countryCode: "AF", + name: "Afghanistan", + }, + albania: { + countryCode: "AL", + name: "Albania", + }, + countryToBeAddedInTaxes: { + name: "Bosnia and Herzegovina", + }, +}; export const CHANNELS = { channelToBeEditedSettings: { id: "Q2hhbm5lbDoyMzkx", @@ -74,6 +87,12 @@ export const CHANNELS = { channelToBeDeleted: { name: "z - channel to be deleted", }, + channelForTaxEdition: { + name: "a channel for tax tests", + }, + plnChannel: { + id: "VGF4Q29uZmlndXJhdGlvbjox", + }, }; export const GIFT_CARDS = { giftCardToBeEdited: { diff --git a/playwright/data/url.ts b/playwright/data/url.ts index a724600a0af..9eeac02bfd6 100644 --- a/playwright/data/url.ts +++ b/playwright/data/url.ts @@ -34,6 +34,8 @@ export const URL_LIST = { vouchers: "discounts/vouchers/", vouchersAddPage: "discounts/vouchers/add", variant: "variant/", + taxChannel: "taxes/channels/", + taxCountry: "taxes/countries/", warehouses: "warehouses/", webhooksAndEvents: "custom-apps/", resetPassword: "new-password/?email=", diff --git a/playwright/pages/configurationPage.ts b/playwright/pages/configurationPage.ts index cb8db846453..902d35c0526 100644 --- a/playwright/pages/configurationPage.ts +++ b/playwright/pages/configurationPage.ts @@ -40,6 +40,9 @@ export class ConfigurationPage { async openShippingMethods() { await this.shippingMethodsButton.click(); } + async openTaxes() { + await this.taxesButton.click(); + } async openChannels() { await this.channelsButton.click(); } diff --git a/playwright/pages/dialogs/addCountriesDialog.ts b/playwright/pages/dialogs/addCountriesDialog.ts new file mode 100644 index 00000000000..b33336671fc --- /dev/null +++ b/playwright/pages/dialogs/addCountriesDialog.ts @@ -0,0 +1,30 @@ +import { Page } from "@playwright/test"; + +export class AddCountriesDialog { + readonly page: Page; + + constructor( + page: Page, + readonly searchCountryInput = page + .getByTestId("search-country-input") + .locator("input"), + readonly countryRow = page.getByTestId("country-row"), + readonly addButton = page.getByTestId("add-button"), + readonly rowRadioButton = page.locator("input[type='radio']"), + ) { + this.page = page; + } + + async typeSearchedCountry(countryName = "Canada") { + await this.searchCountryInput.fill(countryName); + } + + async checkAndSaveSingleCountry(countryName = "Canada") { + await this.countryRow + .filter({ hasText: countryName }) + .locator(this.rowRadioButton) + .click(); + await this.addButton.click(); + await this.countryRow.first().waitFor({ state: "hidden" }); + } +} diff --git a/playwright/pages/taxesPage.ts b/playwright/pages/taxesPage.ts new file mode 100644 index 00000000000..90cc779c54a --- /dev/null +++ b/playwright/pages/taxesPage.ts @@ -0,0 +1,160 @@ +import { CHANNELS } from "@data/e2eTestData"; +import { URL_LIST } from "@data/url"; +import { AddCountriesDialog } from "@dialogs/addCountriesDialog"; +import { MetadataSeoPage } from "@pageElements/metadataSeoPage"; +import { BasePage } from "@pages/basePage"; +import type { Page } from "@playwright/test"; + +export class TaxesPage extends BasePage { + readonly page: Page; + readonly addCountriesDialog: AddCountriesDialog; + readonly metadataSeoPage: MetadataSeoPage; + + constructor( + page: Page, + readonly appOrFlatRateSelect = page + .getByTestId("app-flat-select") + .locator("[role='button']"), + readonly chanelListRow = page.getByTestId("channels-list-rows"), + readonly countriesListRow = page.getByTestId("countries-list-rows"), + readonly classListRow = page.getByTestId("class-list-rows"), + readonly countriesTab = page.getByTestId("countries-tab"), + readonly taxClassTab = page.getByTestId("tax-classes-tab"), + readonly enteredRenderedSection = page.getByTestId( + "entered-rendered-prices-section", + ), + readonly pricesEnteredWithTaxButton = page.locator( + "[name='pricesEnteredWithTax'][ value='true']", + ), + readonly pricesEnteredWithoutTaxButton = page.locator( + "[name='pricesEnteredWithTax'][ value='false']", + ), + readonly displayGrossPricesCheckbox = page.locator( + "[name='displayGrossPrices']", + ), + readonly addCountryButton = page.getByTestId("add-country-button"), + readonly createClassButton = page.getByTestId("create-class-button"), + readonly saveButton = page.getByTestId("button-bar-confirm"), + readonly exceptionCountryRows = page.getByTestId("exception-country"), + readonly exceptionCountryGrossPriceCheckbox = page.getByTestId( + "display-gross-prices-checkbox", + ), + readonly checkBoxCheckedState = page.locator("Mui-checked"), + readonly exceptionCountriesCheckBoxCheckedState = page.locator( + "[class*='Mui-checked']", + ), + readonly searchTaxClassInput = page + .getByTestId("search-tax-class-input") + .locator("input"), + readonly searchedCountryRows = page.getByTestId("country-rows"), + + readonly searchTaxCountryInput = page + .getByTestId("search-tax-countries-input") + .locator("input"), + readonly taxClassNameInput = page + .getByTestId("class-name-input") + .locator("input"), + readonly noTaxRateInput = page.getByTestId("No Taxes").locator("input"), + readonly defaultRateInput = page + .getByTestId("Country default rate") + .locator("input"), + readonly audioProductsRateInput = page + .getByTestId("Audio Products (tapes, cds etc.)") + .locator("input"), + readonly dataServicesRateInput = page + .getByTestId("Data services - storage and retrieval ") + .locator("input"), + readonly standardRateInput = page.getByTestId("standard").locator("input"), + readonly temporaryUnmappedRateInput = page + .getByTestId("Temporary Unmapped Other SKU - taxable default") + .locator("input"), + ) { + super(page); + this.page = page; + this.addCountriesDialog = new AddCountriesDialog(page); + this.metadataSeoPage = new MetadataSeoPage(page); + } + + async clickSelectMethodField() { + await this.appOrFlatRateSelect.click(); + } + async typeAllTaxRatesForCountry( + defaultRate: string, + noTaxRate: string, + audioRate: string, + dataServiceRate: string, + standardRate: string, + temporaryRate: string, + ) { + await this.defaultRateInput.fill(defaultRate); + await this.audioProductsRateInput.fill(audioRate); + await this.dataServicesRateInput.fill(dataServiceRate); + await this.noTaxRateInput.fill(noTaxRate); + await this.standardRateInput.fill(standardRate); + await this.temporaryUnmappedRateInput.fill(temporaryRate); + } + async clickCountriesTab() { + await this.countriesTab.click(); + await this.countriesListRow.first().waitFor({ state: "visible" }); + } + async clickTaxClassTab() { + await this.taxClassTab.click(); + await this.classListRow.first().waitFor({ state: "visible" }); + } + async clickCountryFromList(countryCode: string) { + await this.countriesListRow.filter({ hasText: countryCode }).click(); + } + async clickSaveButton() { + await this.saveButton.click(); + } + async typeTaxClassName(taxClassName: string) { + await this.taxClassNameInput.fill(taxClassName); + } + async typeSearchedTaxCountryName(taxCountryName: string) { + await this.searchTaxCountryInput.fill(taxCountryName); + } + async typeTaxRateInSearchedCountryRow( + taxCountryName: string, + taxRateValue: string, + ) { + await this.searchedCountryRows + .filter({ hasText: taxCountryName }) + .locator("input") + .fill(taxRateValue); + } + async clickCreateClassButton() { + await this.createClassButton.click(); + } + + async selectTaxCalculationMethod(method: "FLAT_RATES" | "TAX_APP") { + await this.clickSelectMethodField(); + await this.page.getByTestId(`select-field-option-${method}`).click(); + } + + async selectPricesWithoutTaxes() { + await this.enteredRenderedSection + .locator(this.pricesEnteredWithoutTaxButton) + .click(); + } + async selectPricesWithTaxes() { + await this.enteredRenderedSection + .locator(this.pricesEnteredWithTaxButton) + .click(); + } + async clickShowGrossPricesInStorefront() { + await this.enteredRenderedSection + .locator(this.displayGrossPricesCheckbox) + .click(); + } + + async clickAddCountryButton() { + await this.addCountryButton.click(); + } + async selectChannel(channelName: string) { + await this.chanelListRow.filter({ hasText: channelName }).click(); + } + + async gotoChannelsTabUrl() { + await this.page.goto(URL_LIST.taxChannel + CHANNELS.plnChannel.id); + } +} diff --git a/playwright/tests/channels.spec.ts b/playwright/tests/channels.spec.ts index f3e7684afcb..cb0cef4746d 100644 --- a/playwright/tests/channels.spec.ts +++ b/playwright/tests/channels.spec.ts @@ -25,7 +25,7 @@ test("TC: SALEOR_97 Create basic channel @e2e @channels", async () => { await channelPage.clickSaveButton(); await channelPage.expectSuccessBanner(); }); -test("TC: SALEOR_98 Edit channel settings to contain transaction flow, allow unpaid orders, authorize instead of charging, prio high stock @e2e @channels", async () => { +test("TC: SALEOR_98 Edit channel - transaction flow, allow unpaid, authorize, prio high stock @e2e @channels", async () => { await channelPage.gotoChannelDetails(CHANNELS.channelToBeEditedSettings.id); await channelPage.clickTransactionFlowCheckbox(); await channelPage.clickAllowUnpaidOrdersCheckbox(); diff --git a/playwright/tests/taxes.spec.ts b/playwright/tests/taxes.spec.ts new file mode 100644 index 00000000000..f3c6f2f90dd --- /dev/null +++ b/playwright/tests/taxes.spec.ts @@ -0,0 +1,77 @@ +import { CHANNELS, COUNTRIES } from "@data/e2eTestData"; +import { ConfigurationPage } from "@pages/configurationPage"; +import { TaxesPage } from "@pages/taxesPage"; +import { expect, test } from "@playwright/test"; + +test.use({ storageState: "playwright/.auth/admin.json" }); + +let configurationPage: ConfigurationPage; +let taxesPage: TaxesPage; + +test.beforeEach(({ page }) => { + configurationPage = new ConfigurationPage(page); + taxesPage = new TaxesPage(page); +}); + +test("TC: SALEOR_115 Change taxes in channel to use tax app @taxes @e2e", async () => { + await configurationPage.gotoConfigurationView(); + await configurationPage.openTaxes(); + await taxesPage.selectChannel(CHANNELS.channelForTaxEdition.name); + await taxesPage.selectTaxCalculationMethod("TAX_APP"); + await taxesPage.clickSaveButton(); + await taxesPage.expectSuccessBanner(); +}); +test("TC: SALEOR_116 Change taxes in channel: enter prices without tax, do not show gross price, add country exception @taxes @e2e", async () => { + await taxesPage.gotoChannelsTabUrl(); + await taxesPage.selectChannel(CHANNELS.channelForTaxEdition.name); + + await taxesPage.selectPricesWithoutTaxes(); + await taxesPage.clickShowGrossPricesInStorefront(); + await taxesPage.clickAddCountryButton(); + await taxesPage.addCountriesDialog.typeSearchedCountry("Canada"); + await taxesPage.addCountriesDialog.checkAndSaveSingleCountry("Canada"); + await expect(taxesPage.exceptionCountryRows).toContainText("Canada"); + expect( + await taxesPage.exceptionCountryRows + .locator(taxesPage.exceptionCountriesCheckBoxCheckedState) + .count(), + ).toEqual(1); + await taxesPage.clickSaveButton(); + await taxesPage.expectSuccessBanner(); +}); + +test("TC: SALEOR_117 Add new country and tax rates to it @taxes @e2e", async () => { + await taxesPage.gotoChannelsTabUrl(); + await taxesPage.clickCountriesTab(); + await taxesPage.clickAddCountryButton(); + await taxesPage.addCountriesDialog.typeSearchedCountry( + COUNTRIES.countryToBeAddedInTaxes.name, + ); + + await taxesPage.addCountriesDialog.checkAndSaveSingleCountry( + COUNTRIES.countryToBeAddedInTaxes.name, + ); + expect(await taxesPage.countriesListRow.first()).toHaveText( + COUNTRIES.countryToBeAddedInTaxes.name, + ); + await taxesPage.typeAllTaxRatesForCountry("23", "0", "16", "7", "21", "19"); + await taxesPage.clickSaveButton(); + await taxesPage.expectSuccessBanner(); +}); + +test("TC: SALEOR_118 Add new class with metadata and set tax rate for single country @taxes @e2e", async () => { + await taxesPage.gotoChannelsTabUrl(); + await taxesPage.clickTaxClassTab(); + await taxesPage.clickCreateClassButton(); + expect(await taxesPage.taxClassNameInput).toHaveValue("New tax class"); + + await taxesPage.typeTaxClassName("Automation test tax class"); + await taxesPage.typeSearchedTaxCountryName("United States of America"); + await taxesPage.typeTaxRateInSearchedCountryRow( + "United States of America", + "20", + ); + await taxesPage.metadataSeoPage.expandAndAddAllMetadata(); + await taxesPage.clickSaveButton(); + await taxesPage.expectSuccessBanner(); +}); diff --git a/src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx b/src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx index c9d732c16b9..f3576e105a4 100644 --- a/src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx +++ b/src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx @@ -61,6 +61,7 @@ export const TaxCountryDialog: React.FC = ({ setQuery(e.target.value)} variant="outlined" @@ -80,6 +81,7 @@ export const TaxCountryDialog: React.FC = ({ {filteredCountries.map(country => ( setSelectedCountry(country)} @@ -92,6 +94,7 @@ export const TaxCountryDialog: React.FC = ({ @@ -61,6 +62,7 @@ export const TaxClassesMenu: React.FC = ({ {taxClasses?.map((taxClass, taxClassId) => ( = ({ {taxClass.name} {taxClass.id !== "new" && ( } @@ -57,6 +61,7 @@ export const TaxCountriesMenu: React.FC = ({ {configurations?.map((config, configIndex) => ( = props => { @@ -143,6 +146,7 @@ export const TaxCountriesPage: React.FC = props => { <> setQuery(e.target.value)} @@ -181,6 +185,7 @@ export const TaxCountriesPage: React.FC = props => { {rate.label}