diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js
index fadd49c4e..153edfc1b 100644
--- a/cypress/apiRequests/Product.js
+++ b/cypress/apiRequests/Product.js
@@ -132,7 +132,12 @@ export function createVariant({
return cy.sendRequestWithQuery(mutation);
}
-export function createTypeProduct(name, attributeId, slug = name) {
+export function createTypeProduct(
+ name,
+ attributeId,
+ hasVariants = true,
+ slug = name
+) {
const mutation = `mutation{
productTypeCreate(input: {
name: "${name}"
@@ -140,6 +145,7 @@ export function createTypeProduct(name, attributeId, slug = name) {
isShippingRequired: true
productAttributes: "${attributeId}"
variantAttributes: "${attributeId}"
+ hasVariants: ${hasVariants}
}){
productErrors{
field
diff --git a/cypress/elements/catalog/assign-products.js b/cypress/elements/catalog/products/assign-products.js
similarity index 100%
rename from cypress/elements/catalog/assign-products.js
rename to cypress/elements/catalog/products/assign-products.js
diff --git a/cypress/elements/catalog/products/price-list.js b/cypress/elements/catalog/products/price-list.js
new file mode 100644
index 000000000..6b5f425cd
--- /dev/null
+++ b/cypress/elements/catalog/products/price-list.js
@@ -0,0 +1,4 @@
+export const PRICE_LIST = {
+ priceInput: "[name*='channel-price']",
+ costPriceInput: "[name*='channel-costPrice']"
+};
diff --git a/cypress/elements/catalog/products/product-details.js b/cypress/elements/catalog/products/product-details.js
new file mode 100644
index 000000000..e7a94040c
--- /dev/null
+++ b/cypress/elements/catalog/products/product-details.js
@@ -0,0 +1,16 @@
+export const PRODUCT_DETAILS = {
+ productNameInput: "[name='name']",
+ productTypeInput: "[data-test='product-type']",
+ categoryInput: "[data-test='category']",
+ categoryItem: "[data-test='singleautocomplete-select-option']",
+ collectionInput: "[data-test='collections']",
+ autocompleteDropdown: "[data-test='autocomplete-dropdown']",
+ firstCategoryItem: "#downshift-0-item-0",
+ visibleRadioBtn: "[name='isPublished']",
+ confirmationMsg: "[data-test='notification-success']",
+ channelAvailabilityItem: "[data-test='channel-availability-item']",
+ addVariantsButton: "[data-test*='button-add-variant']",
+ descriptionInput: "[data-test-id='description']",
+ ratingInput: "[name='rating']",
+ skuInput: "[name='sku']"
+};
diff --git a/cypress/elements/catalog/products/product-selectors.js b/cypress/elements/catalog/products/product-selectors.js
deleted file mode 100644
index d78adce75..000000000
--- a/cypress/elements/catalog/products/product-selectors.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export const PRODUCTS_SELECTORS = {
- productsList: "[data-test-id][data-test='id']",
- products: "[data-test='submenu-item-label'][data-test-id='products']",
- createProductBtn: "[data-test='add-product']",
- productNameInput: "[name='name']",
- productTypeInput: "[data-test='product-type']",
- categoryInput: "[data-test='category']",
- categoryItem: "[data-test='singleautocomplete-select-option']",
- autocompleteDropdown: "[data-test='autocomplete-dropdown']",
- firstCategoryItem: "#downshift-0-item-0",
- visibleRadioBtn: "[name='isPublished']",
- saveBtn: "[data-test='button-bar-confirm']",
- confirmationMsg: "[data-test='notification-success']",
- channelAvailabilityItem: "[data-test='channel-availability-item']",
- searchProducts: "[placeholder='Search Products...']",
- availableManageButton:
- "[data-test-id='channels-availiability-manage-button']",
- channelsAvailabilityForm:
- "[data-test-id='manage-products-channels-availiability-list']",
- channelAvailabilityColumn:
- "[data-test='availability'][data-test-availability='true']",
- channelAvailabilityList: "ul[role='menu']",
- goBackButton: "[data-test-id='app-header-back-button']",
- assignedChannels: "[data-test='channel-availability-item']",
- publishedRadioButton: "[role=radiogroup]",
- addVariantsButton: "[data-test*='button-add-variant']",
- publishedRadioButtons: "[name*='isPublished']",
- availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
- radioButtonsValueTrue: "[value='true']",
- radioButtonsValueFalse: "[value='false']",
- visibleInListingsButton: "[name*='visibleInListings']",
- emptyProductRow: "[class*='Skeleton']"
-};
diff --git a/cypress/elements/catalog/products/products-list.js b/cypress/elements/catalog/products/products-list.js
new file mode 100644
index 000000000..a9e1cf26a
--- /dev/null
+++ b/cypress/elements/catalog/products/products-list.js
@@ -0,0 +1,6 @@
+export const PRODUCTS_LIST = {
+ productsList: "[data-test-id][data-test='id']",
+ createProductBtn: "[data-test='add-product']",
+ searchProducts: "[placeholder='Search Products...']",
+ emptyProductRow: "[class*='Skeleton']"
+};
diff --git a/cypress/elements/catalog/variants-selectors.js b/cypress/elements/catalog/products/variants-selectors.js
similarity index 86%
rename from cypress/elements/catalog/variants-selectors.js
rename to cypress/elements/catalog/products/variants-selectors.js
index f53e7295e..ba21c4434 100644
--- a/cypress/elements/catalog/variants-selectors.js
+++ b/cypress/elements/catalog/products/variants-selectors.js
@@ -2,8 +2,6 @@ export const VARIANTS_SELECTORS = {
attributeCheckbox: "[name*='value:']",
valueContainer: "[data-test-id='value-container']",
nextButton: "[class*='MuiButton-containedPrimary']",
- priceInput: "[name*='channel-price']",
- costPriceInput: "[name*='channel-costPrice']",
warehouseCheckboxes: "[name*='warehouse:']",
skuInput: "input[class*='MuiInputBase'][type='text']",
attributeSelector: "[data-test='attribute-value']",
diff --git a/cypress/elements/channels/available-channels-form.js b/cypress/elements/channels/available-channels-form.js
new file mode 100644
index 000000000..e7be964b5
--- /dev/null
+++ b/cypress/elements/channels/available-channels-form.js
@@ -0,0 +1,10 @@
+export const AVAILABLE_CHANNELS_FORM = {
+ menageChannelsButton: "[data-test-id='channels-availiability-manage-button']",
+ assignedChannels: "[data-test='channel-availability-item']",
+ publishedRadioButtons: "[name*='isPublished']",
+ availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
+ radioButtonsValueTrue: "[value='true']",
+ radioButtonsValueFalse: "[value='false']",
+ visibleInListingsButton: "[name*='visibleInListings']",
+ availableChannel: "[data-test='channel-availability-item']"
+};
diff --git a/cypress/elements/channels/menage-channel-availability-form.js b/cypress/elements/channels/menage-channel-availability-form.js
deleted file mode 100644
index dfbc48256..000000000
--- a/cypress/elements/channels/menage-channel-availability-form.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export const MENAGE_CHANNEL_AVAILABILITY_FORM = {
- channelsMenageButton: "[data-test-id='channels-availiability-manage-button']",
- allChannelsCheckbox: "[name='allChannels']",
- channelRow: "[data-test-id='channel-row']",
- channelCheckbox: "[class*='MuiCheckbox']",
- channelsAvailabilityItem: "[data-test='channel-availability-item']",
- publishedCheckbox: "[name='isPublished']",
- radioButtonsValueTrue: "[value='true']",
- radioButtonsValueFalse: "[value='false']"
-};
diff --git a/cypress/elements/channels/menage-channel-availability.js b/cypress/elements/channels/menage-channel-availability.js
deleted file mode 100644
index f74bd8ef4..000000000
--- a/cypress/elements/channels/menage-channel-availability.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export const MENAGE_CHANNEL_AVAILABILITY = {
- availableManageButton:
- "[data-test-id='channels-availiability-manage-button']",
- channelsAvailabilityForm:
- "[data-test-id='manage-products-channels-availiability-list']",
- channelAvailabilityColumn:
- "[data-test='availability'][data-test-availability='true']",
- channelAvailabilityList: "ul[role='menu']",
- assignedChannels: "[data-test='channel-availability-item']",
- publishedRadioButtons: "[name*='isPublished']",
- availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
- radioButtonsValueTrue: "[value='true']",
- radioButtonsValueFalse: "[value='false']",
- visibleInListingsButton: "[name*='visibleInListings']",
- allChannelsInput: "[name='allChannels']",
- dialog: "[role='dialog']"
-};
diff --git a/cypress/elements/channels/select-channels-to-assign.js b/cypress/elements/channels/select-channels-to-assign.js
new file mode 100644
index 000000000..57b170a92
--- /dev/null
+++ b/cypress/elements/channels/select-channels-to-assign.js
@@ -0,0 +1,8 @@
+export const SELECT_CHANNELS_TO_ASSIGN = {
+ listOfChannels:
+ "[data-test-id='manage-products-channels-availiability-list']",
+ allChannelsCheckbox: "[name='allChannels']",
+ selectChannelsForm: "[role='dialog']",
+ channelRow: "[data-test-id='channel-row']",
+ channelCheckbox: "[class*='MuiCheckbox']"
+};
diff --git a/cypress/elements/shared/button-selectors.js b/cypress/elements/shared/button-selectors.js
index a71e4c0ee..94540610c 100644
--- a/cypress/elements/shared/button-selectors.js
+++ b/cypress/elements/shared/button-selectors.js
@@ -2,5 +2,7 @@ export const BUTTON_SELECTORS = {
back: '[data-test="back"]',
submit: '[data-test="submit"]',
confirm: '[data-test="button-bar-confirm"]',
- checkbox: "[type='checkbox']"
+ goBackButton: "[data-test-id='app-header-back-button']",
+ checkbox: "[type='checkbox']",
+ selectOption: "[data-test*='select-option']"
};
diff --git a/cypress/elements/shared/metadata/metadata-form.js b/cypress/elements/shared/metadata/metadata-form.js
new file mode 100644
index 000000000..84d5798aa
--- /dev/null
+++ b/cypress/elements/shared/metadata/metadata-form.js
@@ -0,0 +1,8 @@
+export const METADATA_FORM = {
+ metadataForm: "[data-test='metadataEditor'][data-test-is-private='false']",
+ privateMetadataForm:
+ "[data-test='metadataEditor'][data-test-is-private='true']",
+ addFieldButton: "[data-test='addField']",
+ nameInput: "[name*='name']",
+ valueField: "[name*='value']"
+};
diff --git a/cypress/elements/shared/seo/seo-form-selectors.js b/cypress/elements/shared/seo/seo-form-selectors.js
new file mode 100644
index 000000000..58bfc7cc0
--- /dev/null
+++ b/cypress/elements/shared/seo/seo-form-selectors.js
@@ -0,0 +1,6 @@
+export const SEO_FORM = {
+ editSeoSettings: "[data-test-id='edit-seo']",
+ slugInput: "[name='slug']",
+ titleInput: "[name='seoTitle']",
+ descriptionInput: "[name='seoDescription']"
+};
diff --git a/cypress/integration/channels.js b/cypress/integration/channels.js
index e382cbf47..0f4311107 100644
--- a/cypress/integration/channels.js
+++ b/cypress/integration/channels.js
@@ -3,10 +3,12 @@ import faker from "faker";
import { createChannel } from "../apiRequests/Channels";
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
-import { PRODUCTS_SELECTORS } from "../elements/catalog/products/product-selectors";
+import { PRODUCTS_LIST } from "../elements/catalog/products/products-list";
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
+import { AVAILABLE_CHANNELS_FORM } from "../elements/channels/available-channels-form";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
+import { SELECT_CHANNELS_TO_ASSIGN } from "../elements/channels/select-channels-to-assign";
import { CONFIGURATION_SELECTORS } from "../elements/configuration/configuration-selectors";
import { HEADER_SELECTORS } from "../elements/header/header-selectors";
import { DRAFT_ORDER_SELECTORS } from "../elements/orders/draft-order-selectors";
@@ -65,12 +67,12 @@ describe("Channels", () => {
cy.addAliasToGraphRequest("InitialProductFilterData");
cy.visit(urlList.products);
cy.wait("@InitialProductFilterData");
- cy.get(PRODUCTS_SELECTORS.productsList)
+ cy.get(PRODUCTS_LIST.productsList)
.first()
.click()
- .get(PRODUCTS_SELECTORS.availableManageButton)
+ .get(AVAILABLE_CHANNELS_FORM.menageChannelsButton)
.click()
- .get(PRODUCTS_SELECTORS.channelsAvailabilityForm)
+ .get(SELECT_CHANNELS_TO_ASSIGN.listOfChannels)
.contains(randomChannel);
});
diff --git a/cypress/integration/collections.js b/cypress/integration/collections.js
index 5c64a75dd..c31e341ca 100644
--- a/cypress/integration/collections.js
+++ b/cypress/integration/collections.js
@@ -11,7 +11,7 @@ import {
import { urlList } from "../url/urlList";
import * as channelsUtils from "../utils/channelsUtils";
import { deleteCollectionsStartsWith } from "../utils/collectionsUtils";
-import * as productsUtils from "../utils/productsUtils";
+import * as productsUtils from "../utils/products/productsUtils";
import { deleteShippingStartsWith } from "../utils/shippingUtils";
import {
isCollectionVisible,
diff --git a/cypress/integration/discounts/sales.js b/cypress/integration/discounts/sales.js
index f516600ec..8df14d6c4 100644
--- a/cypress/integration/discounts/sales.js
+++ b/cypress/integration/discounts/sales.js
@@ -11,7 +11,7 @@ import {
import { urlList } from "../../url/urlList";
import * as channelsUtils from "../../utils/channelsUtils";
import { deleteSalesStartsWith } from "../../utils/discounts/salesUtils";
-import * as productsUtils from "../../utils/productsUtils";
+import * as productsUtils from "../../utils/products/productsUtils";
import {
createShipping,
deleteShippingStartsWith
diff --git a/cypress/integration/discounts/vouchers.js b/cypress/integration/discounts/vouchers.js
index 46000e1ca..74dbe32f3 100644
--- a/cypress/integration/discounts/vouchers.js
+++ b/cypress/integration/discounts/vouchers.js
@@ -9,7 +9,7 @@ import { urlList } from "../../url/urlList";
import * as channelsUtils from "../../utils/channelsUtils";
import { deleteVouchersStartsWith } from "../../utils/discounts/vouchersUtils";
import { createCheckoutWithVoucher } from "../../utils/ordersUtils";
-import * as productsUtils from "../../utils/productsUtils";
+import * as productsUtils from "../../utils/products/productsUtils";
import {
createShipping,
deleteShippingStartsWith
diff --git a/cypress/integration/homePage/homePageAnalitics.js b/cypress/integration/homePage/homePageAnalitics.js
index c9aca74fd..3046b5765 100644
--- a/cypress/integration/homePage/homePageAnalitics.js
+++ b/cypress/integration/homePage/homePageAnalitics.js
@@ -13,7 +13,7 @@ import {
createReadyToFulfillOrder,
createWaitingForCaptureOrder
} from "../../utils/ordersUtils";
-import * as productsUtils from "../../utils/productsUtils";
+import * as productsUtils from "../../utils/products/productsUtils";
import * as shippingUtils from "../../utils/shippingUtils";
//
diff --git a/cypress/integration/orders/draftOrders.js b/cypress/integration/orders/draftOrders.js
index 68ee36872..c8e968656 100644
--- a/cypress/integration/orders/draftOrders.js
+++ b/cypress/integration/orders/draftOrders.js
@@ -11,7 +11,7 @@ import { selectChannelInPicker } from "../../steps/channelsSteps";
import { finalizeDraftOrder } from "../../steps/draftOrderSteps";
import { urlList } from "../../url/urlList";
import { getDefaultChannel } from "../../utils/channelsUtils";
-import * as productsUtils from "../../utils/productsUtils";
+import * as productsUtils from "../../utils/products/productsUtils";
import {
createShipping,
deleteShippingStartsWith
diff --git a/cypress/integration/orders/orders.js b/cypress/integration/orders/orders.js
index 77a0391ed..3f6fe3b0c 100644
--- a/cypress/integration/orders/orders.js
+++ b/cypress/integration/orders/orders.js
@@ -11,7 +11,7 @@ import { finalizeDraftOrder } from "../../steps/draftOrderSteps";
import { urlList } from "../../url/urlList";
import { getDefaultChannel } from "../../utils/channelsUtils";
import { createOrder } from "../../utils/ordersUtils";
-import * as productsUtils from "../../utils/productsUtils";
+import * as productsUtils from "../../utils/products/productsUtils";
import {
createShipping,
deleteShippingStartsWith
diff --git a/cypress/integration/products/createProduct.js b/cypress/integration/products/createProduct.js
new file mode 100644
index 000000000..f818a33b1
--- /dev/null
+++ b/cypress/integration/products/createProduct.js
@@ -0,0 +1,123 @@
+//
+import faker from "faker";
+
+import { PRODUCT_DETAILS } from "../../elements/catalog/products/product-details";
+import { PRODUCTS_LIST } from "../../elements/catalog/products/products-list";
+import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
+import { metadataForms } from "../../steps/catalog/metadataSteps";
+import {
+ fillUpPriceList,
+ priceInputLists
+} from "../../steps/catalog/products/priceList";
+import { fillUpCommonFieldsForProductType } from "../../steps/catalog/products/productSteps";
+import { selectChannelInDetailsPages } from "../../steps/channelsSteps";
+import { urlList } from "../../url/urlList";
+import {
+ expectCorrectProductInformation,
+ expectCorrectProductVariantInformation
+} from "../../utils/products/checkProductInfo";
+import * as productUtils from "../../utils/products/productsUtils";
+
+describe("Create product", () => {
+ const startsWith = "Cy-";
+ const name = `${startsWith}${faker.random.number()}`;
+ const generalInfo = {
+ name: `${startsWith}${faker.random.number()}`,
+ description: faker.lorem.sentence(),
+ rating: 2
+ };
+ const seo = {
+ title: "testTitle",
+ description: generalInfo.description
+ };
+ const metadata = {
+ public: {
+ metadataForm: metadataForms.public,
+ name: "metadataName",
+ value: "metadataValue"
+ },
+ private: {
+ metadataForm: metadataForms.private,
+ name: "privateMetadataName",
+ value: "privateMetadataValue"
+ }
+ };
+ let attribute;
+
+ before(() => {
+ cy.clearSessionData().loginUserViaRequest();
+ productUtils.deleteProductsStartsWith(startsWith);
+ productUtils.createAttribute(name).then(attributeResp => {
+ attribute = attributeResp;
+ });
+ });
+ beforeEach(() => {
+ cy.clearSessionData().loginUserViaRequest();
+ cy.visit(urlList.products)
+ .get(PRODUCTS_LIST.createProductBtn)
+ .click();
+ });
+
+ it("should create product with variants", () => {
+ const randomName = `${startsWith}${faker.random.number()}`;
+ productUtils.createTypeProduct(randomName, attribute.id);
+ seo.slug = randomName;
+ const productData = {
+ generalInfo,
+ seo,
+ metadata,
+ productOrganization: { productType: randomName },
+ attribute
+ };
+ fillUpCommonFieldsForProductType(productData).then(
+ productOrgResp => (productData.productOrganization = productOrgResp)
+ );
+ cy.addAliasToGraphRequest("ProductDetails");
+ cy.get(BUTTON_SELECTORS.confirm).click();
+ cy.wait("@ProductDetails");
+ cy.get(PRODUCT_DETAILS.confirmationMsg).should("be.visible");
+ cy.get("@ProductDetails")
+ .its("response.body")
+ .then(resp => {
+ const productResp = resp.find(element => element.data.product).data
+ .product;
+ expectCorrectProductInformation(productResp, productData);
+ });
+ });
+ it("should create product without variants", () => {
+ const prices = { sellingPrice: 6, costPrice: 3 };
+ const randomName = `${startsWith}${faker.random.number()}`;
+ seo.slug = randomName;
+ productUtils.createTypeProduct(randomName, attribute.id, false);
+ const productData = {
+ generalInfo,
+ seo,
+ metadata,
+ productOrganization: { productType: randomName },
+ attribute
+ };
+ fillUpCommonFieldsForProductType(productData).then(
+ productOrgResp => (productData.productOrganization = productOrgResp)
+ );
+ selectChannelInDetailsPages();
+ fillUpPriceList(prices.sellingPrice);
+ fillUpPriceList(prices.costPrice, priceInputLists.costPrice);
+ cy.get(PRODUCT_DETAILS.skuInput).type(randomName);
+ cy.addAliasToGraphRequest("ProductDetails");
+ cy.get(BUTTON_SELECTORS.confirm).click();
+ cy.wait("@ProductDetails");
+ cy.get(PRODUCT_DETAILS.confirmationMsg).should("be.visible");
+ cy.get("@ProductDetails")
+ .its("response.body")
+ .then(resp => {
+ const productResp = resp.find(element => element.data.product).data
+ .product;
+ expectCorrectProductInformation(productResp, productData);
+ expectCorrectProductVariantInformation(
+ productResp.variants,
+ randomName,
+ prices
+ );
+ });
+ });
+});
diff --git a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js
index 2bfc475d7..d95eaffcd 100644
--- a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js
+++ b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js
@@ -1,10 +1,10 @@
import faker from "faker";
import { getProductDetails } from "../../../apiRequests/storeFront/ProductDetails";
-import { updateProductIsAvailableForPurchase } from "../../../steps/products/productSteps";
+import { updateProductIsAvailableForPurchase } from "../../../steps/catalog/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import { getDefaultChannel } from "../../../utils/channelsUtils";
-import * as productsUtils from "../../../utils/productsUtils";
+import * as productsUtils from "../../../utils/products/productsUtils";
import * as shippingUtils from "../../../utils/shippingUtils";
import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFrontProductUtils";
diff --git a/cypress/integration/products/menageProducts/publishedProducts.js b/cypress/integration/products/menageProducts/publishedProducts.js
index 0b7efe5b8..0037ce178 100644
--- a/cypress/integration/products/menageProducts/publishedProducts.js
+++ b/cypress/integration/products/menageProducts/publishedProducts.js
@@ -1,10 +1,10 @@
import faker from "faker";
import { getProductDetails } from "../../../apiRequests/storeFront/ProductDetails";
-import { updateProductPublish } from "../../../steps/products/productSteps";
+import { updateProductPublish } from "../../../steps/catalog/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import { getDefaultChannel } from "../../../utils/channelsUtils";
-import * as productsUtils from "../../../utils/productsUtils";
+import * as productsUtils from "../../../utils/products/productsUtils";
import { isProductVisible } from "../../../utils/storeFront/storeFrontProductUtils";
//
diff --git a/cypress/integration/products/menageProducts/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js
index aa65df728..4ba06b086 100644
--- a/cypress/integration/products/menageProducts/visibleInListingsProducts.js
+++ b/cypress/integration/products/menageProducts/visibleInListingsProducts.js
@@ -1,10 +1,10 @@
import faker from "faker";
import { searchInShop } from "../../../apiRequests/storeFront/Search";
-import { updateProductVisibleInListings } from "../../../steps/products/productSteps";
+import { updateProductVisibleInListings } from "../../../steps/catalog/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import { getDefaultChannel } from "../../../utils/channelsUtils";
-import * as productsUtils from "../../../utils/productsUtils";
+import * as productsUtils from "../../../utils/products/productsUtils";
import { isProductVisibleInSearchResult } from "../../../utils/storeFront/storeFrontProductUtils";
//
diff --git a/cypress/integration/products/products.js b/cypress/integration/products/products.js
deleted file mode 100644
index 7988137f8..000000000
--- a/cypress/integration/products/products.js
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
-import { urlList } from "../../url/urlList";
-
-describe("Products", () => {
- beforeEach(() => {
- cy.clearSessionData().loginUserViaRequest();
- });
-
- it("should add new visible product", () => {
- cy.visit(urlList.products)
- .get(PRODUCTS_SELECTORS.createProductBtn)
- .click()
- .get(PRODUCTS_SELECTORS.productNameInput)
- .click()
- .type("Visible test product")
- .get(PRODUCTS_SELECTORS.productTypeInput)
- .click()
- .get(PRODUCTS_SELECTORS.autocompleteDropdown) // trying to fill autocomplete before dropdown will cause error
- .get(PRODUCTS_SELECTORS.productTypeInput)
- .click()
- .type("Cushion")
- .get(PRODUCTS_SELECTORS.categoryItem)
- .should("have.length", 1)
- .get(PRODUCTS_SELECTORS.firstCategoryItem)
- .click()
- .get(PRODUCTS_SELECTORS.categoryInput)
- .click()
- .get(PRODUCTS_SELECTORS.categoryItem)
- .first()
- .click()
- .get(PRODUCTS_SELECTORS.channelAvailabilityItem)
- .first()
- .click()
- .get(PRODUCTS_SELECTORS.visibleRadioBtn)
- .first()
- .click()
- .get(PRODUCTS_SELECTORS.saveBtn)
- .click()
- .get(PRODUCTS_SELECTORS.confirmationMsg)
- .contains("Product created");
- });
-});
diff --git a/cypress/integration/products/productsVariants.js b/cypress/integration/products/productsVariants.js
index 8c40c7567..1cc91100b 100644
--- a/cypress/integration/products/productsVariants.js
+++ b/cypress/integration/products/productsVariants.js
@@ -8,13 +8,13 @@ import {
import {
createFirstVariant,
createVariant
-} from "../../steps/products/VariantsSteps";
+} from "../../steps/catalog/products/VariantsSteps";
import { urlList } from "../../url/urlList";
import {
deleteChannelsStartsWith,
getDefaultChannel
} from "../../utils/channelsUtils";
-import * as productUtils from "../../utils/productsUtils";
+import * as productUtils from "../../utils/products/productsUtils";
import * as shippingUtils from "../../utils/shippingUtils";
import { getProductVariants } from "../../utils/storeFront/storeFrontProductUtils";
diff --git a/cypress/steps/catalog/metadataSteps.js b/cypress/steps/catalog/metadataSteps.js
new file mode 100644
index 000000000..9f6f0ac46
--- /dev/null
+++ b/cypress/steps/catalog/metadataSteps.js
@@ -0,0 +1,18 @@
+import { METADATA_FORM } from "../../elements/shared/metadata/metadata-form";
+
+export const metadataForms = {
+ private: METADATA_FORM.privateMetadataForm,
+ public: METADATA_FORM.metadataForm
+};
+
+export function addMetadataField({ metadataForm, name, value }) {
+ cy.get(metadataForm)
+ .find(METADATA_FORM.addFieldButton)
+ .click()
+ .get(metadataForm)
+ .find(METADATA_FORM.nameInput)
+ .type(name)
+ .get(metadataForm)
+ .find(METADATA_FORM.valueField)
+ .type(value);
+}
diff --git a/cypress/steps/products/VariantsSteps.js b/cypress/steps/catalog/products/VariantsSteps.js
similarity index 70%
rename from cypress/steps/products/VariantsSteps.js
rename to cypress/steps/catalog/products/VariantsSteps.js
index 3b7d8924c..cab3ae3f3 100644
--- a/cypress/steps/products/VariantsSteps.js
+++ b/cypress/steps/catalog/products/VariantsSteps.js
@@ -1,18 +1,17 @@
-import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
-import { VARIANTS_SELECTORS } from "../../elements/catalog/variants-selectors";
+import { PRICE_LIST } from "../../../elements/catalog/products/price-list";
+import { PRODUCT_DETAILS } from "../../../elements/catalog/products/product-details";
+import { VARIANTS_SELECTORS } from "../../../elements/catalog/products/variants-selectors";
+import { fillUpPriceList } from "./priceList";
export function createFirstVariant({ sku, warehouseId, price, attribute }) {
- cy.get(PRODUCTS_SELECTORS.addVariantsButton).click();
+ cy.get(PRODUCT_DETAILS.addVariantsButton).click();
cy.get(VARIANTS_SELECTORS.valueContainer)
.contains(attribute)
.find(VARIANTS_SELECTORS.attributeCheckbox)
.click()
.get(VARIANTS_SELECTORS.nextButton)
- .click()
- .get(VARIANTS_SELECTORS.priceInput)
- .each($priceInput => {
- cy.wrap($priceInput).type(price);
- });
+ .click();
+ fillUpPriceList(price);
cy.get(`[name*='${warehouseId}']`)
.click()
.get(VARIANTS_SELECTORS.nextButton)
@@ -30,16 +29,16 @@ export function createVariant({
price,
costPrice = price
}) {
- cy.get(PRODUCTS_SELECTORS.addVariantsButton)
+ cy.get(PRODUCT_DETAILS.addVariantsButton)
.click()
.get(VARIANTS_SELECTORS.attributeSelector)
.click()
.get(VARIANTS_SELECTORS.attributeOption)
.contains(attributeName)
.click()
- .get(VARIANTS_SELECTORS.priceInput)
+ .get(PRICE_LIST.priceInput)
.type(price)
- .get(VARIANTS_SELECTORS.costPriceInput)
+ .get(PRICE_LIST.costPriceInput)
.type(costPrice)
.get(VARIANTS_SELECTORS.skuInputInAddVariant)
.type(sku)
diff --git a/cypress/steps/catalog/products/priceList.js b/cypress/steps/catalog/products/priceList.js
new file mode 100644
index 000000000..2d80f15cd
--- /dev/null
+++ b/cypress/steps/catalog/products/priceList.js
@@ -0,0 +1,14 @@
+import { PRICE_LIST } from "../../../elements/catalog/products/price-list";
+
+export const priceInputLists = {
+ sellingPrice: PRICE_LIST.priceInput,
+ costPrice: PRICE_LIST.costPriceInput
+};
+export function fillUpPriceList(
+ price = 1,
+ priceTypeInput = priceInputLists.sellingPrice
+) {
+ cy.get(priceTypeInput).each($priceInput => {
+ cy.wrap($priceInput).type(price);
+ });
+}
diff --git a/cypress/steps/catalog/products/productSteps.js b/cypress/steps/catalog/products/productSteps.js
new file mode 100644
index 000000000..21ab3f758
--- /dev/null
+++ b/cypress/steps/catalog/products/productSteps.js
@@ -0,0 +1,85 @@
+import { PRODUCT_DETAILS } from "../../../elements/catalog/products/product-details";
+import { AVAILABLE_CHANNELS_FORM } from "../../../elements/channels/available-channels-form";
+import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors";
+import { fillAutocompleteSelect } from "../../shared/autocompleteSelect";
+import { addMetadataField } from "../metadataSteps";
+import { editSeoSettings } from "../seoSteps";
+
+const valueTrue = AVAILABLE_CHANNELS_FORM.radioButtonsValueTrue;
+const valueFalse = AVAILABLE_CHANNELS_FORM.radioButtonsValueFalse;
+
+export function updateProductIsAvailableForPurchase(
+ productUrl,
+ isAvailableForPurchase
+) {
+ const isAvailableForPurchaseSelector = isAvailableForPurchase
+ ? valueTrue
+ : valueFalse;
+ const availableForPurchaseSelector = `${AVAILABLE_CHANNELS_FORM.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`;
+ updateProductMenageInChannel(productUrl, availableForPurchaseSelector);
+}
+export function updateProductPublish(productUrl, isPublished) {
+ const isPublishedSelector = isPublished ? valueTrue : valueFalse;
+ const publishedSelector = `${AVAILABLE_CHANNELS_FORM.publishedRadioButtons}${isPublishedSelector}`;
+ updateProductMenageInChannel(productUrl, publishedSelector);
+}
+export function updateProductVisibleInListings(productUrl) {
+ updateProductMenageInChannel(
+ productUrl,
+ AVAILABLE_CHANNELS_FORM.visibleInListingsButton
+ );
+}
+function updateProductMenageInChannel(productUrl, menageSelector) {
+ cy.visit(productUrl)
+ .get(AVAILABLE_CHANNELS_FORM.assignedChannels)
+ .click()
+ .get(menageSelector)
+ .click();
+ cy.addAliasToGraphRequest("ProductChannelListingUpdate");
+ cy.get(BUTTON_SELECTORS.confirm)
+ .click()
+ .wait("@ProductChannelListingUpdate");
+}
+export function fillUpCommonFieldsForProductType({
+ generalInfo,
+ seo,
+ metadata,
+ productOrganization
+}) {
+ fillUpProductGeneralInfo(generalInfo);
+ editSeoSettings(seo);
+ addMetadataField(metadata.public);
+ addMetadataField(metadata.private);
+ return fillUpProductOrganization(productOrganization).then(
+ productOrgResp => productOrgResp
+ );
+}
+export function fillUpProductGeneralInfo({ name, description, rating }) {
+ cy.get(PRODUCT_DETAILS.productNameInput)
+ .click()
+ .type(name)
+ .get(PRODUCT_DETAILS.descriptionInput)
+ .type(description)
+ .get(PRODUCT_DETAILS.ratingInput)
+ .type(rating);
+}
+export function fillUpProductOrganization({
+ productType,
+ category,
+ collection
+}) {
+ const organization = {};
+ return fillAutocompleteSelect(PRODUCT_DETAILS.productTypeInput, productType)
+ .then(selected => {
+ organization.productType = selected;
+ fillAutocompleteSelect(PRODUCT_DETAILS.categoryInput, category);
+ })
+ .then(selected => {
+ organization.category = selected;
+ fillAutocompleteSelect(PRODUCT_DETAILS.collectionInput, collection);
+ })
+ .then(selected => {
+ organization.collection = selected;
+ return organization;
+ });
+}
diff --git a/cypress/steps/catalog/seoSteps.js b/cypress/steps/catalog/seoSteps.js
new file mode 100644
index 000000000..f29cf114a
--- /dev/null
+++ b/cypress/steps/catalog/seoSteps.js
@@ -0,0 +1,11 @@
+import { SEO_FORM } from "../../elements/shared/seo/seo-form-selectors";
+export function editSeoSettings({ slug, title, description }) {
+ cy.get(SEO_FORM.editSeoSettings)
+ .click()
+ .get(SEO_FORM.slugInput)
+ .type(slug)
+ .get(SEO_FORM.titleInput)
+ .type(title)
+ .get(SEO_FORM.descriptionInput)
+ .type(description, { delay: 0 });
+}
diff --git a/cypress/steps/channelsSteps.js b/cypress/steps/channelsSteps.js
index e2a8fc2db..434c8bdf0 100644
--- a/cypress/steps/channelsSteps.js
+++ b/cypress/steps/channelsSteps.js
@@ -1,7 +1,8 @@
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
+import { AVAILABLE_CHANNELS_FORM } from "../elements/channels/available-channels-form";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
-import { MENAGE_CHANNEL_AVAILABILITY } from "../elements/channels/menage-channel-availability";
+import { SELECT_CHANNELS_TO_ASSIGN } from "../elements/channels/select-channels-to-assign";
import { HEADER_SELECTORS } from "../elements/header/header-selectors";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
@@ -39,14 +40,21 @@ export function selectChannelInHeader(channelName) {
.click();
}
export function selectChannelInDetailsPages(channelName) {
- cy.get(MENAGE_CHANNEL_AVAILABILITY.availableManageButton)
+ cy.get(AVAILABLE_CHANNELS_FORM.menageChannelsButton)
.click()
- .get(MENAGE_CHANNEL_AVAILABILITY.allChannelsInput)
- .click()
- .get(MENAGE_CHANNEL_AVAILABILITY.channelsAvailabilityForm)
- .contains(channelName)
- .click()
- .get(MENAGE_CHANNEL_AVAILABILITY.dialog)
+ .get(SELECT_CHANNELS_TO_ASSIGN.allChannelsCheckbox)
+ .click();
+ if (channelName) {
+ cy.get(SELECT_CHANNELS_TO_ASSIGN.listOfChannels)
+ .contains(channelName)
+ .click();
+ } else {
+ cy.get(SELECT_CHANNELS_TO_ASSIGN.channelRow)
+ .first()
+ .find(SELECT_CHANNELS_TO_ASSIGN.channelCheckbox)
+ .click();
+ }
+ cy.get(SELECT_CHANNELS_TO_ASSIGN.selectChannelsForm)
.find(BUTTON_SELECTORS.submit)
.click();
}
diff --git a/cypress/steps/collectionsSteps.js b/cypress/steps/collectionsSteps.js
index 5ebdff12c..4e2314369 100644
--- a/cypress/steps/collectionsSteps.js
+++ b/cypress/steps/collectionsSteps.js
@@ -1,31 +1,30 @@
import { COLLECTION_SELECTORS } from "../elements/catalog/collection-selectors";
import { ASSIGN_PRODUCTS_SELECTORS } from "../elements/catalog/products/assign-products-selectors";
-import { MENAGE_CHANNEL_AVAILABILITY_FORM } from "../elements/channels/menage-channel-availability-form";
+import { AVAILABLE_CHANNELS_FORM } from "../elements/channels/available-channels-form";
+import { SELECT_CHANNELS_TO_ASSIGN } from "../elements/channels/select-channels-to-assign";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
export function createCollection(collectionName, isPublished, channel) {
const publishedSelector = isPublished
- ? MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueTrue
- : MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueFalse;
+ ? AVAILABLE_CHANNELS_FORM.radioButtonsValueTrue
+ : AVAILABLE_CHANNELS_FORM.radioButtonsValueFalse;
cy.get(COLLECTION_SELECTORS.createCollectionButton)
.click()
.get(COLLECTION_SELECTORS.nameInput)
.type(collectionName)
- .get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsMenageButton)
+ .get(AVAILABLE_CHANNELS_FORM.menageChannelsButton)
.click()
- .get(MENAGE_CHANNEL_AVAILABILITY_FORM.allChannelsCheckbox)
+ .get(SELECT_CHANNELS_TO_ASSIGN.allChannelsCheckbox)
.click();
- cy.contains(MENAGE_CHANNEL_AVAILABILITY_FORM.channelRow, channel.name)
- .find(MENAGE_CHANNEL_AVAILABILITY_FORM.channelCheckbox)
+ cy.contains(SELECT_CHANNELS_TO_ASSIGN.channelRow, channel.name)
+ .find(SELECT_CHANNELS_TO_ASSIGN.channelCheckbox)
.click()
.get(BUTTON_SELECTORS.submit)
.click()
- .get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsAvailabilityItem)
+ .get(AVAILABLE_CHANNELS_FORM.availableChannel)
.click()
- .get(
- `${MENAGE_CHANNEL_AVAILABILITY_FORM.publishedCheckbox}${publishedSelector}`
- )
+ .get(`${AVAILABLE_CHANNELS_FORM.publishedRadioButtons}${publishedSelector}`)
.click();
cy.addAliasToGraphRequest("CreateCollection");
cy.get(COLLECTION_SELECTORS.saveButton).click();
diff --git a/cypress/steps/discounts/salesSteps.js b/cypress/steps/discounts/salesSteps.js
index 2d5a87791..4fad40e40 100644
--- a/cypress/steps/discounts/salesSteps.js
+++ b/cypress/steps/discounts/salesSteps.js
@@ -1,4 +1,4 @@
-import { ASSIGN_PRODUCTS_SELECTORS } from "../../elements/catalog/assign-products";
+import { ASSIGN_PRODUCTS_SELECTORS } from "../../elements/catalog/products/assign-products";
import { SALES_SELECTORS } from "../../elements/discounts/sales";
import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
import { formatDate } from "../../support/formatDate";
diff --git a/cypress/steps/products/productSteps.js b/cypress/steps/products/productSteps.js
deleted file mode 100644
index 28b1b7128..000000000
--- a/cypress/steps/products/productSteps.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
-
-const valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue;
-const valueFalse = PRODUCTS_SELECTORS.radioButtonsValueFalse;
-
-export function updateProductIsAvailableForPurchase(
- productUrl,
- isAvailableForPurchase
-) {
- const isAvailableForPurchaseSelector = isAvailableForPurchase
- ? valueTrue
- : valueFalse;
- const availableForPurchaseSelector = `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`;
- updateProductMenageInChannel(productUrl, availableForPurchaseSelector);
-}
-export function updateProductPublish(productUrl, isPublished) {
- const isPublishedSelector = isPublished ? valueTrue : valueFalse;
- const publishedSelector = `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}`;
- updateProductMenageInChannel(productUrl, publishedSelector);
-}
-export function updateProductVisibleInListings(productUrl) {
- updateProductMenageInChannel(
- productUrl,
- PRODUCTS_SELECTORS.visibleInListingsButton
- );
-}
-function updateProductMenageInChannel(productUrl, menageSelector) {
- cy.visit(productUrl)
- .get(PRODUCTS_SELECTORS.assignedChannels)
- .click()
- .get(menageSelector)
- .click();
- cy.addAliasToGraphRequest("ProductChannelListingUpdate");
- cy.get(PRODUCTS_SELECTORS.saveBtn)
- .click()
- .wait("@ProductChannelListingUpdate");
-}
diff --git a/cypress/steps/shared/autocompleteSelect.js b/cypress/steps/shared/autocompleteSelect.js
new file mode 100644
index 000000000..37385f8fa
--- /dev/null
+++ b/cypress/steps/shared/autocompleteSelect.js
@@ -0,0 +1,22 @@
+import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
+
+export function fillAutocompleteSelect(selectSelector, option) {
+ cy.get(selectSelector)
+ .click()
+ .get(BUTTON_SELECTORS.selectOption)
+ .should("be.visible");
+ if (option) {
+ cy.get(selectSelector).type(option);
+ cy.contains(BUTTON_SELECTORS.selectOption, option).click();
+ cy.wrap(option).as("option");
+ } else {
+ cy.get(BUTTON_SELECTORS.selectOption)
+ .first()
+ .invoke("text")
+ .as("option");
+ cy.get(BUTTON_SELECTORS.selectOption)
+ .first()
+ .click();
+ }
+ return cy.get("@option");
+}
diff --git a/cypress/utils/products/checkProductInfo.js b/cypress/utils/products/checkProductInfo.js
new file mode 100644
index 000000000..fdc598b13
--- /dev/null
+++ b/cypress/utils/products/checkProductInfo.js
@@ -0,0 +1,63 @@
+const { softExpect } = chai;
+export function expectCorrectProductInformation(productResp, productData) {
+ expectCorrectGeneralInformation(productResp, productData.generalInfo);
+ expectCorrectSeoInfo(productResp, productData.seo);
+ expectCorrectMetadataInfo(productResp.metadata, productData.metadata.public);
+ expectCorrectMetadataInfo(
+ productResp.privateMetadata,
+ productData.metadata.private
+ );
+ expectCorrectProductOrgInfo(productResp, productData.productOrganization);
+ expectCorrectAttribute(productResp.attributes, productData.attribute);
+}
+export function expectCorrectProductVariantInformation(
+ variantsResp,
+ variantName,
+ prices
+) {
+ softExpect(
+ expect(variantsResp).to.have.length(1),
+ softExpect(variantsResp[0].sku).to.be.eq(variantName),
+ softExpect(variantsResp[0].channelListings[0].costPrice.amount).to.be.eq(
+ prices.costPrice
+ ),
+ softExpect(variantsResp[0].channelListings[0].price.amount).to.be.eq(
+ prices.sellingPrice
+ )
+ );
+}
+function expectCorrectGeneralInformation(productResp, generalInfo) {
+ softExpect(productResp.name).to.be.eq(generalInfo.name);
+ softExpect(productResp.description).includes(generalInfo.description);
+ softExpect(productResp.rating).to.be.eq(generalInfo.rating);
+}
+function expectCorrectSeoInfo(productResp, seo) {
+ softExpect(productResp.slug).to.be.eq(seo.slug);
+ softExpect(productResp.seoTitle).to.be.eq(seo.title);
+ softExpect(productResp.seoDescription).to.be.eq(seo.description);
+}
+function expectCorrectMetadataInfo(metadataResp, expectedMetadata) {
+ softExpect(
+ expect(metadataResp).to.have.length(1),
+ softExpect(metadataResp[0].key).to.be.eq(expectedMetadata.name),
+ softExpect(metadataResp[0].value).to.be.eq(expectedMetadata.value)
+ );
+}
+function expectCorrectProductOrgInfo(productResp, productOrganization) {
+ softExpect(productResp.productType.name).to.be.eq(
+ productOrganization.productType
+ );
+ softExpect(productResp.category.name).to.be.eq(productOrganization.category);
+ softExpect(
+ expect(productResp.collections).to.have.length(1),
+ softExpect(productResp.collections[0].name).to.be.eq(
+ productOrganization.collection
+ )
+ );
+}
+function expectCorrectAttribute(attributes, attribute) {
+ softExpect(
+ expect(attributes).to.have.length(1),
+ softExpect(attributes[0].attribute.name).to.be.eq(attribute.name)
+ );
+}
diff --git a/cypress/utils/productsUtils.js b/cypress/utils/products/productsUtils.js
similarity index 90%
rename from cypress/utils/productsUtils.js
rename to cypress/utils/products/productsUtils.js
index 8bf2e86e2..0864a89b6 100644
--- a/cypress/utils/productsUtils.js
+++ b/cypress/utils/products/productsUtils.js
@@ -1,6 +1,6 @@
-import * as attributeRequest from "../apiRequests/Attribute";
-import * as categoryRequest from "../apiRequests/Category";
-import * as productRequest from "../apiRequests/Product";
+import * as attributeRequest from "../../apiRequests/Attribute";
+import * as categoryRequest from "../../apiRequests/Category";
+import * as productRequest from "../../apiRequests/Product";
export function createProductInChannel({
name,
@@ -70,9 +70,9 @@ export function createAttribute(name, attributeValues) {
.createAttribute(name, attributeValues)
.its("body.data.attributeCreate.attribute");
}
-export function createTypeProduct(name, attributeId) {
+export function createTypeProduct(name, attributeId, hasVariants) {
return productRequest
- .createTypeProduct(name, attributeId)
+ .createTypeProduct(name, attributeId, hasVariants)
.its("body.data.productTypeCreate.productType");
}
export function createCategory(name) {
diff --git a/src/components/SeoForm/SeoForm.tsx b/src/components/SeoForm/SeoForm.tsx
index e7561dba0..90b8b4dff 100644
--- a/src/components/SeoForm/SeoForm.tsx
+++ b/src/components/SeoForm/SeoForm.tsx
@@ -163,7 +163,12 @@ const SeoForm: React.FC = props => {
defaultMessage: "Search Engine Preview"
})}
toolbar={
-