From 7d59680a9f34c50c138cc865a89c2a29b8f00775 Mon Sep 17 00:00:00 2001 From: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:10:52 +0100 Subject: [PATCH] Order payment and fulfillment with transaction flow tests (#4561) * Create order with activated transaction flow in channel test * new assertions * Mark order as fully paid and fullfill all variants * Trigger Build * capture manual transactions test * remove not needed expect --- .changeset/rich-hairs-bow.md | 6 ++ playwright/data/e2eTestData.ts | 13 +++ playwright/pages/basePage.ts | 1 + .../pages/dialogs/manualTransactionDialog.ts | 45 +++++++++ .../pages/dialogs/markOrderAsPaidDialog.ts | 17 ++++ playwright/pages/fulfillmentPage.ts | 15 +++ playwright/pages/ordersPage.ts | 30 +++++- playwright/tests/orders.spec.ts | 92 ++++++++++++++++++- .../components/OrderDetailsPage/Title.tsx | 2 +- .../OrderPaymentSummaryCard.tsx | 1 + 10 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 .changeset/rich-hairs-bow.md create mode 100644 playwright/pages/dialogs/manualTransactionDialog.ts create mode 100644 playwright/pages/dialogs/markOrderAsPaidDialog.ts create mode 100644 playwright/pages/fulfillmentPage.ts diff --git a/.changeset/rich-hairs-bow.md b/.changeset/rich-hairs-bow.md new file mode 100644 index 00000000000..aa0ca12d861 --- /dev/null +++ b/.changeset/rich-hairs-bow.md @@ -0,0 +1,6 @@ +--- +"saleor-dashboard": minor +--- + +Mark order as fully paid and fulfill all variants test +Manual capture transactions and fulfill order test diff --git a/playwright/data/e2eTestData.ts b/playwright/data/e2eTestData.ts index 2a261c4aa2d..777d05dd2c4 100644 --- a/playwright/data/e2eTestData.ts +++ b/playwright/data/e2eTestData.ts @@ -64,6 +64,19 @@ export const PRODUCTS = { }, }; +export const ORDERS = { + ordersWithinTransactionFlow: { + markAsPaidOrder: { + orderId: "T3JkZXI6MDE4ZWM0NGUtNTgwMC00NGM0LTliMzAtZDE3YTIxYjljOTgz", + info: "Order used to mark as paid and fulfill", + }, + captureManualTransactionOrder: { + orderId: "T3JkZXI6MmE1NTNkMzktOWU0OS00ZWE5LWIyNzEtNzk2ZWI5OGJhNzcz", + info: "Order used to capture manual transactions and fulfill", + }, + }, +}; + export const SHIPPING_METHODS = { shippingMethodWithoutRates: { id: "U2hpcHBpbmdab25lOjIzNzg%3D", diff --git a/playwright/pages/basePage.ts b/playwright/pages/basePage.ts index 7b17482689f..d0f48980fb5 100644 --- a/playwright/pages/basePage.ts +++ b/playwright/pages/basePage.ts @@ -8,6 +8,7 @@ export class BasePage { constructor( page: Page, readonly pageHeader = page.getByTestId("page-header"), + readonly pageHeaderStatusInfo = page.getByTestId("status-info"), readonly bulkDeleteGridRowsButton = page.getByTestId("bulk-delete-button"), readonly gridCanvas = page.locator('[data-testid="data-grid-canvas"]'), readonly gridInput = page diff --git a/playwright/pages/dialogs/manualTransactionDialog.ts b/playwright/pages/dialogs/manualTransactionDialog.ts new file mode 100644 index 00000000000..1b5e8cca249 --- /dev/null +++ b/playwright/pages/dialogs/manualTransactionDialog.ts @@ -0,0 +1,45 @@ +import type { Page } from "@playwright/test"; + +export class ManualTransactionDialog { + constructor( + page: Page, + readonly transactionDescriptionInput = page.getByTestId( + "transactionDescription", + ), + readonly createManualTransactionButton = page.getByTestId( + "manualTransactionSubmit", + ), + readonly transactionPspReferenceInput = page.getByTestId( + "transactionPspReference", + ), + readonly transactAmountInput = page.getByTestId("transactAmountInput"), + ) {} + + async clickCreateManualTransactionButton() { + await this.createManualTransactionButton.click(); + await this.createManualTransactionButton.waitFor({ state: "hidden" }); + } + + async typeTransactionDescription(description = "partial payment") { + await this.transactionDescriptionInput.fill(description); + } + + async typeTransactionPspReference(reference = "999999999") { + await this.transactionPspReferenceInput.fill(reference); + } + + async typeTransactionAmount(amount = "100") { + await this.transactAmountInput.fill(amount); + } + + async completeManualTransactionDialogAndSave( + description: string, + reference: string, + transactionAmount: string, + ) { + await this.typeTransactionDescription(description); + await this.typeTransactionDescription(reference); + await this.typeTransactionAmount(transactionAmount); + await this.clickCreateManualTransactionButton(); + } +} diff --git a/playwright/pages/dialogs/markOrderAsPaidDialog.ts b/playwright/pages/dialogs/markOrderAsPaidDialog.ts new file mode 100644 index 00000000000..71cb828e9a9 --- /dev/null +++ b/playwright/pages/dialogs/markOrderAsPaidDialog.ts @@ -0,0 +1,17 @@ +import type { Page } from "@playwright/test"; + +export class MarkOrderAsPaidDialog { + constructor( + page: Page, + readonly transactionReferenceInput = page + .getByTestId("transaction-reference-input") + .locator("input"), + readonly confirmButton = page.getByTestId("submit"), + ) {} + + async typeAndSaveOrderReference(value = "09728937896253") { + await this.transactionReferenceInput.fill(value); + await this.confirmButton.click(); + await this.transactionReferenceInput.waitFor({ state: "hidden" }); + } +} diff --git a/playwright/pages/fulfillmentPage.ts b/playwright/pages/fulfillmentPage.ts new file mode 100644 index 00000000000..37f5eca9272 --- /dev/null +++ b/playwright/pages/fulfillmentPage.ts @@ -0,0 +1,15 @@ +import type { Page } from "@playwright/test"; + +export class FulfillmentPage { + readonly page: Page; + constructor( + page: Page, + readonly fulfillButton = page.getByTestId("button-bar-confirm"), + ) { + this.page = page; + } + + async clickFulfillButton() { + await this.fulfillButton.click(); + } +} diff --git a/playwright/pages/ordersPage.ts b/playwright/pages/ordersPage.ts index 1de3a6a6e7b..b2f07e2090e 100644 --- a/playwright/pages/ordersPage.ts +++ b/playwright/pages/ordersPage.ts @@ -1,27 +1,37 @@ import { URL_LIST } from "@data/url"; +import { ManualTransactionDialog } from "@dialogs/manualTransactionDialog"; +import { MarkOrderAsPaidDialog } from "@dialogs/markOrderAsPaidDialog"; +import { BasePage } from "@pages/basePage"; import { AddProductsDialog } from "@pages/dialogs/addProductsDialog"; import { AddressDialog } from "@pages/dialogs/addressDialog"; import { OrderCreateDialog } from "@pages/dialogs/orderCreateDialog"; import { ShippingAddressDialog } from "@pages/dialogs/shippingMethodDialog"; import { Page } from "@playwright/test"; -import { BasePage } from "./basePage"; - export class OrdersPage extends BasePage { orderCreateDialog: OrderCreateDialog; + markOrderAsPaidDialog: MarkOrderAsPaidDialog; addProductsDialog: AddProductsDialog; addressDialog: AddressDialog; shippingAddressDialog: ShippingAddressDialog; basePage: BasePage; + manualTransactionDialog: ManualTransactionDialog; constructor( page: Page, readonly createOrderButton = page.getByTestId("create-order-button"), readonly markAsPaidButton = page.getByTestId("markAsPaidButton"), + readonly manualTransactionButton = page.getByTestId( + "captureManualTransactionButton", + ), readonly orderSummarySection = page.getByTestId("OrderSummaryCard"), readonly paymentSummarySection = page.getByTestId("payment-section"), + readonly paymentStatusInfo = page.getByTestId("payment-status"), readonly fulfillButton = page.getByTestId("fulfill-button"), readonly addProducts = page.getByTestId("add-products-button"), + readonly orderTransactionsList = page + .getByTestId("orderTransactionsList") + .locator("table"), readonly salesChannel = page.getByTestId("salesChannel"), readonly editCustomerButton = page.getByTestId("edit-customer"), readonly searchCustomerInput = page.getByTestId("select-customer"), @@ -35,11 +45,13 @@ export class OrdersPage extends BasePage { ), ) { super(page); + this.markOrderAsPaidDialog = new MarkOrderAsPaidDialog(page); this.orderCreateDialog = new OrderCreateDialog(page); this.basePage = new BasePage(page); this.addProductsDialog = new AddProductsDialog(page); this.addressDialog = new AddressDialog(page); this.shippingAddressDialog = new ShippingAddressDialog(page); + this.manualTransactionDialog = new ManualTransactionDialog(page); } async selectCustomer(customer = "allison.freeman@example.com") { @@ -48,6 +60,15 @@ export class OrdersPage extends BasePage { async clickCreateOrderButton() { await this.createOrderButton.click(); } + async clickManualTransactionButton() { + await this.manualTransactionButton.click(); + } + async clickMarkAsPaidButton() { + await this.markAsPaidButton.click(); + } + async clickFulfillButton() { + await this.fulfillButton.click(); + } async clickAddShippingCarrierButton() { await this.addShippingCarrierLink.click(); } @@ -70,4 +91,9 @@ export class OrdersPage extends BasePage { async goToOrdersListView() { await this.page.goto(URL_LIST.orders); } + async goToExistingOrderPage(orderId: string) { + const orderLink = URL_LIST.orders + orderId; + await console.log("Navigating to order details view: " + orderLink); + await this.page.goto(orderLink); + } } diff --git a/playwright/tests/orders.spec.ts b/playwright/tests/orders.spec.ts index 058aaefd83f..9dddb10888e 100644 --- a/playwright/tests/orders.spec.ts +++ b/playwright/tests/orders.spec.ts @@ -1,12 +1,15 @@ -import { PRODUCTS } from "@data/e2eTestData"; +import { ORDERS, PRODUCTS } from "@data/e2eTestData"; +import { FulfillmentPage } from "@pages/fulfillmentPage"; import { OrdersPage } from "@pages/ordersPage"; import { expect, test } from "@playwright/test"; test.use({ storageState: "playwright/.auth/admin.json" }); let ordersPage: OrdersPage; +let fulfillmentPage: FulfillmentPage; test.beforeEach(({ page }) => { ordersPage = new OrdersPage(page); + fulfillmentPage = new FulfillmentPage(page); }); test("TC: SALEOR_28 Create basic order @e2e @order", async () => { @@ -55,3 +58,90 @@ test("TC: SALEOR_76 Create order with transaction flow activated @e2e @order", a await expect(ordersPage.orderSummarySection).toBeVisible(); await expect(ordersPage.fulfillButton).toBeDisabled(); }); + +test("TC: SALEOR_77 Mark order as paid and fulfill it with transaction flow activated @e2e @order", async () => { + await ordersPage.goToExistingOrderPage( + ORDERS.ordersWithinTransactionFlow.markAsPaidOrder.orderId, + ); + await ordersPage.waitForGrid(); + await ordersPage.clickMarkAsPaidButton(); + await ordersPage.markOrderAsPaidDialog.typeAndSaveOrderReference(); + await ordersPage.expectSuccessBannerMessage("paid"); + const transactionsMadeRows = await ordersPage.orderTransactionsList.locator( + "tr", + ); + expect(await transactionsMadeRows.count()).toEqual(1); + await expect(transactionsMadeRows).toContainText("Success"); + await ordersPage.clickFulfillButton(); + await fulfillmentPage.clickFulfillButton(); + await ordersPage.expectSuccessBannerMessage("fulfilled"); + expect(await ordersPage.pageHeaderStatusInfo).toContainText("Fulfilled"); +}); + +test("TC: SALEOR_78 Capture partial amounts by manual transactions and fulfill order with transaction flow activated @e2e @order", async () => { + const firstManualTransactionAmount = "100"; + const secondManualTransactionAmount = "20"; + + await ordersPage.goToExistingOrderPage( + ORDERS.ordersWithinTransactionFlow.captureManualTransactionOrder.orderId, + ); + await ordersPage.waitForGrid(); + await ordersPage.clickManualTransactionButton(); + await ordersPage.manualTransactionDialog.completeManualTransactionDialogAndSave( + "partial payment 1", + "111111", + firstManualTransactionAmount, + ); + const completedTransactionsRows = + await ordersPage.orderTransactionsList.locator("tr"); + + await expect( + completedTransactionsRows.filter({ + hasText: `EUR${firstManualTransactionAmount}`, + }), + "Row with first manual transaction details is visible with Success status", + ).toContainText("Success"); + expect( + await ordersPage.pageHeaderStatusInfo, + "Order should not be yet fulfilled", + ).toContainText("Unfulfilled"); + expect( + await ordersPage.paymentStatusInfo, + "Order should be partially paid", + ).toContainText("Partially paid"); + + await ordersPage.clickManualTransactionButton(); + await ordersPage.manualTransactionDialog.completeManualTransactionDialogAndSave( + "partial payment 2", + "222222", + secondManualTransactionAmount, + ); + + await expect( + completedTransactionsRows.filter({ + hasText: `EUR${secondManualTransactionAmount}`, + }), + "Row with first manual transaction details is visible with Success status", + ).toContainText("Success"); + expect( + await completedTransactionsRows.filter({ hasText: "Success" }).count(), + "Two rows are visible within Manual capture sections with Success status", + ).toEqual(2); + expect( + await ordersPage.pageHeaderStatusInfo, + "Order should not be yet fulfilled", + ).toContainText("Unfulfilled"); + expect( + await ordersPage.paymentStatusInfo, + "Order should fully paid", + ).toContainText("Fully paid"); + + await ordersPage.clickFulfillButton(); + await fulfillmentPage.clickFulfillButton(); + await ordersPage.expectSuccessBannerMessage("fulfilled"); + + expect( + await ordersPage.pageHeaderStatusInfo, + "Order should be yet fulfilled", + ).toContainText("Fulfilled"); +}); diff --git a/src/orders/components/OrderDetailsPage/Title.tsx b/src/orders/components/OrderDetailsPage/Title.tsx index 9c113fa0ae1..5ffae3813dc 100644 --- a/src/orders/components/OrderDetailsPage/Title.tsx +++ b/src/orders/components/OrderDetailsPage/Title.tsx @@ -46,7 +46,7 @@ const Title: React.FC = props => { { orderNumber: order?.number }, )}
- +
diff --git a/src/orders/components/OrderPaymentSummaryCard/OrderPaymentSummaryCard.tsx b/src/orders/components/OrderPaymentSummaryCard/OrderPaymentSummaryCard.tsx index 34e6c5ea925..90de73a0498 100644 --- a/src/orders/components/OrderPaymentSummaryCard/OrderPaymentSummaryCard.tsx +++ b/src/orders/components/OrderPaymentSummaryCard/OrderPaymentSummaryCard.tsx @@ -71,6 +71,7 @@ const OrderPaymentSummaryCard: React.FC = ({ label={payment.localized} color={payment.status} className={classes.paymentStatus} + data-test-id="payment-status" /> } title={}