Skip to content

Commit

Permalink
Migrate product variants tests to playwright (#4326)
Browse files Browse the repository at this point in the history
* migrated create products tests to Playwright

* permission tests with playwright wip

* migrated navigation tests to playwright

* switch to camel case in pw project - remove faker random number generator

* login function in setup unification, wait for success banner extended to 15s

* migrated create basic variant test to playwright

* Create full info variant - via edit variant page
  • Loading branch information
wojteknowacki authored Oct 17, 2023
1 parent c3f7721 commit e2975ef
Show file tree
Hide file tree
Showing 15 changed files with 256 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-cows-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---

Migrated "TC: SALEOR_26 Create basic info variant - via edit variant page" to playwright
2 changes: 1 addition & 1 deletion cypress/e2e/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
expectMainMenuSectionsToBeVisible,
} from "../support/pages/mainMenuPage";

describe("As a staff user I want to navigate through shop using different permissions", () => {
describe("As a staff user I want to navigate through shop using different permissions - migration in progress - to delete when done", () => {
it(
`should be able to navigate through shop as a staff member using DISCOUNTS permission. TC: SALEOR_3405a - sales list`,
{ tags: ["@allEnv", "@navigation", "@stable", "@oldRelease", "@critical"] },
Expand Down
8 changes: 8 additions & 0 deletions playwright/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/no-floating-promises": "error"
}
}
8 changes: 8 additions & 0 deletions playwright/data/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,12 @@ export const PRODUCTS = {
id: "UHJvZHVjdFR5cGU6Njcy",
name: "Single product type",
},
productToAddVariants: {
id: "UHJvZHVjdDo3Mjk%3D",
name: "Product that not contain any variant yet",
},
productWithOneVariant: {
id: "UHJvZHVjdDo3MzM%3D",
name: "Product that contains single variant",
},
};
10 changes: 10 additions & 0 deletions playwright/pages/basePage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LOCATORS } from "@data/commonLocators";
import { URL_LIST } from "@data/url";
import type { Locator, Page } from "@playwright/test";
import { expect } from "@playwright/test";
Expand All @@ -18,9 +19,18 @@ export class BasePage {
);
await expect(this.pageHeader).toBeVisible({ timeout: 10000 });
}
async gotoExistingProductPage(productId: string) {
await this.page.goto(`${URL_LIST.products}${productId}`);
await expect(this.pageHeader).toBeVisible({ timeout: 10000 });
}
async expectGridToBeAttached() {
await expect(this.gridCanvas).toBeAttached({
timeout: 10000,
});
}
async expectSuccessBanner() {
await expect(this.page.locator(LOCATORS.successBanner)).toBeVisible({
timeout: 15000,
});
}
}
Empty file.
17 changes: 10 additions & 7 deletions playwright/pages/productPage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as faker from "faker";

import { LOCATORS } from "@data/commonLocators";
import { URL_LIST } from "@data/url";
import { ChannelSelectDialog } from "@pages/dialogs/channelSelectDialog";
import { MetadataSeoPage } from "@pages/pageElements/metadataSeoPage";
import { RightSideDetailsPage } from "@pages/pageElements/rightSideDetailsSection";
import type { Locator, Page } from "@playwright/test";
import { expect } from "@playwright/test";

import { BasePage } from "./basePage";

const productName = `e2e-productName-${faker.datatype.number()}`;
const productDescription = `e2e-productDescription-${faker.datatype.number()}`;
Expand Down Expand Up @@ -42,18 +42,20 @@ export class ProductPage {
readonly uploadSavedImagesButton: Locator;
readonly uploadMediaUrlButton: Locator;
readonly saveUploadUrlButton: Locator;
readonly editVariant: Locator;
readonly editVariantButton: Locator;
readonly saveButton: Locator;
readonly firstRowDataGrid: Locator;
readonly addProductButton: Locator;
readonly productUpdateFormSection: Locator;
readonly manageChannelsButton: Locator;
metadataSeoPage: MetadataSeoPage;
rightSideDetailsPage: RightSideDetailsPage;
basePage: BasePage;
channelSelectDialog: ChannelSelectDialog;

constructor(page: Page) {
this.page = page;
this.basePage = new BasePage(page);
this.channelSelectDialog = new ChannelSelectDialog(page);
this.metadataSeoPage = new MetadataSeoPage(page);
this.rightSideDetailsPage = new RightSideDetailsPage(page);
Expand Down Expand Up @@ -81,7 +83,7 @@ export class ProductPage {
this.uploadSavedImagesButton = page.getByTestId("upload-images");
this.uploadMediaUrlButton = page.getByTestId("upload-media-url");
this.saveUploadUrlButton = page.getByTestId("upload-url-button");
this.editVariant = page.getByTestId("row-action-button");
this.editVariantButton = page.getByTestId("row-action-button");
this.productUpdateFormSection = page.getByTestId("product-update-form");
this.firstCategoryItem = page.locator("#downshift-0-item-0");
this.visibleRadioBtn = page.locator("[name='isPublished']");
Expand Down Expand Up @@ -150,9 +152,7 @@ export class ProductPage {
await this.saveButton.click();
}
async expectSuccessBanner() {
await expect(this.page.locator(LOCATORS.successBanner)).toBeVisible({
timeout: 15000,
});
await this.basePage.expectSuccessBanner();
}
async selectOneChannelAsAvailable() {
await this.manageChannelsButton.click();
Expand All @@ -164,6 +164,9 @@ export class ProductPage {
async clickCreateProductButton() {
await this.createProductButton.click();
}
async clickFirstEditVariantButton() {
await this.editVariantButton.first().click();
}

async gotoProductListPage() {
await this.page.goto(URL_LIST.products);
Expand Down
8 changes: 5 additions & 3 deletions playwright/pages/productTypePage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { LOCATORS } from "@data/commonLocators";
import { URL_LIST } from "@data/url";
import type { Locator, Page } from "@playwright/test";
import { expect } from "@playwright/test";

import { BasePage } from "./basePage";

export class ProductTypePage {
readonly page: Page;
basePage: BasePage;
readonly nameInput: Locator;
readonly isShippingRequired: Locator;
readonly assignProductAttributeButton: Locator;
Expand All @@ -18,6 +19,7 @@ export class ProductTypePage {

constructor(page: Page) {
this.page = page;
this.basePage = new BasePage(page);
this.addProductTypeButton = page.getByTestId("add-product-type");
this.notificationSuccess = page.getByTestId("notification-message");
this.nameInput = page.locator("[name='name']");
Expand Down Expand Up @@ -53,7 +55,7 @@ export class ProductTypePage {
await this.page.goto(URL_LIST.productTypesAdd);
}
async expectSuccessBanner() {
await expect(this.page.locator(LOCATORS.successBanner)).toBeVisible();
await this.basePage.expectSuccessBanner();
}

async gotoProductTypeListPage() {
Expand Down
129 changes: 129 additions & 0 deletions playwright/pages/variantsPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { Locator, Page } from "@playwright/test";

import { BasePage } from "./basePage";
import { ChannelSelectDialog } from "./dialogs/channelSelectDialog";
import { MetadataSeoPage } from "./pageElements/metadataSeoPage";

export class VariantsPage {
readonly page: Page;
readonly variantNameInput: Locator;
readonly skuTextField: Locator;
readonly attributeOption: Locator;
readonly attributeSelector: Locator;
readonly addWarehouseButton: Locator;
readonly warehouseOption: Locator;
readonly saveButton: Locator;
readonly stockInput: Locator;
readonly booleanAttributeCheckbox: Locator;
readonly selectOption: Locator;
readonly manageChannels: Locator;
readonly allChannels: Locator;
readonly chooseMediaButton: Locator;
readonly assignWarehouseButton: Locator;
readonly addVariantButton: Locator;
readonly priceFieldInput: Locator;
readonly variantsList: Locator;
readonly variantsNames: Locator;
readonly checkoutLimitInput: Locator;
readonly shippingWeightInput: Locator;
channelSelectDialog: ChannelSelectDialog;
metadataSeoPage: MetadataSeoPage;
basePage: BasePage;

constructor(page: Page) {
this.page = page;
this.basePage = new BasePage(page);
this.metadataSeoPage = new MetadataSeoPage(page);
this.channelSelectDialog = new ChannelSelectDialog(page);
this.variantNameInput = page.getByTestId("variant-name-input");
this.skuTextField = page.getByTestId("sku");
this.variantsList = page.getByTestId("variants-list");
this.variantsNames = page.getByTestId("variant-name");
this.attributeOption = page.getByTestId("select-option");
this.attributeSelector = page.getByTestId("attribute-value");
this.addWarehouseButton = page.getByTestId("add-warehouse");
this.chooseMediaButton = page.getByTestId("choose-media-button");
this.addVariantButton = page.getByTestId("button-add-variant");
this.warehouseOption = page.getByRole("menuitem");
this.saveButton = page.getByTestId("button-bar-confirm");
this.stockInput = page.getByTestId("stock-input");
this.shippingWeightInput = page.locator("[name='weight']");
this.priceFieldInput = page.getByTestId("price-field");
this.checkoutLimitInput = page.getByTestId("checkout-limit-input");
this.assignWarehouseButton = page.getByTestId("assign-warehouse-button");
this.booleanAttributeCheckbox = page.locator(
"[name*='attribute'][type='checkbox']",
);
this.selectOption = page.getByTestId("multi-autocomplete-select-option");
this.manageChannels = page.getByTestId("manage-channels-button");
this.allChannels = page.locator("[name='allChannels']");
}

async typeVariantName(variantName = "XXL beverage") {
await this.variantNameInput.fill(variantName);
}
async typeShippingWeight(weight = "150") {
await this.shippingWeightInput.fill(weight);
}
async typeCheckoutLimit(checkoutLimit = "10") {
await this.checkoutLimitInput.fill(checkoutLimit);
}
async typeSellingPriceInChannel(
channelName: string,
sellingPriceValue = "99",
) {
await this.page
.locator(`[data-test-id="Channel-${channelName}"]`)
.locator(this.priceFieldInput)
.first()
.fill(sellingPriceValue);
}
async typeCostPriceInChannel(channelName: string, costPriceValue = "10") {
await this.page
.locator(`[data-test-id="Channel-${channelName}"]`)
.locator(this.priceFieldInput)
.last()
.fill(costPriceValue);
}

async clickMageChannelsButton() {
await this.manageChannels.click();
}
async clickChooseMediaButton() {
await this.chooseMediaButton.click();
}
async clickAddVariantButton() {
await this.addVariantButton.click();
}

async typeSku(sku = "sku dummy e2e") {
await this.skuTextField.fill(sku);
}
async clickAssignWarehouseButton() {
await this.assignWarehouseButton.click();
}
async clickSaveVariantButton() {
await this.saveButton.click();
}
async expectSuccessBanner() {
await this.basePage.expectSuccessBanner();
}
async selectFirstAttributeValue() {
await this.attributeSelector.click();
await this.attributeOption.first().click();
}
async selectWarehouse(warehouse = "Oceania") {
await this.clickAssignWarehouseButton();
await this.warehouseOption.locator(`text=${warehouse}`).click();
}
async typeQuantityInStock(warehouse = "Oceania", quantity = "10") {
const quantityInput = await this.page
.getByTestId(warehouse)
.locator(this.stockInput);
await quantityInput.clear();
await quantityInput.fill(quantity);
}
async addAllMetaData() {
await this.metadataSeoPage.expandAndAddAllMetadata();
}
}
64 changes: 63 additions & 1 deletion playwright/tests/product.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { PRODUCTS } from "@data/testData";
import { BasePage } from "@pages/basePage";
import { ProductCreateDialog } from "@pages/dialogs/productCreateDialog";
import { ProductPage } from "@pages/productPage";
import { test } from "@playwright/test";
import { VariantsPage } from "@pages/variantsPage";
import { expect, test } from "@playwright/test";

test.use({ storageState: "playwright/.auth/admin.json" });

Expand Down Expand Up @@ -42,3 +43,64 @@ test("TC: SALEOR_5 Create basic product without variants @basic-regression @prod
await productPage.clickSaveButton();
await productPage.expectSuccessBanner();
});

test("TC: SALEOR_26 Create basic info variant - via edit variant page @basic-regression @product", async ({
page,
}) => {
const variantName = `TC: SALEOR_26 - variant name - ${new Date().toISOString()}`;
const basePage = new BasePage(page);
const productPage = new ProductPage(page);
const variantsPage = new VariantsPage(page);

await basePage.gotoExistingProductPage(PRODUCTS.productWithOneVariant.id);
await productPage.clickFirstEditVariantButton();
await variantsPage.clickAddVariantButton();
await variantsPage.typeVariantName(variantName);
await variantsPage.clickMageChannelsButton();
await variantsPage.channelSelectDialog.clickAllChannelsCheckbox();
await variantsPage.channelSelectDialog.selectFirstChannel();
await variantsPage.channelSelectDialog.clickConfirmButton();
await variantsPage.typeSellingPriceInChannel("PLN");
await variantsPage.typeCostPriceInChannel("PLN");
await variantsPage.clickSaveVariantButton();
await variantsPage.expectSuccessBanner();
await expect(
variantsPage.variantsList.locator(variantsPage.variantsNames, {
hasText: variantName,
}),
).toBeVisible();
});
test("TC: SALEOR_27 Create full info variant - via edit variant page @basic-regression @product", async ({
page,
}) => {
const variantName = `TC: SALEOR_26 - variant name - ${new Date().toISOString()}`;
const basePage = new BasePage(page);
const productPage = new ProductPage(page);
const variantsPage = new VariantsPage(page);

await basePage.gotoExistingProductPage(PRODUCTS.productWithOneVariant.id);
await productPage.clickFirstEditVariantButton();
await variantsPage.clickAddVariantButton();
await variantsPage.typeVariantName(variantName);
await variantsPage.clickMageChannelsButton();
await variantsPage.channelSelectDialog.clickAllChannelsCheckbox();
await variantsPage.channelSelectDialog.selectFirstChannel();
await variantsPage.channelSelectDialog.clickConfirmButton();
await variantsPage.selectFirstAttributeValue();
await variantsPage.typeCheckoutLimit();
await variantsPage.typeShippingWeight();
await variantsPage.typeSellingPriceInChannel("PLN");
await variantsPage.typeSku();
await variantsPage.addAllMetaData();
await variantsPage.clickSaveVariantButton();
await variantsPage.expectSuccessBanner();
await expect(
variantsPage.variantsList.locator(variantsPage.variantsNames, {
hasText: variantName,
}),
).toBeVisible();
await variantsPage.selectWarehouse();
await variantsPage.typeQuantityInStock();
await variantsPage.clickSaveVariantButton();
await variantsPage.expectSuccessBanner();
});
9 changes: 7 additions & 2 deletions src/products/components/ProductStocks/ProductStocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export const ProductStocks: React.FC<ProductStocksProps> = ({
);

return (
<TableRowLink key={stock.id}>
<TableRowLink data-test-id={stock.label} key={stock.id}>
<TableCell style={{ paddingLeft: vars.spacing[6] }}>
<Text>{stock.label}</Text>
</TableCell>
Expand Down Expand Up @@ -249,7 +249,12 @@ export const ProductStocks: React.FC<ProductStocksProps> = ({
warehousesToAssign.length > 0 && (
<Dropdown>
<Dropdown.Trigger>
<Button type="button" variant="secondary" marginTop={5}>
<Button
type="button"
variant="secondary"
marginTop={5}
data-test-id="assign-warehouse-button"
>
<FormattedMessage {...messages.assignWarehouse} />
</Button>
</Dropdown.Trigger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const ProductVariantCheckoutSettings: React.FC<
</DashboardCard.Title>
<DashboardCard.Content>
<Input
data-test-id="checkout-limit-input"
width="100%"
disabled={disabled}
error={!!formErrors.quantityLimitPerCustomer}
Expand Down
Loading

0 comments on commit e2975ef

Please sign in to comment.