From 87304c601b6da1cc00d9152f75c43cfc00d84ff6 Mon Sep 17 00:00:00 2001 From: vanpham-sw Date: Tue, 4 Feb 2025 07:22:20 +0700 Subject: [PATCH 1/2] feat: add new address --- src/page-objects/storefront/Account.ts | 7 +++-- .../storefront/AccountAddresses.ts | 5 +++ .../storefront/AccountAddresssCreate.ts | 2 ++ src/page-objects/storefront/AccountLogin.ts | 27 ++++++++++++++++ src/services/TestDataService.ts | 2 +- src/tasks/shop-customer-tasks.ts | 2 ++ .../shop-customer/Account/AddNewAddress.ts | 31 +++++++++++++++++++ src/types/ShopwareTypes.ts | 19 +++++++++++- 8 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/tasks/shop-customer/Account/AddNewAddress.ts diff --git a/src/page-objects/storefront/Account.ts b/src/page-objects/storefront/Account.ts index a997b26f..5d7da58b 100644 --- a/src/page-objects/storefront/Account.ts +++ b/src/page-objects/storefront/Account.ts @@ -12,7 +12,8 @@ export class Account implements PageObject { public readonly newsletterCheckbox: Locator; public readonly newsletterRegistrationSuccessMessage: Locator; public readonly customerGroupRequestMessage: Locator; - + public readonly cannotDeliverToCountryAlert: Locator; + public readonly shippingToAddressNotPossibleAlert: Locator; constructor(public readonly page: Page, public readonly instanceMeta: HelperFixtureTypes['InstanceMeta']) { this.headline = page.getByRole('heading', { name: 'Overview' }); this.personalDataCardTitle = page.getByRole('heading', { name: 'Personal data' }); @@ -27,7 +28,9 @@ export class Account implements PageObject { } else { this.customerGroupRequestMessage = page.locator('.alert-content-container'); } - + this.cannotDeliverToCountryAlert = page.getByText('We can not deliver to the country that is stored in your delivery address.'); + this.shippingToAddressNotPossibleAlert = page.getByText('Shipping to the selected shipping address is currently not possible.'); + } async getCustomerGroupAlert(customerGroup: string): Promise { diff --git a/src/page-objects/storefront/AccountAddresses.ts b/src/page-objects/storefront/AccountAddresses.ts index 1234611e..df65e696 100644 --- a/src/page-objects/storefront/AccountAddresses.ts +++ b/src/page-objects/storefront/AccountAddresses.ts @@ -7,6 +7,8 @@ export class AccountAddresses implements PageObject { public readonly editShippingAddressButton: Locator; public readonly useDefaultBillingAddressButton: Locator; public readonly useDefaultShippingAddressButton: Locator; + public readonly deliveryNotPossibleAlert: Locator; + public readonly otherShippingAddress: Locator; constructor(public readonly page: Page) { this.addNewAddressButton = page.getByRole('link', { name: /Add (new )?address/ }); @@ -14,6 +16,9 @@ export class AccountAddresses implements PageObject { this.editShippingAddressButton = page.getByRole('link', { name: 'Edit address' }).nth(1); this.useDefaultBillingAddressButton = page.getByRole('button', { name: 'Use as default billing address' }); this.useDefaultShippingAddressButton = page.getByRole('button', { name: 'Use as default shipping address' }); + const otherAddress = page.locator('.other-address'); + this.deliveryNotPossibleAlert = otherAddress.getByText('A delivery to this country is not possible.'); + this.otherShippingAddress = otherAddress.locator('.address').nth(1); } url() { diff --git a/src/page-objects/storefront/AccountAddresssCreate.ts b/src/page-objects/storefront/AccountAddresssCreate.ts index 894164d7..cd9bcd99 100644 --- a/src/page-objects/storefront/AccountAddresssCreate.ts +++ b/src/page-objects/storefront/AccountAddresssCreate.ts @@ -12,6 +12,7 @@ export class AccountAddressCreate implements PageObject { public readonly cityInput: Locator; public readonly countryDropdown: Locator; public readonly saveAddressButton: Locator; + public readonly stateDropdown: Locator; constructor(public readonly page: Page) { this.salutationDropdown = page.locator('#addresspersonalSalutation'); @@ -23,6 +24,7 @@ export class AccountAddressCreate implements PageObject { this.zipcodeInput = page.locator('#addressAddressZipcode'); this.cityInput = page.locator('#addressAddressCity'); this.countryDropdown = page.locator('#addressAddressCountry'); + this.stateDropdown = page.locator('#addressAddressCountryState'); this.saveAddressButton = page.locator('.address-form-submit'); } diff --git a/src/page-objects/storefront/AccountLogin.ts b/src/page-objects/storefront/AccountLogin.ts index ab926213..df20affb 100644 --- a/src/page-objects/storefront/AccountLogin.ts +++ b/src/page-objects/storefront/AccountLogin.ts @@ -29,6 +29,19 @@ export class AccountLogin implements PageObject { public readonly postalCodeInput: Locator; public readonly registerButton: Locator; + //Input for shipping address + public readonly differentShippingAddressCheckbox: Locator; + public readonly registerShippingAddressFormArea: Locator; + public readonly shippingAddressSalutationSelect: Locator; + public readonly shippingAddressFirstNameInput: Locator; + public readonly shippingAddressLastNameInput: Locator; + public readonly shippingAddressStreetAddressInput: Locator; + public readonly shippingAddressCityInput: Locator; + public readonly shippingAddressCountryInput: Locator; + public readonly shippingAddressPostalCodeInput: Locator; + public readonly shippingAddressStateInput: Locator; + + constructor(public readonly page: Page) { this.emailInput = page.getByLabel('Your email address'); this.passwordInput = page.getByLabel('Your password'); @@ -52,10 +65,24 @@ export class AccountLogin implements PageObject { this.cityInput = this.billingAddressFormArea.getByLabel('City'); this.countryInput = this.billingAddressFormArea.getByLabel('Country'); this.postalCodeInput = this.billingAddressFormArea.getByLabel('Postal code'); + this.differentShippingAddressCheckbox = page.getByRole('checkbox', { name: 'Shipping and billing address do not match.' }); + this.registerShippingAddressFormArea = page.locator('.register-shipping'); + this.shippingAddressSalutationSelect = this.registerShippingAddressFormArea.locator('.form-group').filter({ has: page.getByLabel('Salutation') }).locator('.form-select'); + this.shippingAddressFirstNameInput = this.registerShippingAddressFormArea.getByLabel('First name'); + this.shippingAddressLastNameInput = this.registerShippingAddressFormArea.getByLabel('Last name'); + this.shippingAddressStreetAddressInput = this.registerShippingAddressFormArea.getByLabel('Street address'); + this.shippingAddressCityInput = this.registerShippingAddressFormArea.getByLabel('City'); + this.shippingAddressCountryInput = this.registerShippingAddressFormArea.getByLabel('Country'); + this.shippingAddressPostalCodeInput = this.registerShippingAddressFormArea.getByLabel('Postal code'); + this.shippingAddressStateInput = this.registerShippingAddressFormArea.getByLabel('State'); this.registerButton = page.getByRole('button', { name: 'Continue' }); this.logoutLink = page.getByRole('link', { name: 'Log out'}); this.successAlert = page.getByText('Successfully logged out.'); this.passwordUpdatedAlert = page.getByText('Your password has been updated.'); + } + + async getShippingCountryLocatorByName(countryName: string): Promise { + return this.shippingAddressCountryInput.locator('option').filter({ hasText: countryName }); } url() { diff --git a/src/services/TestDataService.ts b/src/services/TestDataService.ts index f6b0baf0..be5c5ccb 100644 --- a/src/services/TestDataService.ts +++ b/src/services/TestDataService.ts @@ -100,7 +100,7 @@ export class TestDataService { * * @private */ - private highPriorityEntities = ['order', 'product', 'landing_page', 'shipping_method', 'sales_channel_domain', 'sales_channel_currency', 'sales_channel_country', 'customer']; + private highPriorityEntities = ['order', 'product', 'landing_page', 'shipping_method', 'sales_channel_domain', 'sales_channel_currency', 'sales_channel_country', 'customer', 'customer_address']; /** * A registry of all created records. diff --git a/src/tasks/shop-customer-tasks.ts b/src/tasks/shop-customer-tasks.ts index 96072638..f2a34a18 100644 --- a/src/tasks/shop-customer-tasks.ts +++ b/src/tasks/shop-customer-tasks.ts @@ -5,6 +5,7 @@ import { Logout } from './shop-customer/Account/Logout'; import { Register } from './shop-customer/Account/Register'; import { RegisterGuest } from './shop-customer/Account/RegisterGuest'; import { ChangeStorefrontCurrency } from './shop-customer/Account/ChangeStorefrontCurrency'; +import { AddNewAddress } from './shop-customer/Account/AddNewAddress'; import { AddProductToCart } from './shop-customer/Product/AddProductToCart'; import { ProceedFromProductToCheckout } from './shop-customer/Product/ProceedFromProductToCheckout'; @@ -31,6 +32,7 @@ export const test = mergeTests( Register, RegisterGuest, ChangeStorefrontCurrency, + AddNewAddress, AddProductToCart, ChangeProductQuantity, ProceedFromProductToCheckout, diff --git a/src/tasks/shop-customer/Account/AddNewAddress.ts b/src/tasks/shop-customer/Account/AddNewAddress.ts new file mode 100644 index 00000000..83d90367 --- /dev/null +++ b/src/tasks/shop-customer/Account/AddNewAddress.ts @@ -0,0 +1,31 @@ +import { test as base } from '@playwright/test'; +import type { Task } from '../../../types/Task'; +import type { FixtureTypes} from '../../../types/FixtureTypes'; +import { Address } from '../../../types/ShopwareTypes'; + +export const AddNewAddress = base.extend<{ AddNewAddress: Task }, FixtureTypes>({ + AddNewAddress: async ({ ShopCustomer, StorefrontAccountAddresses, StorefrontAccountAddressCreate }, use)=> { + const task = (address: Address) => { + return async function AddNewAddress() { + + await ShopCustomer.goesTo(StorefrontAccountAddresses.url()); + await StorefrontAccountAddresses.addNewAddressButton.click(); + + await StorefrontAccountAddressCreate.firstNameInput.fill(address.firstName); + await StorefrontAccountAddressCreate.lastNameInput.fill(address.lastName); + await StorefrontAccountAddressCreate.companyInput.fill(address.company); + await StorefrontAccountAddressCreate.departmentInput.fill(address.department); + await StorefrontAccountAddressCreate.streetInput.fill(address.street); + await StorefrontAccountAddressCreate.zipcodeInput.fill(address.zipCode); + await StorefrontAccountAddressCreate.cityInput.fill(address.city); + await StorefrontAccountAddressCreate.countryDropdown.selectOption({label: address.country}); + await StorefrontAccountAddressCreate.stateDropdown.selectOption({label: address.state}); + + await StorefrontAccountAddressCreate.saveAddressButton.click(); + + } + }; + + await use(task); + }, +}); diff --git a/src/types/ShopwareTypes.ts b/src/types/ShopwareTypes.ts index 35b3e830..f36b020d 100644 --- a/src/types/ShopwareTypes.ts +++ b/src/types/ShopwareTypes.ts @@ -35,6 +35,19 @@ export type CustomerAddress = components['schemas']['CustomerAddress'] & { id: string, } +export interface Address { + salutation: string, + firstName: string, + lastName: string, + company: string, + department: string, + street: string, + city: string, + zipCode: string, + country: string, + state: string, +} + export type Salutation = components['schemas']['Salutation'] & { id: string, } @@ -106,8 +119,12 @@ export type Currency = components['schemas']['Currency'] & { id: string, } -export type Country = components['schemas']['Country'] & { +export type Country = Omit & { id: string, + states: [{ + name: string, + shortCode: string, + }], } export type SystemConfig = components['schemas']['SystemConfig'] & { From 4688f0758f6dc173cfb649af577886c1d930c5b9 Mon Sep 17 00:00:00 2001 From: vanpham-sw Date: Wed, 5 Feb 2025 11:38:19 +0700 Subject: [PATCH 2/2] fix: move deleteRegisteredUser to ShopwareDataHelpers --- src/page-objects/storefront/Account.ts | 2 +- src/page-objects/storefront/AccountLogin.ts | 1 - src/services/ShopwareDataHelpers.ts | 29 ++++++++++++++++- src/services/TestDataService.ts | 2 +- .../shop-customer/Account/AddNewAddress.ts | 5 +-- src/tasks/shop-customer/Account/Register.ts | 32 ++----------------- 6 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/page-objects/storefront/Account.ts b/src/page-objects/storefront/Account.ts index 5d7da58b..26d33e0f 100644 --- a/src/page-objects/storefront/Account.ts +++ b/src/page-objects/storefront/Account.ts @@ -14,6 +14,7 @@ export class Account implements PageObject { public readonly customerGroupRequestMessage: Locator; public readonly cannotDeliverToCountryAlert: Locator; public readonly shippingToAddressNotPossibleAlert: Locator; + constructor(public readonly page: Page, public readonly instanceMeta: HelperFixtureTypes['InstanceMeta']) { this.headline = page.getByRole('heading', { name: 'Overview' }); this.personalDataCardTitle = page.getByRole('heading', { name: 'Personal data' }); @@ -30,7 +31,6 @@ export class Account implements PageObject { } this.cannotDeliverToCountryAlert = page.getByText('We can not deliver to the country that is stored in your delivery address.'); this.shippingToAddressNotPossibleAlert = page.getByText('Shipping to the selected shipping address is currently not possible.'); - } async getCustomerGroupAlert(customerGroup: string): Promise { diff --git a/src/page-objects/storefront/AccountLogin.ts b/src/page-objects/storefront/AccountLogin.ts index df20affb..3b3f7c43 100644 --- a/src/page-objects/storefront/AccountLogin.ts +++ b/src/page-objects/storefront/AccountLogin.ts @@ -41,7 +41,6 @@ export class AccountLogin implements PageObject { public readonly shippingAddressPostalCodeInput: Locator; public readonly shippingAddressStateInput: Locator; - constructor(public readonly page: Page) { this.emailInput = page.getByLabel('Your email address'); this.passwordInput = page.getByLabel('Your password'); diff --git a/src/services/ShopwareDataHelpers.ts b/src/services/ShopwareDataHelpers.ts index ee6a4e27..26cf76d3 100644 --- a/src/services/ShopwareDataHelpers.ts +++ b/src/services/ShopwareDataHelpers.ts @@ -286,4 +286,31 @@ export const getPromotionWithDiscount = async (promotionId: string, adminApiCont const { data: promotion } = (await resp.json()) as { data: Promotion[] }; return promotion[0]; -}; \ No newline at end of file +}; + +export async function deleteRegisteredUser(adminApiContext: AdminApiContext, email: string): Promise { + if (!email) return; + + try { + const response = await adminApiContext.post('search/customer', { + data: { + limit: 1, + filter: [ + { + type: 'equals', + field: 'email', + value: email, + }, + ], + }, + }); + + const { data: customers } = (await response.json()) as { data: components['schemas']['Customer'][] }; + + for (const customer of customers) { + await adminApiContext.delete(`customer/${customer.id}`); + } + } catch (error) { + console.error(`Error deleting user with email ${email}:`, error); + } +} \ No newline at end of file diff --git a/src/services/TestDataService.ts b/src/services/TestDataService.ts index be5c5ccb..f6b0baf0 100644 --- a/src/services/TestDataService.ts +++ b/src/services/TestDataService.ts @@ -100,7 +100,7 @@ export class TestDataService { * * @private */ - private highPriorityEntities = ['order', 'product', 'landing_page', 'shipping_method', 'sales_channel_domain', 'sales_channel_currency', 'sales_channel_country', 'customer', 'customer_address']; + private highPriorityEntities = ['order', 'product', 'landing_page', 'shipping_method', 'sales_channel_domain', 'sales_channel_currency', 'sales_channel_country', 'customer']; /** * A registry of all created records. diff --git a/src/tasks/shop-customer/Account/AddNewAddress.ts b/src/tasks/shop-customer/Account/AddNewAddress.ts index 83d90367..324ad24a 100644 --- a/src/tasks/shop-customer/Account/AddNewAddress.ts +++ b/src/tasks/shop-customer/Account/AddNewAddress.ts @@ -4,11 +4,9 @@ import type { FixtureTypes} from '../../../types/FixtureTypes'; import { Address } from '../../../types/ShopwareTypes'; export const AddNewAddress = base.extend<{ AddNewAddress: Task }, FixtureTypes>({ - AddNewAddress: async ({ ShopCustomer, StorefrontAccountAddresses, StorefrontAccountAddressCreate }, use)=> { + AddNewAddress: async ({ StorefrontAccountAddresses, StorefrontAccountAddressCreate }, use)=> { const task = (address: Address) => { return async function AddNewAddress() { - - await ShopCustomer.goesTo(StorefrontAccountAddresses.url()); await StorefrontAccountAddresses.addNewAddressButton.click(); await StorefrontAccountAddressCreate.firstNameInput.fill(address.firstName); @@ -22,7 +20,6 @@ export const AddNewAddress = base.extend<{ AddNewAddress: Task }, FixtureTypes>( await StorefrontAccountAddressCreate.stateDropdown.selectOption({label: address.state}); await StorefrontAccountAddressCreate.saveAddressButton.click(); - } }; diff --git a/src/tasks/shop-customer/Account/Register.ts b/src/tasks/shop-customer/Account/Register.ts index dfaba2c2..82763fb1 100644 --- a/src/tasks/shop-customer/Account/Register.ts +++ b/src/tasks/shop-customer/Account/Register.ts @@ -1,9 +1,8 @@ import { test as base } from '@playwright/test'; import type { Task } from '../../../types/Task'; import type { FixtureTypes } from '../../../types/FixtureTypes'; -import type { components } from '@shopware/api-client/admin-api-types'; import type { RegistrationData } from '../../../types/ShopwareTypes'; -import { AdminApiContext } from 'src/services/AdminApiContext'; +import { deleteRegisteredUser } from '../../../services/ShopwareDataHelpers'; export const Register = base.extend<{ Register: Task }, FixtureTypes>({ Register: async ({ StorefrontAccountLogin, AdminApiContext, IdProvider }, use) => { @@ -67,31 +66,4 @@ export const Register = base.extend<{ Register: Task }, FixtureTypes>({ await deleteRegisteredUser(AdminApiContext, registeredEmail); }, -}); - -async function deleteRegisteredUser(adminApiContext: AdminApiContext, email: string): Promise { - if (!email) return; - - try { - const response = await adminApiContext.post('search/customer', { - data: { - limit: 1, - filter: [ - { - type: 'equals', - field: 'email', - value: email, - }, - ], - }, - }); - - const { data: customers } = (await response.json()) as { data: components['schemas']['Customer'][] }; - - for (const customer of customers) { - await adminApiContext.delete(`customer/${customer.id}`); - } - } catch (error) { - console.error(`Error deleting user with email ${email}:`, error); - } -} \ No newline at end of file +}); \ No newline at end of file