Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEST][E2E] Add tests for customer CRUD #4657

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brown-mayflies-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---

E2E tests for customer CRUD
59 changes: 59 additions & 0 deletions playwright/data/addresses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import faker from "faker";
yellowee marked this conversation as resolved.
Show resolved Hide resolved

export type AddressType = {
addressUK: AddressFieldsType;
addressPL: AddressFieldsType;
addressUS: AddressFieldsType;
};

export type AddressFieldsType = {
firstName: string;
lastName: string;
companyName: string;
phone: string;
addressLine1: string;
addressLine2: string;
zip: string;
city: string;
country: string;
countryArea: string;
};

export const ADDRESS: AddressType = {
addressUS: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
companyName: faker.company.companyName(),
phone: "+12125771133",
addressLine1: "69 W 9th Street",
addressLine2: faker.address.county(),
city: "New York",
zip: "10001",
country: "United States of America",
countryArea: "New York",
},
addressPL: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
companyName: faker.company.companyName(),
phone: "+48225042123",
addressLine1: "Teczowa",
addressLine2: "7",
city: "WROCLAW",
zip: "53-601",
country: "Poland",
countryArea: "",
},
addressUK: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
companyName: faker.company.companyName(),
phone: "+445556667777",
addressLine1: "Albert Street",
addressLine2: "78/2",
city: "Edinburgh",
zip: "EH7 5LR",
country: "United Kingdom",
countryArea: "",
},
};
60 changes: 60 additions & 0 deletions playwright/data/e2eTestData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,3 +619,63 @@ export const PRODUCT_TYPES = {
ids: ["UHJvZHVjdFR5cGU6NzAw", "UHJvZHVjdFR5cGU6NzAx"],
},
};
export const CUSTOMERS = {
deleteCustomer: {
id: "VXNlcjoxMzY3",
email: "[email protected]",
},
editCustomer: {
id: "VXNlcjoxMzY2",
email: "[email protected]",
note: "simple note",
initialShippingAddress: {
firstName: "e2e_customer_with_addresses",
lastName: "to-be-edited",
companyName: "Saleor",
phone: "+48225042123",
addressLine1: "Teczowa",
addressLine2: "7",
city: "WROCLAW",
zip: "53-601",
country: "Poland",
},
initialBillingAddress: {
firstName: "address",
lastName: "to-be-deleted",
companyName: "Saleor",
phone: "+48225042123",
addressLine1: "Teczowa",
addressLine2: "7",
city: "WROCLAW",
zip: "53-601",
country: "Poland",
},
additionalAddress: {
firstName: "Test",
lastName: "Test",
addressLine1: "Nowy Świat",
city: "WARSZAWA",
zip: "00-504",
country: "Poland",
},
},
customersToBeBulkDeleted: {
names: [
"e2e_customer_1 bulk-delete",
"e2e_customer_2 bulk-delete",
"e2e_customer_3 bulk-delete",
],
},
customerToBeActivated: {
id: "VXNlcjoxMzY0",
email: "[email protected]",
firstName: "e2e-customer",
lastName: "to-be-activated",
},
customerToBeDeactivated: {
id: "VXNlcjoxMzY1",
email: "[email protected]",
firstName: "e2e-customer",
lastName: "to-be-deactivated",
},
};
104 changes: 104 additions & 0 deletions playwright/pages/addressesListPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { AddressFieldsType } from "@data/addresses";
import { expect, Page } from "@playwright/test";

import { BasePage } from "./basePage";

export class AddressesListPage extends BasePage {
constructor(
page: Page,
readonly editAddressButton = page.getByTestId("edit-address"),
readonly deleteAddressButton = page.getByTestId("delete-address"),
readonly addAddressButton = page.getByTestId("add-address"),
readonly addressTypeTitle = page.getByTestId("address-type-title"),
readonly addressCard = page.getByTestId("address-card"),
readonly savedAddress = page.getByTestId("address"),
readonly menageAddressButton = page.getByTestId("manage-addresses"),
readonly showMoreMenuButton = page.getByTestId("show-more-button"),
readonly addAddressDialog = page.getByTestId("add-address-dialog"),
readonly setAsDefaultBilling = page.getByTestId("set-default-billing-address"),
readonly setAsDefaultShipping = page.getByTestId("set-default-shipping-address"),
readonly deleteDialog = page.getByTestId("delete-address-dialog-content"),
readonly companyName = page.getByTestId("company-name"),
readonly addressLines = page.getByTestId("addressLines"),
readonly postalCodeAndCity = page.getByTestId("postal-code-and-city"),
readonly countryAreaAndCountry = page.getByTestId("country-area-and-country"),
readonly name = page.getByTestId("name"),
readonly phone = page.getByTestId("phone"),
) {
super(page);
}

async clickShowMoreMenu(addressPart: string) {
await this.addressCard
.filter({ hasText: addressPart })
.locator(this.showMoreMenuButton)
.click();
}

async clickManageAddresses() {
await this.menageAddressButton.click();
}

async clickAddAddressButton() {
await this.addAddressButton.click();
}

async clickEditAddress() {
await this.editAddressButton.click();
}

async clickDeleteAddress() {
await this.deleteAddressButton.click();
await this.waitForDOMToFullyLoad();
}

async setAsDeafultShippingAddress() {
await this.setAsDefaultShipping.click();
}

async setAsDeafultBillingAddress() {
await this.setAsDefaultBilling.click();
}

async verifyRequiredAddressFields(addressPart: string, address: AddressFieldsType) {
const addressToVerify = await this.savedAddress.filter({
hasText: addressPart,
});
const city = address.city.toUpperCase();

await expect(addressToVerify.locator(this.name)).toContainText(
`${address.firstName} ${address.lastName}`,
);
await expect(addressToVerify.locator(this.addressLines)).toContainText(address.addressLine1);
await expect(addressToVerify.locator(this.postalCodeAndCity)).toContainText(
` ${address.zip} ${city}`,
);
await expect(addressToVerify.locator(this.countryAreaAndCountry)).toContainText(
address.country,
);
}

async verifyPhoneField(addressPart: string, address: AddressFieldsType) {
const addressToVerify = await this.savedAddress.filter({
hasText: addressPart,
});

await expect(addressToVerify.locator(this.phone)).toContainText(address.phone);
}

async verifyCompanyField(addressPart: string, address: AddressFieldsType) {
const addressToVerify = await this.savedAddress.filter({
hasText: addressPart,
});

await expect(addressToVerify.locator(this.companyName)).toContainText(address.companyName);
}

async verifyAddressLine2Field(addressPart: string, address: AddressFieldsType) {
const addressToVerify = await this.savedAddress.filter({
hasText: addressPart,
});

await expect(addressToVerify.locator(this.addressLines)).toContainText(address.addressLine2);
}
}
40 changes: 38 additions & 2 deletions playwright/pages/basePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
readonly searchInputListView = page.getByTestId("search-input"),
readonly emptyDataGridListView = page.getByTestId("empty-data-grid-text"),
readonly dialog = page.getByRole("dialog"),
readonly giftCardInTable = page.locator('[href*="/dashboard/gift-cards/.*]'),
readonly selectAllCheckbox = page.getByTestId("select-all-checkbox").locator("input"),
) {
this.page = page;
Expand Down Expand Up @@ -63,7 +64,10 @@
}

async typeInSearchOnListView(searchItem: string) {
await this.searchInputListView.fill(searchItem);
await this.waitForNetworkIdle(async () => {
await this.searchInputListView.fill(searchItem);
await this.waitForDOMToFullyLoad();
});
}

async clickNextPageButton() {
Expand Down Expand Up @@ -95,6 +99,10 @@
await expect(this.errorBanner, "No error banner should be visible").not.toBeVisible();
}

async expectErrorBannerMessage(msg: string) {
await this.errorBanner.locator(`text=${msg}`).waitFor({ state: "visible", timeout: 10000 });
}

async expectSuccessBanner() {
await this.successBanner.first().waitFor({ state: "visible", timeout: 15000 });
await expect(this.errorBanner, "No error banner should be visible").not.toBeVisible();
Expand All @@ -105,8 +113,8 @@
await expect(this.errorBanner, "No error banner should be visible").not.toBeVisible();
}

async waitForNetworkIdle(action: () => Promise<void>, timeoutMs = 50000) {
async waitForNetworkIdle(action: () => Promise<void>, timeoutMs = 60000) {
const responsePromise = this.page.waitForResponse("**/graphql/", {

Check failure on line 117 in playwright/pages/basePage.ts

View workflow job for this annotation

GitHub Actions / run-tests (1/2)

[chromium] › orders.spec.ts:31:5 › TC: SALEOR_28 Create basic order @e2e @order

2) [chromium] › orders.spec.ts:31:5 › TC: SALEOR_28 Create basic order @e2e @order ─────────────── Error: page.waitForResponse: Test timeout of 45000ms exceeded. =========================== logs =========================== waiting for response "**/graphql/" ============================================================ at ../pages/basePage.ts:117 115 | 116 | async waitForNetworkIdle(action: () => Promise<void>, timeoutMs = 60000) { > 117 | const responsePromise = this.page.waitForResponse("**/graphql/", { | ^ 118 | timeout: timeoutMs, 119 | }); 120 | at ShippingAddressDialog.waitForNetworkIdle (/home/runner/work/saleor-dashboard/saleor-dashboard/playwright/pages/basePage.ts:117:39) at ShippingAddressDialog.pickAndConfirmFirstShippingMethod (/home/runner/work/saleor-dashboard/saleor-dashboard/playwright/pages/dialogs/shippingMethodDialog.ts:16:16) at /home/runner/work/saleor-dashboard/saleor-dashboard/playwright/tests/orders.spec.ts:43:42
timeout: timeoutMs,
});

Expand Down Expand Up @@ -146,6 +154,7 @@
We seek over the fiber node (hack), ignore typings for it.
*/
const fiberParent = node.parentNode[fiberKey as keyof typeof node.parentNode] as any;

const bounds: { x: number; y: number; width: number; height: number } =
fiberParent.pendingProps.children.props.gridRef.current.getBounds(col, row);

Expand All @@ -163,6 +172,21 @@
);
}

async checkGridCellTextAndClick(
columnNumber: number,
rowsToCheck: number[],
listToCheck: string[],
) {
const searchResults = [];

for (let i = 0; i < rowsToCheck.length; i++) {
const searchResult = await this.getGridCellText(rowsToCheck[i], columnNumber);

searchResults.push(searchResult);
await expect(searchResult).toEqual(listToCheck[i]);
await this.clickGridCell(columnNumber, rowsToCheck[i]);
}
}
/*
Example:

Expand Down Expand Up @@ -197,6 +221,7 @@
await this.gridCanvas.locator("table tr").first().waitFor({ state: "attached" });

const rowIndexes: number[] = [];

const rows = await this.page.$$eval("table tr", rows => rows.map(row => row.textContent));

for (const searchedText of searchTextArray) {
Expand Down Expand Up @@ -263,4 +288,15 @@
async waitForDOMToFullyLoad() {
await this.page.waitForLoadState("domcontentloaded", { timeout: 70000 });
}

async expectElementIsHidden(locator: Locator) {
await locator.first().waitFor({
state: "hidden",
timeout: 30000,
});
}

async waitForCanvasContainsText(text: string) {
await this.gridCanvas.getByText(text).waitFor({ state: "attached", timeout: 50000 });
}
}
Loading
Loading