From 7e9fa940cb33057189820e8f55a485e746a4c782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20Escalante=20=C3=81lvarez?= Date: Fri, 31 Jan 2025 19:52:28 +0100 Subject: [PATCH] WIP --- package-lock.json | 2 +- playwright.config.js | 5 + .../BackOffice.js} | 4 +- tests-e2e/fixtures/pages/CheckoutPage.js | 117 ++++++++++++++++++ tests-e2e/fixtures/pages/ProductPage.js | 58 +++++++++ tests-e2e/fixtures/test.js | 15 ++- .../SeQuraHelper.js} | 4 +- tests-e2e/specs/001-checkout-product.spec.js | 67 ++++++++++ .../004-configuration-payment-methods.spec.js | 9 +- 9 files changed, 264 insertions(+), 17 deletions(-) rename tests-e2e/fixtures/{MagentoBackOffice.js => base/BackOffice.js} (94%) create mode 100644 tests-e2e/fixtures/pages/CheckoutPage.js create mode 100644 tests-e2e/fixtures/pages/ProductPage.js rename tests-e2e/fixtures/{MagentoSeQuraHelper.js => utils/SeQuraHelper.js} (86%) create mode 100644 tests-e2e/specs/001-checkout-product.spec.js diff --git a/package-lock.json b/package-lock.json index baebd19..87808df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,7 +84,7 @@ }, "node_modules/playwright-fixture-for-plugins": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/sequra/playwright-fixture-for-plugins.git#b785ffd8bbbfd06d656c53957c87847f2380a65c", + "resolved": "git+ssh://git@github.com/sequra/playwright-fixture-for-plugins.git#a6f429e4ba930cf770893d12494f46aea60d98f3", "dev": true }, "node_modules/undici-types": { diff --git a/playwright.config.js b/playwright.config.js index c65bf0e..c5e346f 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -35,6 +35,11 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ + { + name: 'checkout-product', + use: { ...devices['Desktop Chrome'] }, + testMatch: '001-checkout-product.spec.js', + }, { name: 'configuration-payment-methods', use: { ...devices['Desktop Chrome'] }, diff --git a/tests-e2e/fixtures/MagentoBackOffice.js b/tests-e2e/fixtures/base/BackOffice.js similarity index 94% rename from tests-e2e/fixtures/MagentoBackOffice.js rename to tests-e2e/fixtures/base/BackOffice.js index c99b45e..7e5fab3 100644 --- a/tests-e2e/fixtures/MagentoBackOffice.js +++ b/tests-e2e/fixtures/base/BackOffice.js @@ -1,5 +1,5 @@ -import { BackOffice } from 'playwright-fixture-for-plugins'; -export default class MagentoBackOffice extends BackOffice { +import { BackOffice as BaseBackOffice } from 'playwright-fixture-for-plugins'; +export default class BackOffice extends BaseBackOffice { /** * Init the locators with the locators available diff --git a/tests-e2e/fixtures/pages/CheckoutPage.js b/tests-e2e/fixtures/pages/CheckoutPage.js new file mode 100644 index 0000000..8f337fc --- /dev/null +++ b/tests-e2e/fixtures/pages/CheckoutPage.js @@ -0,0 +1,117 @@ +import { CheckoutPage as BaseCheckoutPage } from "playwright-fixture-for-plugins"; + +/** + * Checkout page + */ +export default class CheckoutPage extends BaseCheckoutPage { + + /** + * Init the locators with the locators available + * + * @returns {Object} + */ + initLocators() { + return { + // ...super.initLocators(), + // messageSuccess: () => this.page.locator('.message-success'), + loader: () => this.page.locator('.loading-mask', { state: 'visible' }), + email: () => this.page.locator('#customer-email'), + firstName: () => this.page.locator('[name=firstname]'), + lastName: () => this.page.locator('[name=lastname]'), + // locator for selector [name="street[0]"] + address1: () => this.page.locator('[name="street[0]"]'), + country: () => this.page.locator('[name=country_id]'), + state: () => this.page.locator('[name=region_id]'), + city: () => this.page.locator('[name=city]'), + postcode: () => this.page.locator('[name=postcode]'), + phone: () => this.page.locator('[name=telephone]'), + flatRateShipping: () => this.page.locator('[value="flatrate_flatrate"]'), + continueButton: () => this.page.locator('.action.continue'), + }; + } + + /** + * Provide the checkout URL + * @param {Object} options + * @returns {string} The checkout URL + */ + checkoutUrl(options = {}) { + return `${this.baseURL}/checkout/`; + } + + /** + * Fill the checkout page's form + * @param {Object} options Contains the data to fill the form + * @param {string} options.email Email + * @param {string} options.firstName First name + * @param {string} options.lastName Last name + * @param {string} options.address1 Address first line + * @param {string} options.country Typically a 2-letter ISO country code + * @param {string} options.state Name of the state + * @param {string} options.city Name of the city + * @param {string} options.postcode Postcode + * @param {string} options.phone Phone number + * @param {string} options.shippingMethod Shipping method + * @returns {Promise} + */ + async fillForm(options) { + await this.fillShippingForm(options); + await this.selectShippingMethod(options); + await this.locators.continueButton().click(); + // TODO: Implement the form filling + } + + /** + * Fill the shipping form + * @param {Object} options + * @param {string} options.email Email + * @param {string} options.firstName First name + * @param {string} options.lastName Last name + * @param {string} options.address1 Address first line + * @param {string} options.country Typically a 2-letter ISO country code + * @param {string} options.state Name of the state + * @param {string} options.city Name of the city + * @param {string} options.postcode Postcode + * @param {string} options.phone Phone number + * @returns {Promise} + */ + async fillShippingForm(options) { + await this.page.waitForURL(/#shipping/); + await this.#waitForFinishLoading(); + const { email, firstName, lastName, address1, country, state, city, postcode, phone } = options; + // TODO: Implement the form filling + await this.locators.email().fill(email); + await this.locators.firstName().fill(firstName); + await this.locators.lastName().fill(lastName); + await this.locators.address1().fill(address1); + await this.locators.country().selectOption(country); + await this.locators.state().selectOption({ label: state }); + await this.locators.city().fill(city); + await this.locators.postcode().fill(postcode); + await this.locators.phone().fill(phone); + } + + /** + * Select the shipping method + * @param {Object} options + * @param {string} options.shippingMethod Shipping method + * @returns {Promise} + */ + async selectShippingMethod(options) { + await this.page.waitForURL(/#shipping/); + const { shippingMethod } = options; + // TODO: Implement the method selection + await this.#waitForFinishLoading(); + this.locators.flatRateShipping().click(); + } + + /** + * Wait for the checkout to finish loading + * @returns {Promise} + */ + async #waitForFinishLoading() { + do { + await this.expect(this.locators.loader().first()).toBeHidden(); + } while ((await this.locators.loader()) > 0); + } +} \ No newline at end of file diff --git a/tests-e2e/fixtures/pages/ProductPage.js b/tests-e2e/fixtures/pages/ProductPage.js new file mode 100644 index 0000000..bcc67e8 --- /dev/null +++ b/tests-e2e/fixtures/pages/ProductPage.js @@ -0,0 +1,58 @@ +import { ProductPage as BaseProductPage } from "playwright-fixture-for-plugins"; + +/** + * Product page + */ +export default class ProductPage extends BaseProductPage { + + /** + * Init the locators with the locators available + * + * @returns {Object} + */ + initLocators() { + return { + ...super.initLocators(), + messageSuccess: () => this.page.locator('.message-success'), + }; + } + + /** + * Provide the product URL + * @param {Object} options + * @param {string} options.slug The product slug + * @returns {string} The product URL + */ + productUrl(options) { + const { slug } = options; + return `${this.baseURL}/${slug}.html`; + } + + /** + * Provide the locator for the quantity input + * + * @param {Object} options + * @returns {import("@playwright/test").Locator} + */ + qtyLocator(options = {}) { + return this.page.locator('#qty'); + } + /** + * Provide the locator for adding to cart button + * + * @param {Object} options + * @returns {import("@playwright/test").Locator} + */ + addToCartLocator(options = {}) { + return this.page.locator('#product-addtocart-button'); + } + + /** + * Wait for the product to be in the cart + * @param {Object} options + * @returns {Promise} + */ + async expectProductIsInCart(options = {}) { + await this.locators.messageSuccess().waitFor({timeout: 10000}); + } +} \ No newline at end of file diff --git a/tests-e2e/fixtures/test.js b/tests-e2e/fixtures/test.js index f344edb..6a40ea9 100644 --- a/tests-e2e/fixtures/test.js +++ b/tests-e2e/fixtures/test.js @@ -1,12 +1,17 @@ import { test as baseTest, expect } from "@playwright/test"; -import { PaymentMethodsSettingsPage } from "playwright-fixture-for-plugins"; -import MagentoBackOffice from "./MagentoBackOffice"; -import MagentoSeQuraHelper from "./MagentoSeQuraHelper"; +import { DataProvider, PaymentMethodsSettingsPage } from "playwright-fixture-for-plugins"; +import BackOffice from "./base/BackOffice"; +import SeQuraHelper from "./utils/SeQuraHelper"; +import ProductPage from "./pages/ProductPage"; +import CheckoutPage from "./pages/CheckoutPage"; const test = baseTest.extend({ - backOffice: async ({ page, baseURL }, use) => await use(new MagentoBackOffice(page, baseURL, expect)), - helper: async ({ page, baseURL, request }, use) => await use(new MagentoSeQuraHelper(page, baseURL, expect, request)), + dataProvider: async ({ page, baseURL }, use) => await use(new DataProvider(page, baseURL, expect)), + backOffice: async ({ page, baseURL }, use) => await use(new BackOffice(page, baseURL, expect)), + helper: async ({ page, baseURL, request }, use) => await use(new SeQuraHelper(page, baseURL, expect, request)), paymentMethodsSettingsPage: async ({ page, baseURL, request, backOffice, helper}, use) => await use(new PaymentMethodsSettingsPage(page, baseURL, expect, request, backOffice, helper)), + productPage: async ({ page, baseURL, request}, use) => await use(new ProductPage(page, baseURL, expect, request)), + checkoutPage: async ({ page, baseURL, request}, use) => await use(new CheckoutPage(page, baseURL, expect, request)), }); test.afterEach(async ({ page }, testInfo) => { diff --git a/tests-e2e/fixtures/MagentoSeQuraHelper.js b/tests-e2e/fixtures/utils/SeQuraHelper.js similarity index 86% rename from tests-e2e/fixtures/MagentoSeQuraHelper.js rename to tests-e2e/fixtures/utils/SeQuraHelper.js index 782ab77..c48efe8 100644 --- a/tests-e2e/fixtures/MagentoSeQuraHelper.js +++ b/tests-e2e/fixtures/utils/SeQuraHelper.js @@ -1,6 +1,6 @@ -import { SeQuraHelper } from 'playwright-fixture-for-plugins'; +import { SeQuraHelper as BaseSeQuraHelper } from 'playwright-fixture-for-plugins'; -export default class MagentoSeQuraHelper extends SeQuraHelper { +export default class SeQuraHelper extends BaseSeQuraHelper { /** * Init the webhooks available diff --git a/tests-e2e/specs/001-checkout-product.spec.js b/tests-e2e/specs/001-checkout-product.spec.js new file mode 100644 index 0000000..74dde1e --- /dev/null +++ b/tests-e2e/specs/001-checkout-product.spec.js @@ -0,0 +1,67 @@ +import { test } from '../fixtures/test'; + +test.describe('Product checkout', () => { + + test('All available seQura products appear in the checkout', async ({ helper, dataProvider, productPage, checkoutPage }) => { + // Setup + const { dummy_config } = helper.webhooks; + const shopper = dataProvider.shopper(); + await helper.executeWebhook({ webhook: dummy_config }); // Setup for physical products. + + // Execution + await productPage.addToCart({ slug: 'push-it-messenger-bag', quantity: 1 }); + await checkoutPage.goto(); + await checkoutPage.fillForm(shopper); + // -- + + // await productPage.addToCart({ slug: 'sunglasses', quantity: 1 }); + + // const helper = new SeQuraHelper(request, expect); + // for (const version of ['classic', 'blocks']) { + // await helper.executeWebhook({ webhook: helper.webhooks.CHECKOUT_VERSION, args: [{ name: 'version', value: version }] }); + // await checkoutPage.goto(); + // if (version === 'blocks') { + // await checkoutPage.expectPaymentMethodsBeingReloaded(); + // } + // await checkoutPage.expectI1ToBeVisible(); + // await checkoutPage.expectSp1ToBeVisible(); + // await checkoutPage.expectPp3ToBeVisible(); + // await checkoutPage.expectEducationPopupToWork('100.00'); + // } + }); + + // test('Make a successful payment using any shopper name', async ({ productPage, checkoutPage }) => { + // await checkoutPage.setupForPhysicalProducts(); + // await productPage.addToCart({ slug: 'sunglasses', quantity: 1 }); + + // await checkoutPage.goto(); + // await checkoutPage.fillWithNonSpecialShopperName({}); + // await checkoutPage.expectPaymentMethodsBeingReloaded(); + // await checkoutPage.placeOrderUsingI1({ shopper: 'nonSpecial' }); + // await checkoutPage.waitForOrderSuccess(); + // }); + + // test('Make a 🍊 payment with "Review test approve" names', async ({ productPage, checkoutPage }) => { + // await checkoutPage.setupForPhysicalProducts(); + // await productPage.addToCart({ slug: 'sunglasses', quantity: 1 }); + + // await checkoutPage.goto(); + // await checkoutPage.fillWithReviewTest({}); + // await checkoutPage.expectPaymentMethodsBeingReloaded(); + // await checkoutPage.placeOrderUsingI1({}); + // await checkoutPage.waitForOrderOnHold(); + // await checkoutPage.expectOrderChangeTo({ toStatus: 'wc-processing' }); + // }); + + // test('Make a 🍊 payment with "Review test cancel" names', async ({ productPage, checkoutPage }) => { + // await checkoutPage.setupForPhysicalProducts(); + // await productPage.addToCart({ slug: 'sunglasses', quantity: 1 }); + + // await checkoutPage.goto(); + // await checkoutPage.fillWithReviewTest({ shopper: 'cancel' }); + // await checkoutPage.expectPaymentMethodsBeingReloaded(); + // await checkoutPage.placeOrderUsingI1({}); + // await checkoutPage.waitForOrderOnHold(); + // await checkoutPage.expectOrderChangeTo({ toStatus: 'wc-cancelled' }); + // }); +}); \ No newline at end of file diff --git a/tests-e2e/specs/004-configuration-payment-methods.spec.js b/tests-e2e/specs/004-configuration-payment-methods.spec.js index 4369159..4b5ad23 100644 --- a/tests-e2e/specs/004-configuration-payment-methods.spec.js +++ b/tests-e2e/specs/004-configuration-payment-methods.spec.js @@ -1,15 +1,10 @@ import { test } from '../fixtures/test'; test.describe('Configuration', () => { - test('Payment methods', async ({ helper, paymentMethodsSettingsPage }) => { + test('Payment methods', async ({ helper, dataProvider, paymentMethodsSettingsPage }) => { // Setup const { dummy_config } = helper.webhooks; - const countries = [ - { name: 'Spain', paymentMethods: ['Paga Después', 'Divide tu pago en 3', 'Paga Fraccionado'] }, - { name: 'France', paymentMethods: ['Payez en plusieurs fois'] }, - { name: 'Italy', paymentMethods: ['Pagamento a rate'] }, - { name: 'Portugal', paymentMethods: ['Pagamento Fracionado'] } - ]; + const countries = dataProvider.countriesPaymentMethods(); // Execution await helper.executeWebhook({ webhook: dummy_config }); await paymentMethodsSettingsPage.goto();