Skip to content

Commit

Permalink
[E2E] [TEST] Adding test for a line item refund type (saleor#5003)
Browse files Browse the repository at this point in the history
* adding changeset

* e2e tests for refunds

* refund test
  • Loading branch information
yellowee authored and luiz-linkezio committed Jul 4, 2024
1 parent 583c66f commit d1c114d
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .changeset/cool-doors-drum.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"saleor-dashboard": minor
"saleor-dashboard": patch
---

You can now run e2e tests without flakiness
5 changes: 5 additions & 0 deletions .changeset/stale-students-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---

You can now run E2E tests for refunds
6 changes: 3 additions & 3 deletions .github/actions/prepare-tests-variables/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ runs:
uses: ./.github/actions/prepare-api-variables
with:
MODE: ${{ inputs.MODE }}
VERSION: ${{ inputs.VERSION || ''}}
VERSION: ${{ inputs.VERSION || ''}}

- name: Generate backup
id: backup
uses: ./.github/actions/prepare-backups-variables
Expand All @@ -79,4 +79,4 @@ runs:
echo "::notice title=POOL_INSTANCE::${POOL_INSTANCE}"
echo "::notice title=BACKUP_NAMESPACE::${BACKUP_NAMESPACE}"
echo "::notice title=SNAPSHOT::backup_id=${BACKUP_ID}, version=${BACKUP_VER}, name=${BACKUP_NAME}"
echo "::notice title=SALEOR_CLOUD_SERVICE::${SALEOR_CLOUD_SERVICE}"
echo "::notice title=SALEOR_CLOUD_SERVICE::${SALEOR_CLOUD_SERVICE}"
35 changes: 35 additions & 0 deletions playwright/data/e2eTestData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,41 @@ export const ORDERS = {
orderNotFulfilledToChangeShippingAddress: {
id: "T3JkZXI6ZWFhZjA0MzgtNzkyYi00ZTdlLWIyODUtMTBkMjViMjM0MzRk",
},
fullyPaidOrdersWithSingleTransaction: {
first: {
id: "T3JkZXI6ZjZjZWUxMzItNDk2Zi00MWUyLWJkNTItYTk1MDM1YTVlZmVm",
lineItems: [
{
name: "Bean Juice",
quantity: "1",
},
{ name: "Lake Tunes", quantity: "2" },
],
},
second: {
id: "T3JkZXI6YzI4YjFmYmEtZWU1NS00YmU5LTg5MjktNTMyYzk5MDlkZGVk",
lineItems: ["Blue Hoodie 2", "Black Hoodie", "Mustard Hoodie", "White Hoodie"],
},
},
fullyPaidOrderWithSeveralTransactions: {
id: "T3JkZXI6MTVhYTEwMzYtZWE3OS00MzJiLTliODctNDhlYTMwYmU1NmNl",
},
partiallyPaidOrder: {
id: "T3JkZXI6NmVlMDMwMTctZTViOS00OGNmLWFkYTQtODg4YTQ5MDI3ZjNk",
},
orderWithRefunds: {
id: "T3JkZXI6Y2YyY2EwNWYtZmQ3Yy00ODk5LThjZTktMzQ4NjYxYThjZDkx",
refunds: [
{
lineOrderRefundId: "T3JkZXJHcmFudGVkUmVmdW5kOjE=",
amount: 4.5,
},
{
manualRefundId: "",
amount: 22.0,
},
],
},
};

export const SHIPPING_METHODS = {
Expand Down
21 changes: 21 additions & 0 deletions playwright/pages/dialogs/lineRefundReasonDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Page } from "@playwright/test";

export class AddLineRefundReasonDialog {
readonly page: Page;

constructor(
page: Page,
readonly lineRefundReasonInput = page.getByTestId("line-refund-reason-input"),
readonly confirmButton = page.getByTestId("confirm-button"),
) {
this.page = page;
}

async provideLineRefundReason(reason: string) {
await this.lineRefundReasonInput.fill(reason);
}

async submitLineRefundReason() {
await this.confirmButton.click();
}
}
24 changes: 24 additions & 0 deletions playwright/pages/dialogs/orderRefundDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { BasePage } from "@pages/basePage";
import { Page } from "@playwright/test";

export class OrderRefundDialog extends BasePage {
constructor(
page: Page,
readonly standardRefund = page.getByTestId("standard-refund"),
readonly manualRefund = page.getByTestId("manual-refund"),
readonly backButton = page.getByTestId("back-button"),
readonly proceedButton = page.getByTestId("proceed-button"),
) {
super(page);
}

async pickLineItemsRefund() {
await this.standardRefund.click();
await this.proceedButton.click();
}

async pickManualRefund() {
await this.manualRefund.click();
await this.proceedButton.click();
}
}
30 changes: 28 additions & 2 deletions playwright/pages/ordersPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { OrderCreateDialog } from "@pages/dialogs/orderCreateDialog";
import { ShippingAddressDialog } from "@pages/dialogs/shippingMethodDialog";
import { Page } from "@playwright/test";

import { OrderRefundDialog } from "./dialogs/orderRefundDialog";

export class OrdersPage extends BasePage {
orderCreateDialog: OrderCreateDialog;

Expand All @@ -29,6 +31,8 @@ export class OrdersPage extends BasePage {

rightSideDetailsPage: RightSideDetailsPage;

orderRefundDialog: OrderRefundDialog;

constructor(
page: Page,
readonly createOrderButton = page.getByTestId("create-order-button"),
Expand All @@ -47,8 +51,12 @@ export class OrdersPage extends BasePage {
readonly salesChannel = page.getByTestId("salesChannel"),
readonly addShippingCarrierLink = page.getByTestId("add-shipping-carrier"),
readonly finalizeButton = page.getByTestId("button-bar-confirm"),

readonly addRefundButton = page.getByTestId("add-new-refund-button"),
readonly customerEmail = page.getByTestId("customer-email"),
readonly orderRefundModal = page.getByTestId("order-refund-dialog"),
readonly orderRefundSection = page.getByTestId("order-refund-section"),
readonly orderRefundList = page.getByTestId("refund-list"),
readonly editRefundButton = page.getByTestId("edit-refund-button"),
) {
super(page);
this.markOrderAsPaidDialog = new MarkOrderAsPaidDialog(page);
Expand All @@ -60,6 +68,7 @@ export class OrdersPage extends BasePage {
this.manualTransactionDialog = new ManualTransactionDialog(page);
this.addTrackingDialog = new AddTrackingDialog(page);
this.rightSideDetailsPage = new RightSideDetailsPage(page);
this.orderRefundDialog = new OrderRefundDialog(page);
}

async clickCreateOrderButton() {
Expand Down Expand Up @@ -104,6 +113,23 @@ export class OrdersPage extends BasePage {

await console.log("Navigating to order details view: " + orderLink);
await this.page.goto(orderLink);
await this.waitForGrid();
await this.waitForDOMToFullyLoad();
}

async clickAddRefundButton() {
await this.addRefundButton.click();
await this.orderRefundModal.waitFor({ state: "visible" });
}

async clickEditRefundButton(refundInfo: string) {
const refund = await this.orderRefundList.locator("tr").filter({ hasText: refundInfo });

await refund.locator(this.editRefundButton).click();
}

async assertRefundOnList(refundInfo: string) {
const refund = await this.orderRefundList.locator("tr").filter({ hasText: refundInfo });

await refund.waitFor({ state: "visible" });
}
}
84 changes: 84 additions & 0 deletions playwright/pages/refundPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { URL_LIST } from "@data/url";
import { AddLineRefundReasonDialog } from "@pages/dialogs/lineRefundReasonDialog";
import { OrdersPage } from "@pages/ordersPage";
import { expect, Page } from "@playwright/test";

export class RefundPage extends OrdersPage {
addLineRefundReasonDialog: AddLineRefundReasonDialog;

constructor(
page: Page,
readonly allButton = page.getByTestId("all-button"),
readonly productQuantityInput = page.getByTestId("product-quantity-input"),
readonly maxLineRefundQuantity = page.getByTestId("max-line-refund-quantity"),
readonly refundReasonInput = page.getByTestId("refund-reason-input"),
readonly lineRefundReasonDialog = page.getByTestId("refund-reason-dialog"),
readonly lineRefundReasonButton = page.getByTestId("line-refund-reason-button"),
) {
super(page);
this.addLineRefundReasonDialog = new AddLineRefundReasonDialog(page);
}

async getProductRow(productName: string) {
return await this.page.locator("table tr").filter({ hasText: productName });
}

async expectLineItemsRefundPageOpen(orderId: string) {
const orderLink = `${URL_LIST.orders}${orderId}/refund`;

await expect(this.page).toHaveURL(orderLink);
await this.waitForDOMToFullyLoad();
await expect(this.pageHeader).toContainText("Create refund with line items");
}

async expectManualRefundPageOpen(orderId: string) {
const orderLink = `${URL_LIST.orders}${orderId}/manual-refund`;

await expect(this.page).toHaveURL(orderLink);
await expect(this.pageHeader).toContainText("Refund with manual amount");
}

async pickAllProductQuantityToRefund(productName: string) {
const productRow = await this.getProductRow(productName);

await productRow.locator(this.allButton).click();

const maxLineRefundQuantityText = await productRow
.locator(this.maxLineRefundQuantity)
.first()
.innerText();
const value = maxLineRefundQuantityText.slice(-1);

await expect(productRow.locator(this.productQuantityInput)).toHaveValue(value);
}

async provideRefundReason(reason: string) {
await this.refundReasonInput.fill(reason);
}

async clickLineRefundReasonButton(productName: string) {
const productRow = await this.getProductRow(productName);

await productRow.locator(this.lineRefundReasonButton).click();
await this.lineRefundReasonDialog.waitFor({ state: "visible" });
}

async inputProductLineQuantity(productName: string, amount: string) {
const productRow = await this.getProductRow(productName);

await productRow.locator(this.productQuantityInput).fill(amount);
await expect(productRow.locator(this.productQuantityInput)).toHaveValue(amount);
}

async saveDraft() {
await expect(this.saveButton).toHaveText("Save draft");
await this.clickSaveButton();
await this.waitForDOMToFullyLoad();
}

async transferFunds() {
await expect(this.saveButton).toHaveText("Transfer funds");
await this.clickSaveButton();
await this.waitForDOMToFullyLoad();
}
}
36 changes: 36 additions & 0 deletions playwright/tests/orders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DraftOrdersPage } from "@pages/draftOrdersPage";
import { AddressForm } from "@pages/forms/addressForm";
import { FulfillmentPage } from "@pages/fulfillmentPage";
import { OrdersPage } from "@pages/ordersPage";
import { RefundPage } from "@pages/refundPage";
import { expect, test } from "@playwright/test";

test.use({ storageState: "./playwright/.auth/admin.json" });
Expand All @@ -16,6 +17,7 @@ let fulfillmentPage: FulfillmentPage;
let addressDialog: AddressDialog;
let addressForm: AddressForm;
let addressesListPage: AddressesListPage;
let refundPage: RefundPage;

test.beforeEach(({ page }) => {
ordersPage = new OrdersPage(page);
Expand All @@ -24,6 +26,7 @@ test.beforeEach(({ page }) => {
addressDialog = new AddressDialog(page);
addressesListPage = new AddressesListPage(page);
addressForm = new AddressForm(page);
refundPage = new RefundPage(page);
});

const variantSKU = PRODUCTS.productAvailableWithTransactionFlow.variant1sku;
Expand Down Expand Up @@ -252,3 +255,36 @@ test("TC: SALEOR_84 Create draft order @e2e @draft", async () => {
await draftOrdersPage.clickFinalizeButton();
await draftOrdersPage.expectSuccessBannerMessage("finalized");
});

test("TC: SALEOR_191 Refund products from the fully paid order @e2e @refunds", async () => {
const order = ORDERS.fullyPaidOrdersWithSingleTransaction.first;

await ordersPage.goToExistingOrderPage(order.id);
await ordersPage.clickAddRefundButton();
await ordersPage.orderRefundDialog.pickLineItemsRefund();
await ordersPage.orderRefundModal.waitFor({ state: "hidden" });
await refundPage.expectLineItemsRefundPageOpen(order.id);
await refundPage.pickAllProductQuantityToRefund(order.lineItems[0].name);

const productRow = await refundPage.getProductRow(order.lineItems[0].name);

expect(productRow.locator(refundPage.productQuantityInput)).toHaveValue(
order.lineItems[0].quantity,
);

const refundReason = "Expectations not met";

await refundPage.inputProductLineQuantity(order.lineItems[1].name, "1");
await refundPage.clickLineRefundReasonButton(order.lineItems[0].name);
await refundPage.addLineRefundReasonDialog.provideLineRefundReason("Item is damaged");
await refundPage.addLineRefundReasonDialog.submitLineRefundReason();
await refundPage.provideRefundReason(refundReason);
await refundPage.saveDraft();
await refundPage.expectSuccessBanner();
await ordersPage.goToExistingOrderPage(order.id);
await ordersPage.orderRefundSection.waitFor({ state: "visible" });
await ordersPage.assertRefundOnList(refundReason);
await ordersPage.clickEditRefundButton(refundReason);
await refundPage.transferFunds();
await refundPage.expectSuccessBannerMessage("Refund has been sent");
});
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const OrderDetailsRefundLine: React.FC<OrderDetailsRefundLineProps> = ({
<Box display="flex" justifyContent="flex-end">
{isEditable ? (
<Link to={orderTransactionRefundEditUrl(orderId, refund.id)}>
<Button icon={<EditIcon />} variant="secondary" />
<Button data-test-id="edit-refund-button" icon={<EditIcon />} variant="secondary" />
</Link>
) : (
<Tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ export const OrderDetailsRefundTable: React.FC<OrderDetailsRefundTableProps> = (
});

return (
<DashboardCard>
<DashboardCard data-test-id="order-refund-section">
<Box paddingTop={6} display="flex" justifyContent="space-between" paddingX={6}>
<Text size={5} fontWeight="bold">
<FormattedMessage {...refundGridMessages.refundSection} />
</Text>
<Tooltip>
<Tooltip.Trigger>
<Button
data-test-id="add-new-refund-button"
variant="secondary"
onClick={onRefundAdd}
disabled={!isRefundPossible.canRefund}
Expand All @@ -56,7 +57,7 @@ export const OrderDetailsRefundTable: React.FC<OrderDetailsRefundTableProps> = (
)}
</Tooltip>
</Box>
<GridTable height="100%" paddingX={6}>
<GridTable data-test-id="refund-list" height="100%" paddingX={6}>
<GridTable.Colgroup>
<GridTable.Col __width="1%" />
<GridTable.Col __width="10%" />
Expand Down
9 changes: 6 additions & 3 deletions src/orders/components/OrderRefundDialog/OrderRefundDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const OrderRefundDialog = ({

return (
<DashboardModal open={open} onChange={onClose}>
<DashboardModal.Content __width="400px">
<DashboardModal.Content __width="400px" data-test-id="order-refund-dialog">
<DashboardModal.Title>
{intl.formatMessage(orderRefundDialogMesages.title)}
</DashboardModal.Title>
Expand Down Expand Up @@ -71,10 +71,13 @@ export const OrderRefundDialog = ({
</Box>
</RadioTiles>
<DashboardModal.Actions>
<Button onClick={onClose} variant="secondary">
<Button onClick={onClose} variant="secondary" data-test-id="back-button">
<Text fontWeight="medium">{intl.formatMessage(buttonMessages.back)}</Text>
</Button>
<Button onClick={selectedRefundType === "standard" ? onStandardRefund : onManualRefund}>
<Button
onClick={selectedRefundType === "standard" ? onStandardRefund : onManualRefund}
data-test-id="proceed-button"
>
<Text fontWeight="medium" color="buttonDefaultPrimary">
{intl.formatMessage(buttonMessages.proceed)}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const OrderTransactionReason: React.FC<OrderTransactionReasonProps> = ({
<FormattedMessage {...transactionRefundReasonMessages.reasonForRefund} />
</Text>
<Textarea
data-test-id="refund-reason-input"
placeholder={intl.formatMessage(transactionRefundReasonMessages.optionalPlaceholder)}
size="medium"
rows={4}
Expand Down
Loading

0 comments on commit d1c114d

Please sign in to comment.