From cb528a7744b807cbf6aaa4787de7fb305a78b444 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 4 Feb 2021 12:15:27 +0100 Subject: [PATCH 01/16] tests for publish products --- cypress/apiRequests/Attribute.js | 49 +++++ cypress/apiRequests/Category.js | 43 ++++ cypress/apiRequests/Channels.js | 53 +++++ cypress/apiRequests/Product.js | 192 ++++++++++++++++++ cypress/apiRequests/ShopInfo.js | 13 ++ cypress/elements/catalog/product-selectors.js | 14 +- .../frontend-elements/search-selectors.js | 5 + cypress/fixtures/addresses.json | 5 + cypress/integration/products.js | 47 ----- cypress/integration/products/products.js | 109 ++++++++++ cypress/integration/products/publish.js | 141 +++++++++++++ cypress/steps/frontendSteps/searchSteps.js | 11 + cypress/support/index.js | 35 ++++ cypress/support/user/index.js | 20 ++ cypress/url/url-list.js | 6 + cypress/utils/channelsUtils.js | 38 ++++ cypress/utils/productsUtils.js | 153 ++++++++++++++ 17 files changed, 886 insertions(+), 48 deletions(-) create mode 100644 cypress/apiRequests/Attribute.js create mode 100644 cypress/apiRequests/Category.js create mode 100644 cypress/apiRequests/Channels.js create mode 100644 cypress/apiRequests/Product.js create mode 100644 cypress/apiRequests/ShopInfo.js create mode 100644 cypress/elements/frontend-elements/search-selectors.js create mode 100644 cypress/fixtures/addresses.json delete mode 100644 cypress/integration/products.js create mode 100644 cypress/integration/products/products.js create mode 100644 cypress/integration/products/publish.js create mode 100644 cypress/steps/frontendSteps/searchSteps.js create mode 100644 cypress/url/url-list.js create mode 100644 cypress/utils/channelsUtils.js create mode 100644 cypress/utils/productsUtils.js diff --git a/cypress/apiRequests/Attribute.js b/cypress/apiRequests/Attribute.js new file mode 100644 index 000000000..4ba3467f2 --- /dev/null +++ b/cypress/apiRequests/Attribute.js @@ -0,0 +1,49 @@ +class Attribute { + createAttribute(name) { + const mutation = `mutation{ + attributeCreate(input:{ + name:"${name}" + valueRequired:false + type:PRODUCT_TYPE + }){ + attribute{ + id + } + attributeErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + getAttributes(first, search) { + const mutation = `query{ + attributes(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + deleteAttribute(attributeId) { + const mutation = `mutation{ + attributeDelete(id:"${attributeId}"){ + attributeErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Attribute; diff --git a/cypress/apiRequests/Category.js b/cypress/apiRequests/Category.js new file mode 100644 index 000000000..0d76c7b3b --- /dev/null +++ b/cypress/apiRequests/Category.js @@ -0,0 +1,43 @@ +class Category { + createCategory(name, slug = name) { + const mutation = `mutation{ + categoryCreate(input:{name:"${name}", slug: "${slug}"}){ + productErrors{ + field + message + } + category{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + getCategories(first, search) { + const mutation = `query{ + categories(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + deleteCategory(categoryId) { + const mutation = `mutation{ + categoryDelete(id:"${categoryId}"){ + productErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Category; diff --git a/cypress/apiRequests/Channels.js b/cypress/apiRequests/Channels.js new file mode 100644 index 000000000..2daa39a9e --- /dev/null +++ b/cypress/apiRequests/Channels.js @@ -0,0 +1,53 @@ +class Channels { + createChannel(isActive, name, slug, currencyCode) { + const createChannelMutation = `mutation{ + channelCreate(input: { + isActive: ${isActive} + name: "${name}" + slug: "${slug}" + currencyCode: "${currencyCode}" + }){ + channel{ + name + slug + } + channelErrors{ + code + message + } + } + }`; + return cy.sendRequestWithQuery(createChannelMutation); + } + + getChannels() { + const getChannelsInfoQuery = `query{ + channels{ + name + id + isActive + slug + currencyCode + } + } + `; + return cy.sendRequestWithQuery(getChannelsInfoQuery); + } + + deleteChannel(channelId, targetChennelId) { + const deleteChannelMutation = `mutation{ + channelDelete(id: "${channelId}", input:{ + targetChannel: "${targetChennelId}" + }){ + channel{ + name + } + channelErrors{ + message + } + } + }`; + return cy.sendRequestWithQuery(deleteChannelMutation); + } +} +export default Channels; diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js new file mode 100644 index 000000000..e7e0c11d7 --- /dev/null +++ b/cypress/apiRequests/Product.js @@ -0,0 +1,192 @@ +class Product { + getFirstProducts(first, search) { + let filter = ""; + if (search) { + filter = `, filter:{ + search:"${search}" + }`; + } + const query = `query{ + products(first:${first}${filter}){ + edges{ + node{ + id + name + variants{ + id + } + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } + + updateChannelInProduct( + productId, + channelId, + isPublished, + isAvailableForPurchase + ) { + const mutation = `mutation{ + productChannelListingUpdate(id:"${productId}", + input:{ + addChannels:{ + channelId:"${channelId}" + isPublished:${isPublished} + isAvailableForPurchase:${isAvailableForPurchase} + visibleInListings:true + } + }){ + product{ + id + name + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + updateChannelPriceInVariant(variantId, channelId) { + const mutation = `mutation{ + productVariantChannelListingUpdate(id: "${variantId}", input:{ + channelId: "${channelId}" + price: 10 + costPrice: 10 + }){ + productChannelListingErrors{ + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + createProduct(attributeId, name, productType, category) { + const mutation = `mutation{ + productCreate(input:{ + attributes:[{ + id:"${attributeId}" + }] + name:"${name}" + productType:"${productType}" + category:"${category}" + }){ + product{ + id + } + productErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + createVariant( + productId, + sku, + warehouseId, + quantity, + channelId, + price = 1, + costPrice = 1 + ) { + let channelListings = ""; + let stocks = ""; + if (channelId) { + channelListings = `channelListings:{ + channelId:"${channelId}" + price:"${price}" + costPrice:"${costPrice}" + }`; + } + if (warehouseId) { + stocks = `stocks:{ + warehouse:"${warehouseId}" + quantity:${quantity} + }`; + } + const mutation = `mutation{ + productVariantBulkCreate(product:"${productId}", variants:{ + attributes:[] + sku:"${sku}" + ${channelListings} + ${stocks} + }){ + productVariants{ + id + name + } + bulkProductErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + createTypeProduct(name, attributeId, slug = name) { + const mutation = `mutation{ + productTypeCreate(input:{ + name:"${name}" + slug: "${slug}" + isShippingRequired:true + productAttributes:"${attributeId}" + }){ + productErrors{ + field + message + } + productType{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + deleteProduct(productId) { + const mutation = `mutation{ + productDelete(id:"${productId}"){ + productErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + getProductTypes(first, search) { + const query = `query{ + productTypes(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(query); + } + + deleteProductType(productTypeId) { + const mutation = `mutation{ + productTypeDelete(id:"${productTypeId}"){ + productErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} + +export default Product; diff --git a/cypress/apiRequests/ShopInfo.js b/cypress/apiRequests/ShopInfo.js new file mode 100644 index 000000000..17d5f8a9e --- /dev/null +++ b/cypress/apiRequests/ShopInfo.js @@ -0,0 +1,13 @@ +class ShopInfo { + getShopInfo() { + const query = `query{ + shop{ + domain{ + url + } + } + }`; + return cy.sendRequestWithQuery(query); + } +} +export default ShopInfo; diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index de68973b0..b61b38a11 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -1,5 +1,6 @@ /* eslint-disable sort-keys */ 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']", @@ -11,5 +12,16 @@ export const PRODUCTS_SELECTORS = { visibleRadioBtn: "[name='isPublished']", saveBtn: "[data-test='button-bar-confirm']", confirmationMsg: "[data-test='notification-success']", - channelAvailabilityItem: "[data-test='channel-availability-item']" + 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]" }; diff --git a/cypress/elements/frontend-elements/search-selectors.js b/cypress/elements/frontend-elements/search-selectors.js new file mode 100644 index 000000000..712a91ce9 --- /dev/null +++ b/cypress/elements/frontend-elements/search-selectors.js @@ -0,0 +1,5 @@ +export const SEARCH_SELECTORS = { + searchButton: "[data-test='menuSearchOverlayLink']", + searchInputField: "[placeholder='search']", + productItem: ".search__products__item" +}; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json new file mode 100644 index 000000000..5a346904f --- /dev/null +++ b/cypress/fixtures/addresses.json @@ -0,0 +1,5 @@ +{ + "plAddress":{ + "currency": "PLN" + } +} \ No newline at end of file diff --git a/cypress/integration/products.js b/cypress/integration/products.js deleted file mode 100644 index 1d8e38ee8..000000000 --- a/cypress/integration/products.js +++ /dev/null @@ -1,47 +0,0 @@ -import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors"; -import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; - -// -describe("Products", () => { - beforeEach(() => { - cy.clearSessionData().loginUserViaRequest(); - }); - - it("should add new visible product", () => { - cy.visit("/") - .get(LEFT_MENU_SELECTORS.catalog) - .click() - .get(PRODUCTS_SELECTORS.products) - .click() - .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/products.js b/cypress/integration/products/products.js new file mode 100644 index 000000000..9cb780cda --- /dev/null +++ b/cypress/integration/products/products.js @@ -0,0 +1,109 @@ +import faker from "faker"; + +import Channels from "../../apiRequests/Channels"; +import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors"; +import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import ProductsUtils from "../../utils/productsUtils"; + +// +describe("Products", () => { + const channels = new Channels(); + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + + const startsWith = "Cy-"; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProducts(startsWith); + channelsUtils.deleteChannels(startsWith); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + xit("should add new visible product", () => { + cy.visit("/") + .get(LEFT_MENU_SELECTORS.catalog) + .click() + .get(PRODUCTS_SELECTORS.products) + .click() + .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"); + }); + it("should display correct availibility for product in channel", () => { + const name = `${startsWith}${faker.random.number()}`; + cy.fixture("addresses").then(json => { + channels.createChannel(true, name, name, json.plAddress.currency); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + const productTypeId = productsUtils.getProductTypeId(); + const attributeId = productsUtils.getAttributeId(); + const categoryId = productsUtils.getCategoryId(); + productsUtils.createProduct( + name, + attributeId, + productTypeId, + categoryId + ); + cy.visit(URL_LIST.products) + .get(PRODUCTS_SELECTORS.searchProducts) + .type(name) + .get(PRODUCTS_SELECTORS.productsList) + .contains(name) + .click() + .get(PRODUCTS_SELECTORS.availableManageButton) + .click() + .get(PRODUCTS_SELECTORS.channelsAvailabilityForm) + .contains(name) + .click() + .get(BUTTON_SELECTORS.submit) + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .get(PRODUCTS_SELECTORS.goBackButton) + .click() + .get(PRODUCTS_SELECTORS.searchProducts) + .type(name) + .get(PRODUCTS_SELECTORS.productsList) + .contains(name) + .parentsUntil("tbody") + .find(PRODUCTS_SELECTORS.channelAvailabilityColumn) + .click() + .get(PRODUCTS_SELECTORS.channelAvailabilityList) + .contains(name); + }); + }); + }); +}); diff --git a/cypress/integration/products/publish.js b/cypress/integration/products/publish.js new file mode 100644 index 000000000..ebd7588fc --- /dev/null +++ b/cypress/integration/products/publish.js @@ -0,0 +1,141 @@ +import faker from "faker"; + +import ShopInfo from "../../apiRequests/ShopInfo"; +import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; +import SearchSteps from "../../steps/frontendSteps/searchSteps"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import ProductsUtils from "../../utils/productsUtils"; + +// +describe("Products", () => { + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const searchSteps = new SearchSteps(); + + const shopInfo = new ShopInfo(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productTypeId; + let attributeId; + let categoryId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productTypeId = productsUtils.getProductTypeId(); + attributeId = productsUtils.getAttributeId(); + categoryId = productsUtils.getCategoryId(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + shopInfo + .getShopInfo() + .its("body.data.shop.domain.url") + .as("shopUrl"); + }); + xit("should display on frontend only published products", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + false, + false + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Opublikowany") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(name); + cy.get(SEARCH_SELECTORS.productItem).contains(name); + }); + }); + }); + }); + it("shouldn't display not published product for unlogged user", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Nie opublikowano") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(name); + cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); + }); + }); + }); + }); + xit("should display not published product for staff member", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Nie opublikowano") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl) + .loginInShop() + .then(() => { + searchSteps.searchFor(name); + cy.get(SEARCH_SELECTORS.productItem).contains(name); + }); + }); + }); + }); + }); +}); diff --git a/cypress/steps/frontendSteps/searchSteps.js b/cypress/steps/frontendSteps/searchSteps.js new file mode 100644 index 000000000..9c42e54fa --- /dev/null +++ b/cypress/steps/frontendSteps/searchSteps.js @@ -0,0 +1,11 @@ +import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; + +class SearchSteps { + searchFor(query) { + cy.get(SEARCH_SELECTORS.searchButton) + .click() + .get(SEARCH_SELECTORS.searchInputField) + .type(query); + } +} +export default SearchSteps; diff --git a/cypress/support/index.js b/cypress/support/index.js index 531b8aab9..c09fd0ded 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -17,3 +17,38 @@ Cypress.Commands.add("clearSessionData", () => { } }); }); + +Cypress.Commands.add("sendRequestWithQuery", query => + cy.request({ + method: "POST", + body: { + method: "POST", + url: Cypress.env("API_URI"), + query + }, + headers: { + Authorization: `JWT ${window.sessionStorage.getItem("auth")}` + }, + url: Cypress.env("API_URI") + }) +); + +Cypress.Commands.add("waitForGraph", operationName => { + const GRAPH_URL = "/graphql"; + cy.intercept("POST", GRAPH_URL, req => { + req.statusCode = 200; + const requestBody = req.body; + if (Array.isArray(requestBody)) { + requestBody.forEach(element => { + if (element.operationName === operationName) { + req.alias = operationName; + } + }); + } else { + if (requestBody.operationName === operationName) { + req.alias = operationName; + } + } + }); + cy.wait(`@${operationName}`); +}); diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 9edf99e27..1cdf56cf9 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -46,3 +46,23 @@ Cypress.Commands.add("loginUserViaRequest", () => { window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); }); }); + +Cypress.Commands.add("loginInShop", () => { + cy.request({ + method: "POST", + url: Cypress.env("API_URI"), + body: [ + { + operationName: "TokenAuth", + variables: { + email: Cypress.env("USER_NAME"), + password: Cypress.env("USER_PASSWORD") + }, + query: + "mutation TokenAuth($email: String!, $password: String!) {\n tokenCreate(email: $email, password: $password) {\n token\n errors: accountErrors {\n code\n field\n message\n __typename\n }\n user {\n id\n __typename\n }\n __typename\n }\n}\n" + } + ] + }).then(resp => { + window.localStorage.setItem("token", resp.body[0].data.tokenCreate.token); + }); +}); diff --git a/cypress/url/url-list.js b/cypress/url/url-list.js new file mode 100644 index 000000000..aa047845c --- /dev/null +++ b/cypress/url/url-list.js @@ -0,0 +1,6 @@ +export const URL_LIST = { + dashbord: "/", + channels: "/channels/", + products: "/products/", + orders: "/orders/" +}; diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js new file mode 100644 index 000000000..a536fb4f3 --- /dev/null +++ b/cypress/utils/channelsUtils.js @@ -0,0 +1,38 @@ +import Channels from "../apiRequests/Channels"; + +class ChannelsUtils { + channels = new Channels(); + + deleteChannels(nameStartsWith) { + this.channels.getChannels().then(resp => { + const channelsArray = new Set(resp.body.data.channels); + if (channelsArray) { + channelsArray.forEach(element => { + if (element.name.startsWith(nameStartsWith)) { + const targetChannels = Array.from(channelsArray).filter(function( + channelElement + ) { + return ( + element.currencyCode === channelElement.currencyCode && + element.id !== channelElement.id + ); + }); + if (targetChannels[0]) { + this.channels.deleteChannel(element.id, targetChannels[0].id); + channelsArray.delete(element); + } + } + }); + } + }); + } + getDefaultChannel() { + return this.channels.getChannels().then(resp => { + const channelsArray = resp.body.data.channels; + return channelsArray.find(function(channelElement) { + return channelElement.slug === "default-channel"; + }); + }); + } +} +export default ChannelsUtils; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js new file mode 100644 index 000000000..eda29a95b --- /dev/null +++ b/cypress/utils/productsUtils.js @@ -0,0 +1,153 @@ +import Attribute from "../apiRequests/Attribute"; +import Category from "../apiRequests/Category"; +import Product from "../apiRequests/Product"; + +class ProductsUtils { + createdVariantId; + createdProductId; + productTypeId; + attributeId; + categoryId; + + updateChannelInProduct(productsList, channelId) { + const product = new Product(); + productsList.forEach(productElement => { + product.updateChannelInProduct(productElement.node.id, channelId); + const variants = productElement.node.variants; + variants.forEach(variant => { + product.updateChannelPriceInVariant(variant.id, channelId); + }); + }); + } + createProduct(name, attributeId, productTypeId, categoryId) { + const product = new Product(); + return product + .createProduct(attributeId, name, productTypeId, categoryId) + .then(createProductResp => { + this.createdProductId = + createProductResp.body.data.productCreate.product.id; + return product + .createVariant(this.createdProductId, name) + .then(createVariantResp => { + this.createdVariantId = + createVariantResp.body.data.productVariantBulkCreate.productVariants; + }); + }); + } + createProductInChannel( + name, + productTypeId, + attributeId, + categoryId, + channelId, + isPublished, + isAvailableForPurchase, + warehouseId, + quantityInWarehouse, + price + ) { + const product = new Product(); + return product + .createProduct(attributeId, name, productTypeId, categoryId) + .then(createProductResp => { + this.createdProductId = + createProductResp.body.data.productCreate.product.id; + return product + .updateChannelInProduct( + this.createdProductId, + channelId, + isPublished, + isAvailableForPurchase + ) + .then(() => + product + .createVariant( + this.createdProductId, + name, + warehouseId, + quantityInWarehouse, + channelId, + price + ) + .then(createVariantResp => { + this.createdVariantId = + createVariantResp.body.data.productVariantBulkCreate.productVariants; + }) + ); + }); + } + + createTypeAttributeAndCategoryForProduct(name) { + const attribute = new Attribute(); + const category = new Category(); + const product = new Product(); + return attribute.createAttribute(name).then(createAttributeResp => { + this.attributeId = + createAttributeResp.body.data.attributeCreate.attribute.id; + return product + .createTypeProduct(name, this.attributeId) + .then(createTypeProductResp => { + this.productTypeId = + createTypeProductResp.body.data.productTypeCreate.productType.id; + return category.createCategory(name).then(categoryResp => { + this.categoryId = categoryResp.body.data.categoryCreate.category.id; + }); + }); + }); + } + + getCreatedVariants() { + return this.createdVariantId; + } + getProductTypeId() { + return this.productTypeId; + } + getAttributeId() { + return this.attributeId; + } + getCategoryId() { + return this.categoryId; + } + getCreatedProductId() { + return this.createdProductId; + } + + deleteProducts(startsWith) { + const product = new Product(); + const attribute = new Attribute(); + const category = new Category(); + product.getProductTypes(100, startsWith).then(resp => { + const productTypes = resp.body.data.productTypes.edges; + productTypes.forEach(productType => { + if (productType.node.name.includes(startsWith)) { + product.deleteProductType(productType.node.id); + } + }); + }); + attribute.getAttributes(100, startsWith).then(resp => { + const attributes = resp.body.data.attributes.edges; + attributes.forEach(attributeElement => { + if (attributeElement.node.name.includes(startsWith)) { + attribute.deleteAttribute(attributeElement.node.id); + } + }); + }); + category.getCategories(100, startsWith).then(resp => { + const categories = resp.body.data.categories.edges; + categories.forEach(categoryElement => { + if (categoryElement.node.name.includes(startsWith)) { + category.deleteCategory(categoryElement.node.id); + } + }); + }); + product.getFirstProducts(100, startsWith).then(getProductResp => { + const products = getProductResp.body.data.products.edges; + products.forEach(productElement => { + if (productElement.node.name.includes(startsWith)) { + product.deleteProducts(productElement.node.id); + } + }); + }); + } +} +export default ProductsUtils; From 584e66b9f9a70c6c2a92ed090592c92a49fc6da0 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 4 Feb 2021 13:26:04 +0100 Subject: [PATCH 02/16] one test for available for purchase --- cypress/apiRequests/ShippingMethod.js | 84 +++++++++++++++ cypress/apiRequests/Warehouse.js | 55 ++++++++++ .../frontend-elements/cart-selectors.js | 3 + .../product-details-selectors.js | 3 + .../products/availableForPurchase.js | 101 ++++++++++++++++++ cypress/utils/channelsUtils.js | 13 ++- cypress/utils/shippingUtils.js | 71 ++++++++++++ 7 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 cypress/apiRequests/ShippingMethod.js create mode 100644 cypress/apiRequests/Warehouse.js create mode 100644 cypress/elements/frontend-elements/cart-selectors.js create mode 100644 cypress/elements/frontend-elements/product-details-selectors.js create mode 100644 cypress/integration/products/availableForPurchase.js create mode 100644 cypress/utils/shippingUtils.js diff --git a/cypress/apiRequests/ShippingMethod.js b/cypress/apiRequests/ShippingMethod.js new file mode 100644 index 000000000..25c067a43 --- /dev/null +++ b/cypress/apiRequests/ShippingMethod.js @@ -0,0 +1,84 @@ +class ShippingMethod { + createShippingRate(name, shippingZone) { + const mutation = ` + mutation{ + shippingPriceCreate(input:{ + name: "${name}" + shippingZone: "${shippingZone}" + type: PRICE + }){ + shippingMethod{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + createShippingZone(name, country) { + const mutation = ` + mutation{ + shippingZoneCreate(input:{ + name: "${name}" + countries: "${country}" + }){ + shippingZone{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + addChannelToShippingMethod(shippingRateId, channelId, price) { + const mutation = ` + mutation{ + shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ + addChannels: { + channelId:"${channelId}" + price: ${price} + } + }){ + shippingMethod{ + id + } + shippingErrors{ + code + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + deleteShippingZone(shippingZoneId) { + const mutation = `mutation{ + shippingZoneDelete(id:"${shippingZoneId}"){ + shippingErrors{ + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + getShippingZones() { + const query = `query{ + shippingZones(first:100){ + edges{ + node{ + name + id + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } +} +export default ShippingMethod; diff --git a/cypress/apiRequests/Warehouse.js b/cypress/apiRequests/Warehouse.js new file mode 100644 index 000000000..80df7f6f6 --- /dev/null +++ b/cypress/apiRequests/Warehouse.js @@ -0,0 +1,55 @@ +class Warehouse { + createWarehouse(name, shippingZone, address, slug = name) { + const mutation = `mutation{ + createWarehouse(input:{ + name:"${name}" + slug:"${slug}" + shippingZones:"${shippingZone}" + address:{ + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + }){ + warehouseErrors{ + field + message + } + warehouse{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + getWarehouses(first, search) { + const query = `query{ + warehouses(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(query); + } + deleteWarehouse(warehouseId) { + const mutation = `mutation{ + deleteWarehouse(id:"${warehouseId}"){ + warehouseErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Warehouse; diff --git a/cypress/elements/frontend-elements/cart-selectors.js b/cypress/elements/frontend-elements/cart-selectors.js new file mode 100644 index 000000000..0209eba5c --- /dev/null +++ b/cypress/elements/frontend-elements/cart-selectors.js @@ -0,0 +1,3 @@ +export const CART_SELECTORS = { + productInCart: "[data-test='cartRow']" +}; diff --git a/cypress/elements/frontend-elements/product-details-selectors.js b/cypress/elements/frontend-elements/product-details-selectors.js new file mode 100644 index 000000000..9cda9b9f6 --- /dev/null +++ b/cypress/elements/frontend-elements/product-details-selectors.js @@ -0,0 +1,3 @@ +export const PRODUCTS_DETAILS_SELECTORS = { + addToCartButton: "[data-test='addProductToCartButton']" +}; diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js new file mode 100644 index 000000000..1aa2c3685 --- /dev/null +++ b/cypress/integration/products/availableForPurchase.js @@ -0,0 +1,101 @@ +import faker from "faker"; + +import ShopInfo from "../../apiRequests/ShopInfo"; +import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import { CART_SELECTORS } from "../../elements/frontend-elements/cart-selectors"; +import { PRODUCTS_DETAILS_SELECTORS } from "../../elements/frontend-elements/product-details-selectors"; +import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; +import SearchSteps from "../../steps/frontendSteps/searchSteps"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import ProductsUtils from "../../utils/productsUtils"; +import ShippingUtils from "../../utils/shippingUtils"; + +// +describe("Products", () => { + const shippingUtils = new ShippingUtils(); + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const searchSteps = new SearchSteps(); + + const shopInfo = new ShopInfo(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productTypeId; + let attributeId; + let categoryId; + let defaultChannel; + let warehouseId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + shippingUtils.deleteShipping(startsWith); + productsUtils.deleteProducts(startsWith); + + channelsUtils.findDefaultChannel().then(() => { + defaultChannel = channelsUtils.getDefaultChannel(); + cy.fixture("addresses").then(json => { + shippingUtils + .createShipping(defaultChannel, name, json.plAddress, 10) + .then(() => { + warehouseId = shippingUtils.getWarehouseId(); + }); + }); + }); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productTypeId = productsUtils.getProductTypeId(); + attributeId = productsUtils.getAttributeId(); + categoryId = productsUtils.getCategoryId(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + shopInfo + .getShopInfo() + .its("body.data.shop.domain.url") + .as("shopUrl"); + }); + + it("should be possible to add to cart available for purchase product", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel, + true, + false, + warehouseId, + 10, + 10 + ) + .then(() => { + const productUrl = `${URL_LIST.products}${productsUtils.getCreatedProductId}`; + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Dostępne do zakupu") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(name); + cy.get(SEARCH_SELECTORS.productItem) + .contains(productName) + .click() + .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) + .click() + .get(CART_SELECTORS.productInCart) + .contains(productName); + }); + }); + }); +}); diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js index a536fb4f3..7e1e58973 100644 --- a/cypress/utils/channelsUtils.js +++ b/cypress/utils/channelsUtils.js @@ -3,6 +3,8 @@ import Channels from "../apiRequests/Channels"; class ChannelsUtils { channels = new Channels(); + defaultChannel; + deleteChannels(nameStartsWith) { this.channels.getChannels().then(resp => { const channelsArray = new Set(resp.body.data.channels); @@ -26,13 +28,18 @@ class ChannelsUtils { } }); } - getDefaultChannel() { + findDefaultChannel() { return this.channels.getChannels().then(resp => { const channelsArray = resp.body.data.channels; - return channelsArray.find(function(channelElement) { + return (this.defaultChannel = channelsArray.find(function( + channelElement + ) { return channelElement.slug === "default-channel"; - }); + })); }); } + getDefaultChannel() { + return this.defaultChannel; + } } export default ChannelsUtils; diff --git a/cypress/utils/shippingUtils.js b/cypress/utils/shippingUtils.js new file mode 100644 index 000000000..5806ac82d --- /dev/null +++ b/cypress/utils/shippingUtils.js @@ -0,0 +1,71 @@ +import ShippingMethod from "../apiRequests/ShippingMethod"; +import Warehouse from "../apiRequests/Warehouse"; +class ShippingUtils { + shippingMethodId; + shippingZoneId; + warehouseId; + + createShipping(channelId, name, address, price) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + return shippingMethod + .createShippingZone(name, address.country) + .then(shippingZoneResp => { + this.shippingZoneId = + shippingZoneResp.body.data.shippingZoneCreate.shippingZone.id; + return warehouse + .createWarehouse(name, this.shippingZoneId, address) + .then(createWarehouseResp => { + this.warehouseId = + createWarehouseResp.body.data.createWarehouse.warehouse.id; + return shippingMethod + .createShippingRate(name, this.shippingZoneId) + .then(rateResp => { + this.shippingMethodId = + rateResp.body.data.shippingPriceCreate.shippingMethod.id; + return shippingMethod.addChannelToShippingMethod( + this.shippingMethodId, + channelId, + price + ); + }); + }); + }); + } + + getShippingMethodId() { + return this.shippingMethodId; + } + + getShippingZoneId() { + return this.shippingZoneId; + } + + getWarehouseId() { + return this.warehouseId; + } + + deleteShipping(startsWith) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + shippingMethod.getShippingZones().then(resp => { + if (resp.body.data.shippingZones) { + const shippingZone = resp.body.data.shippingZones.edges; + shippingZone.forEach(element => { + if (element.node.name.includes(startsWith)) { + shippingMethod.deleteShippingZone(element.node.id); + } + }); + } + }); + warehouse.getWarehouses(100, startsWith).then(resp => { + const warehouses = resp.body.data.warehouses.edges; + warehouses.forEach(warehouseElement => { + if (warehouseElement.node.name.includes(startsWith)) { + warehouse.deleteWarehouse(warehouseElement.node.id); + } + }); + }); + } +} +export default ShippingUtils; From 2a9e4204200ba9b5e651893f9fbf23ee6b636429 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Mon, 8 Feb 2021 10:23:12 +0100 Subject: [PATCH 03/16] all tests for products --- cypress/apiRequests/Product.js | 5 +- cypress/elements/catalog/product-selectors.js | 3 +- cypress/fixtures/addresses.json | 10 +- .../products/availableForPurchase.js | 54 ++++++- cypress/integration/products/publish.js | 23 +-- .../integration/products/showInListings.js | 144 ++++++++++++++++++ cypress/utils/channelsUtils.js | 7 +- cypress/utils/productsUtils.js | 4 +- 8 files changed, 224 insertions(+), 26 deletions(-) create mode 100644 cypress/integration/products/showInListings.js diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index e7e0c11d7..035c6f083 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -27,7 +27,8 @@ class Product { productId, channelId, isPublished, - isAvailableForPurchase + isAvailableForPurchase, + visibleInListings ) { const mutation = `mutation{ productChannelListingUpdate(id:"${productId}", @@ -36,7 +37,7 @@ class Product { channelId:"${channelId}" isPublished:${isPublished} isAvailableForPurchase:${isAvailableForPurchase} - visibleInListings:true + visibleInListings:${visibleInListings} } }){ product{ diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index b61b38a11..a4d1b6437 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -23,5 +23,6 @@ export const PRODUCTS_SELECTORS = { channelAvailabilityList: "ul[role='menu']", goBackButton: "[data-test-id='app-header-back-button']", assignedChannels: "[data-test='channel-availability-item']", - publishedRadioButton: "[role=radiogroup]" + publishedRadioButton: "[role=radiogroup]", + visibleInListingsButton: "[class*='MuiFormControlLabel']" }; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json index 5a346904f..08fad0466 100644 --- a/cypress/fixtures/addresses.json +++ b/cypress/fixtures/addresses.json @@ -1,5 +1,13 @@ { - "plAddress":{ + "plAddress": { + "companyName": "Test3", + "streetAddress1": "Smolna", + "streetAddress2": "13/1", + "city": "Wrocław", + "postalCode": "53-346", + "country": "PL", + "countryArea": "Dolny Śląsk", + "phone": "123456787", "currency": "PLN" } } \ No newline at end of file diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js index 1aa2c3685..18a73cd29 100644 --- a/cypress/integration/products/availableForPurchase.js +++ b/cypress/integration/products/availableForPurchase.js @@ -33,8 +33,8 @@ describe("Products", () => { shippingUtils.deleteShipping(startsWith); productsUtils.deleteProducts(startsWith); - channelsUtils.findDefaultChannel().then(() => { - defaultChannel = channelsUtils.getDefaultChannel(); + channelsUtils.getDefaultChannel().then(channel => { + defaultChannel = channel; cy.fixture("addresses").then(json => { shippingUtils .createShipping(defaultChannel, name, json.plAddress, 10) @@ -66,15 +66,18 @@ describe("Products", () => { productTypeId, attributeId, categoryId, - defaultChannel, + defaultChannel.id, true, false, + true, warehouseId, 10, 10 ) .then(() => { - const productUrl = `${URL_LIST.products}${productsUtils.getCreatedProductId}`; + const productUrl = `${ + URL_LIST.products + }${productsUtils.getCreatedProductId()}`; cy.visit(productUrl) .get(PRODUCTS_SELECTORS.assignedChannels) .click() @@ -87,7 +90,7 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); + searchSteps.searchFor(productName); cy.get(SEARCH_SELECTORS.productItem) .contains(productName) .click() @@ -98,4 +101,45 @@ describe("Products", () => { }); }); }); + it("shouldn't be possible to add to cart not available for purchase product", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + true, + true, + warehouseId, + 10, + 10 + ) + .then(() => { + const productUrl = `${ + URL_LIST.products + }${productsUtils.getCreatedProductId()}`; + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Niedostępne do zakupu") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem) + .contains(productName) + .click() + .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) + .should("be.disabled"); + }); + }); + }); }); diff --git a/cypress/integration/products/publish.js b/cypress/integration/products/publish.js index ebd7588fc..aa274dee4 100644 --- a/cypress/integration/products/publish.js +++ b/cypress/integration/products/publish.js @@ -39,7 +39,7 @@ describe("Products", () => { .its("body.data.shop.domain.url") .as("shopUrl"); }); - xit("should display on frontend only published products", () => { + it("should display on frontend only published products", () => { const productName = `${startsWith}${faker.random.number()}`; channelsUtils.getDefaultChannel().then(defaultChannel => { productsUtils @@ -50,7 +50,8 @@ describe("Products", () => { categoryId, defaultChannel.id, false, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -65,8 +66,8 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); - cy.get(SEARCH_SELECTORS.productItem).contains(name); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); }); }); }); @@ -82,7 +83,8 @@ describe("Products", () => { categoryId, defaultChannel.id, true, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -97,13 +99,13 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); + searchSteps.searchFor(productName); cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); }); }); }); }); - xit("should display not published product for staff member", () => { + it("should display not published product for staff member", () => { const productName = `${startsWith}${faker.random.number()}`; channelsUtils.getDefaultChannel().then(defaultChannel => { productsUtils @@ -114,7 +116,8 @@ describe("Products", () => { categoryId, defaultChannel.id, true, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -131,8 +134,8 @@ describe("Products", () => { cy.visit(shopUrl) .loginInShop() .then(() => { - searchSteps.searchFor(name); - cy.get(SEARCH_SELECTORS.productItem).contains(name); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); }); }); }); diff --git a/cypress/integration/products/showInListings.js b/cypress/integration/products/showInListings.js new file mode 100644 index 000000000..a4a67fe72 --- /dev/null +++ b/cypress/integration/products/showInListings.js @@ -0,0 +1,144 @@ +import faker from "faker"; + +import ShopInfo from "../../apiRequests/ShopInfo"; +import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; +import SearchSteps from "../../steps/frontendSteps/searchSteps"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import ProductsUtils from "../../utils/productsUtils"; + +// +describe("Products", () => { + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const searchSteps = new SearchSteps(); + + const shopInfo = new ShopInfo(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productTypeId; + let attributeId; + let categoryId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productTypeId = productsUtils.getProductTypeId(); + attributeId = productsUtils.getAttributeId(); + categoryId = productsUtils.getCategoryId(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + shopInfo + .getShopInfo() + .its("body.data.shop.domain.url") + .as("shopUrl"); + }); + it("should display on frontend only visible in listings products", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + false + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); + }); + }); + }); + }); + it("shouldn't display not visible in listing product for unlogged user", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); + }); + }); + }); + }); + it("should display not visible in listing product for staff member", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl) + .loginInShop() + .then(() => { + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); + }); + }); + }); + }); + }); +}); diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js index 7e1e58973..dbb053c6e 100644 --- a/cypress/utils/channelsUtils.js +++ b/cypress/utils/channelsUtils.js @@ -3,8 +3,6 @@ import Channels from "../apiRequests/Channels"; class ChannelsUtils { channels = new Channels(); - defaultChannel; - deleteChannels(nameStartsWith) { this.channels.getChannels().then(resp => { const channelsArray = new Set(resp.body.data.channels); @@ -28,7 +26,7 @@ class ChannelsUtils { } }); } - findDefaultChannel() { + getDefaultChannel() { return this.channels.getChannels().then(resp => { const channelsArray = resp.body.data.channels; return (this.defaultChannel = channelsArray.find(function( @@ -38,8 +36,5 @@ class ChannelsUtils { })); }); } - getDefaultChannel() { - return this.defaultChannel; - } } export default ChannelsUtils; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index eda29a95b..914e51aaa 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -42,6 +42,7 @@ class ProductsUtils { channelId, isPublished, isAvailableForPurchase, + visibleInListings, warehouseId, quantityInWarehouse, price @@ -57,7 +58,8 @@ class ProductsUtils { this.createdProductId, channelId, isPublished, - isAvailableForPurchase + isAvailableForPurchase, + visibleInListings ) .then(() => product From 92782086391dc684584e626c16410c9d5a19a4a9 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 12 Feb 2021 09:54:52 +0100 Subject: [PATCH 04/16] add frontShop request --- cypress/apiRequests/ShopInfo.js | 13 ----- .../frontend-elements/cart-selectors.js | 3 -- .../product-details-selectors.js | 3 -- .../frontend-elements/search-selectors.js | 5 -- .../products/availableForPurchase.js | 51 +++++++------------ cypress/steps/frontendSteps/searchSteps.js | 11 ---- cypress/support/frontShop/index.js | 41 +++++++++++++++ cypress/support/index.js | 1 + 8 files changed, 61 insertions(+), 67 deletions(-) delete mode 100644 cypress/apiRequests/ShopInfo.js delete mode 100644 cypress/elements/frontend-elements/cart-selectors.js delete mode 100644 cypress/elements/frontend-elements/product-details-selectors.js delete mode 100644 cypress/elements/frontend-elements/search-selectors.js delete mode 100644 cypress/steps/frontendSteps/searchSteps.js create mode 100644 cypress/support/frontShop/index.js diff --git a/cypress/apiRequests/ShopInfo.js b/cypress/apiRequests/ShopInfo.js deleted file mode 100644 index 17d5f8a9e..000000000 --- a/cypress/apiRequests/ShopInfo.js +++ /dev/null @@ -1,13 +0,0 @@ -class ShopInfo { - getShopInfo() { - const query = `query{ - shop{ - domain{ - url - } - } - }`; - return cy.sendRequestWithQuery(query); - } -} -export default ShopInfo; diff --git a/cypress/elements/frontend-elements/cart-selectors.js b/cypress/elements/frontend-elements/cart-selectors.js deleted file mode 100644 index 0209eba5c..000000000 --- a/cypress/elements/frontend-elements/cart-selectors.js +++ /dev/null @@ -1,3 +0,0 @@ -export const CART_SELECTORS = { - productInCart: "[data-test='cartRow']" -}; diff --git a/cypress/elements/frontend-elements/product-details-selectors.js b/cypress/elements/frontend-elements/product-details-selectors.js deleted file mode 100644 index 9cda9b9f6..000000000 --- a/cypress/elements/frontend-elements/product-details-selectors.js +++ /dev/null @@ -1,3 +0,0 @@ -export const PRODUCTS_DETAILS_SELECTORS = { - addToCartButton: "[data-test='addProductToCartButton']" -}; diff --git a/cypress/elements/frontend-elements/search-selectors.js b/cypress/elements/frontend-elements/search-selectors.js deleted file mode 100644 index 712a91ce9..000000000 --- a/cypress/elements/frontend-elements/search-selectors.js +++ /dev/null @@ -1,5 +0,0 @@ -export const SEARCH_SELECTORS = { - searchButton: "[data-test='menuSearchOverlayLink']", - searchInputField: "[placeholder='search']", - productItem: ".search__products__item" -}; diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js index 18a73cd29..2c70189ef 100644 --- a/cypress/integration/products/availableForPurchase.js +++ b/cypress/integration/products/availableForPurchase.js @@ -1,11 +1,6 @@ import faker from "faker"; -import ShopInfo from "../../apiRequests/ShopInfo"; import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; -import { CART_SELECTORS } from "../../elements/frontend-elements/cart-selectors"; -import { PRODUCTS_DETAILS_SELECTORS } from "../../elements/frontend-elements/product-details-selectors"; -import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; -import SearchSteps from "../../steps/frontendSteps/searchSteps"; import { URL_LIST } from "../../url/url-list"; import ChannelsUtils from "../../utils/channelsUtils"; import ProductsUtils from "../../utils/productsUtils"; @@ -16,9 +11,6 @@ describe("Products", () => { const shippingUtils = new ShippingUtils(); const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); - const searchSteps = new SearchSteps(); - - const shopInfo = new ShopInfo(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; @@ -52,10 +44,6 @@ describe("Products", () => { beforeEach(() => { cy.clearSessionData().loginUserViaRequest(); - shopInfo - .getShopInfo() - .its("body.data.shop.domain.url") - .as("shopUrl"); }); it("should be possible to add to cart available for purchase product", () => { @@ -87,21 +75,19 @@ describe("Products", () => { .get(PRODUCTS_SELECTORS.saveBtn) .click() .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem) - .contains(productName) - .click() - .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) - .click() - .get(CART_SELECTORS.productInCart) - .contains(productName); + .getProductDetails( + productsUtils.getCreatedProductId(), + defaultChannel.slug + ) + .then(productDetailsResp => { + expect(productDetailsResp.body[0].data.product.name).to.equal( + productName + ); + // expect(productDetailsResp.body[0].data.product.isAvailableForPurchase).to.be.true }); }); }); - it("shouldn't be possible to add to cart not available for purchase product", () => { + xit("shouldn't be possible to add to cart not available for purchase product", () => { const productName = `${startsWith}${faker.random.number()}`; productsUtils .createProductInChannel( @@ -131,14 +117,15 @@ describe("Products", () => { .click() .waitForGraph("ProductChannelListingUpdate") .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem) - .contains(productName) - .click() - .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) - .should("be.disabled"); + .getProductDetails( + productsUtils.getCreatedProductId(), + defaultChannel.slug + ) + .then(productDetailsResp => { + expect(productDetailsResp.body[0].data.product.name).to.equal( + productName + ); + // expect(productDetailsResp.body[0].data.product.isAvailableForPurchase).to.be.false; }); }); }); diff --git a/cypress/steps/frontendSteps/searchSteps.js b/cypress/steps/frontendSteps/searchSteps.js deleted file mode 100644 index 9c42e54fa..000000000 --- a/cypress/steps/frontendSteps/searchSteps.js +++ /dev/null @@ -1,11 +0,0 @@ -import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; - -class SearchSteps { - searchFor(query) { - cy.get(SEARCH_SELECTORS.searchButton) - .click() - .get(SEARCH_SELECTORS.searchInputField) - .type(query); - } -} -export default SearchSteps; diff --git a/cypress/support/frontShop/index.js b/cypress/support/frontShop/index.js new file mode 100644 index 000000000..ecc38b831 --- /dev/null +++ b/cypress/support/frontShop/index.js @@ -0,0 +1,41 @@ +/* eslint-disable sort-keys */ + +Cypress.Commands.add("searchInShop", searchQuery => + cy.request({ + method: "POST", + url: Cypress.env("API_URI"), + body: [ + { + operationName: "SearchProducts", + variables: { + attributes: {}, + channel: "default-channel", + pageSize: 6, + priceGte: null, + priceLte: null, + query: searchQuery, + sortBy: null + }, + query: + "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n" + } + ] + }) +); +Cypress.Commands.add("getProductDetails", (productId, channelId) => + cy.request({ + method: "POST", + url: Cypress.env("API_URI"), + body: [ + { + operationName: "ProductDetails", + variables: { + channel: channelId, + id: productId + }, + query: + "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n" + } + ] + }) +); diff --git a/cypress/support/index.js b/cypress/support/index.js index c09fd0ded..35ee6ad9a 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,4 +1,5 @@ import "./user"; +import "./frontShop"; Cypress.Commands.add("clearSessionData", () => { // Because of known cypress bug, not all local storage data are cleared. From 16cbe6c3a564b946b4848f43766ef68478327555 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 12 Feb 2021 10:16:04 +0100 Subject: [PATCH 05/16] add expect --- cypress/elements/catalog/product-selectors.js | 7 +++++-- .../products/availableForPurchase.js | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index a4d1b6437..10190297b 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -23,6 +23,9 @@ export const PRODUCTS_SELECTORS = { channelAvailabilityList: "ul[role='menu']", goBackButton: "[data-test-id='app-header-back-button']", assignedChannels: "[data-test='channel-availability-item']", - publishedRadioButton: "[role=radiogroup]", - visibleInListingsButton: "[class*='MuiFormControlLabel']" + publishedRadioButtons: "[name*='isPublished']", + availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']", + radioButtonsValueTrue: "[value:'true']", + radioButtonsValueFalse: "[value:'false']", + visibleInListingsButton: "[name*='visibleInListings']" }; diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js index 2c70189ef..6c7a55900 100644 --- a/cypress/integration/products/availableForPurchase.js +++ b/cypress/integration/products/availableForPurchase.js @@ -69,8 +69,9 @@ describe("Products", () => { cy.visit(productUrl) .get(PRODUCTS_SELECTORS.assignedChannels) .click() - .get(PRODUCTS_SELECTORS.publishedRadioButton) - .contains("Dostępne do zakupu") + .get( + `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${PRODUCTS_SELECTORS.radioButtonsValueTrue}` + ) .click() .get(PRODUCTS_SELECTORS.saveBtn) .click() @@ -83,7 +84,9 @@ describe("Products", () => { expect(productDetailsResp.body[0].data.product.name).to.equal( productName ); - // expect(productDetailsResp.body[0].data.product.isAvailableForPurchase).to.be.true + expect( + productDetailsResp.body[0].data.product.isAvailableForPurchase + ).to.be.eq(true); }); }); }); @@ -110,8 +113,9 @@ describe("Products", () => { cy.visit(productUrl) .get(PRODUCTS_SELECTORS.assignedChannels) .click() - .get(PRODUCTS_SELECTORS.publishedRadioButton) - .contains("Niedostępne do zakupu") + .get( + `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${PRODUCTS_SELECTORS.radioButtonsValueFalse}` + ) .click() .get(PRODUCTS_SELECTORS.saveBtn) .click() @@ -125,7 +129,9 @@ describe("Products", () => { expect(productDetailsResp.body[0].data.product.name).to.equal( productName ); - // expect(productDetailsResp.body[0].data.product.isAvailableForPurchase).to.be.false; + expect( + productDetailsResp.body[0].data.product.isAvailableForPurchase + ).to.be.eq(false); }); }); }); From 9f5c1b544ebb63cf2729c0d96183f5cd5a2618dd Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 12 Feb 2021 15:36:13 +0100 Subject: [PATCH 06/16] tests for products --- .../apiRequests/frontShop/ProductDetails.js | 32 ++++ cypress/apiRequests/frontShop/Search.js | 34 +++++ cypress/elements/catalog/product-selectors.js | 4 +- cypress/integration/products.js | 54 ------- ...ase.js => availableForPurchaseProducts.js} | 65 +++----- cypress/integration/products/products.js | 77 ++-------- cypress/integration/products/publish.js | 144 ------------------ .../integration/products/publishedProducts.js | 94 ++++++++++++ .../integration/products/showInListings.js | 144 ------------------ .../products/visibleInListingsProducts.js | 94 ++++++++++++ cypress/steps/productSteps.js | 46 ++++++ cypress/support/frontShop/index.js | 41 ----- cypress/support/index.js | 1 - .../utils/frontShop/frontShopProductUtils.js | 28 ++++ 14 files changed, 362 insertions(+), 496 deletions(-) create mode 100644 cypress/apiRequests/frontShop/ProductDetails.js create mode 100644 cypress/apiRequests/frontShop/Search.js delete mode 100644 cypress/integration/products.js rename cypress/integration/products/{availableForPurchase.js => availableForPurchaseProducts.js} (58%) delete mode 100644 cypress/integration/products/publish.js create mode 100644 cypress/integration/products/publishedProducts.js delete mode 100644 cypress/integration/products/showInListings.js create mode 100644 cypress/integration/products/visibleInListingsProducts.js create mode 100644 cypress/steps/productSteps.js delete mode 100644 cypress/support/frontShop/index.js create mode 100644 cypress/utils/frontShop/frontShopProductUtils.js diff --git a/cypress/apiRequests/frontShop/ProductDetails.js b/cypress/apiRequests/frontShop/ProductDetails.js new file mode 100644 index 000000000..bb6e77c95 --- /dev/null +++ b/cypress/apiRequests/frontShop/ProductDetails.js @@ -0,0 +1,32 @@ +class ProductDetails { + getProductDetails(productId, channelId) { + return cy.request({ + method: "POST", + url: Cypress.env("API_URI"), + headers: { + authorization: `JWT ${window.localStorage.getItem("token")}` + }, + body: [ + { + operationName: "ProductDetails", + variables: { + channel: channelId, + id: productId + }, + query: + "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n" + } + ] + }); + } + isAvailableForPurchaseFromResp(resp) { + return resp.body[0].data.product.isAvailableForPurchase; + } + isProductExist(resp, name) { + return ( + resp.body[0].data.product !== null && + resp.body[0].data.product.name === name + ); + } +} +export default ProductDetails; diff --git a/cypress/apiRequests/frontShop/Search.js b/cypress/apiRequests/frontShop/Search.js new file mode 100644 index 000000000..644a698ad --- /dev/null +++ b/cypress/apiRequests/frontShop/Search.js @@ -0,0 +1,34 @@ +class Search { + searchInShop(searchQuery) { + return cy.request({ + method: "POST", + url: Cypress.env("API_URI"), + headers: { + authorization: `JWT ${window.localStorage.getItem("token")}` + }, + body: [ + { + operationName: "SearchProducts", + variables: { + attributes: {}, + channel: "default-channel", + pageSize: 6, + priceGte: null, + priceLte: null, + query: searchQuery, + sortBy: null + }, + query: + "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n" + } + ] + }); + } + isProductExist(resp, name) { + return ( + resp.body[0].data.products.totalCount !== 0 && + resp.body[0].data.products.edges[0].node.name === name + ); + } +} +export default Search; diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index ecc70124c..2fd93e118 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -25,8 +25,8 @@ export const PRODUCTS_SELECTORS = { assignedChannels: "[data-test='channel-availability-item']", publishedRadioButtons: "[name*='isPublished']", availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']", - radioButtonsValueTrue: "[value:'true']", - radioButtonsValueFalse: "[value:'false']", + radioButtonsValueTrue: "[value='true']", + radioButtonsValueFalse: "[value='false']", visibleInListingsButton: "[name*='visibleInListings']", emptyProductRow: "[class*='Skeleton']" }; diff --git a/cypress/integration/products.js b/cypress/integration/products.js deleted file mode 100644 index f1e8f5468..000000000 --- a/cypress/integration/products.js +++ /dev/null @@ -1,54 +0,0 @@ -// -import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors"; -import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; -import { urlList } from "../url/urlList"; - -describe("Products", () => { - beforeEach(() => { - cy.clearSessionData().loginUserViaRequest(); - }); - - it("should navigate to channels page", () => { - cy.visit(urlList.homePage) - .get(LEFT_MENU_SELECTORS.catalog) - .click() - .get(LEFT_MENU_SELECTORS.products) - .click() - .location("pathname") - .should("contain", "/products"); - }); - - 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/availableForPurchase.js b/cypress/integration/products/availableForPurchaseProducts.js similarity index 58% rename from cypress/integration/products/availableForPurchase.js rename to cypress/integration/products/availableForPurchaseProducts.js index 6c7a55900..198f6a8ef 100644 --- a/cypress/integration/products/availableForPurchase.js +++ b/cypress/integration/products/availableForPurchaseProducts.js @@ -1,17 +1,19 @@ import faker from "faker"; -import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import ProductSteps from "../../steps/productSteps"; import { URL_LIST } from "../../url/url-list"; import ChannelsUtils from "../../utils/channelsUtils"; +import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../utils/productsUtils"; import ShippingUtils from "../../utils/shippingUtils"; // -describe("Products", () => { +describe("Products available in listings", () => { const shippingUtils = new ShippingUtils(); const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); - + const productSteps = new ProductSteps(); + const frontShopProductUtils = new FrontShopProductUtils(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; let productTypeId; @@ -46,7 +48,7 @@ describe("Products", () => { cy.clearSessionData().loginUserViaRequest(); }); - it("should be possible to add to cart available for purchase product", () => { + it("should update product to available for purchase", () => { const productName = `${startsWith}${faker.random.number()}`; productsUtils .createProductInChannel( @@ -66,31 +68,19 @@ describe("Products", () => { const productUrl = `${ URL_LIST.products }${productsUtils.getCreatedProductId()}`; - cy.visit(productUrl) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get( - `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${PRODUCTS_SELECTORS.radioButtonsValueTrue}` - ) - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .getProductDetails( + productSteps.updateProductIsAvailableForPurchase(productUrl, true); + frontShopProductUtils + .isProductAvailableForPurchase( productsUtils.getCreatedProductId(), - defaultChannel.slug + defaultChannel.slug, + productName ) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal( - productName - ); - expect( - productDetailsResp.body[0].data.product.isAvailableForPurchase - ).to.be.eq(true); + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); }); }); }); - xit("shouldn't be possible to add to cart not available for purchase product", () => { + it("should update product to not available for purchase", () => { const productName = `${startsWith}${faker.random.number()}`; productsUtils .createProductInChannel( @@ -110,28 +100,15 @@ describe("Products", () => { const productUrl = `${ URL_LIST.products }${productsUtils.getCreatedProductId()}`; - cy.visit(productUrl) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get( - `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${PRODUCTS_SELECTORS.radioButtonsValueFalse}` - ) - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .getProductDetails( + productSteps.updateProductIsAvailableForPurchase(productUrl, false); + frontShopProductUtils + .isProductAvailableForPurchase( productsUtils.getCreatedProductId(), - defaultChannel.slug + defaultChannel.slug, + productName ) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal( - productName - ); - expect( - productDetailsResp.body[0].data.product.isAvailableForPurchase - ).to.be.eq(false); + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(false); }); }); }); diff --git a/cypress/integration/products/products.js b/cypress/integration/products/products.js index 9cb780cda..87960c269 100644 --- a/cypress/integration/products/products.js +++ b/cypress/integration/products/products.js @@ -1,37 +1,25 @@ -import faker from "faker"; - -import Channels from "../../apiRequests/Channels"; +// import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors"; import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; -import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors"; -import { URL_LIST } from "../../url/url-list"; -import ChannelsUtils from "../../utils/channelsUtils"; -import ProductsUtils from "../../utils/productsUtils"; +import { urlList } from "../../url/urlList"; -// describe("Products", () => { - const channels = new Channels(); - const channelsUtils = new ChannelsUtils(); - const productsUtils = new ProductsUtils(); - - const startsWith = "Cy-"; - - before(() => { - cy.clearSessionData().loginUserViaRequest(); - productsUtils.deleteProducts(startsWith); - channelsUtils.deleteChannels(startsWith); - }); - beforeEach(() => { cy.clearSessionData().loginUserViaRequest(); }); - xit("should add new visible product", () => { - cy.visit("/") + it("should navigate to channels page", () => { + cy.visit(urlList.homePage) .get(LEFT_MENU_SELECTORS.catalog) .click() - .get(PRODUCTS_SELECTORS.products) + .get(LEFT_MENU_SELECTORS.products) .click() + .location("pathname") + .should("contain", "/products"); + }); + + it("should add new visible product", () => { + cy.visit(urlList.products) .get(PRODUCTS_SELECTORS.createProductBtn) .click() .get(PRODUCTS_SELECTORS.productNameInput) @@ -63,47 +51,4 @@ describe("Products", () => { .get(PRODUCTS_SELECTORS.confirmationMsg) .contains("Product created"); }); - it("should display correct availibility for product in channel", () => { - const name = `${startsWith}${faker.random.number()}`; - cy.fixture("addresses").then(json => { - channels.createChannel(true, name, name, json.plAddress.currency); - productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { - const productTypeId = productsUtils.getProductTypeId(); - const attributeId = productsUtils.getAttributeId(); - const categoryId = productsUtils.getCategoryId(); - productsUtils.createProduct( - name, - attributeId, - productTypeId, - categoryId - ); - cy.visit(URL_LIST.products) - .get(PRODUCTS_SELECTORS.searchProducts) - .type(name) - .get(PRODUCTS_SELECTORS.productsList) - .contains(name) - .click() - .get(PRODUCTS_SELECTORS.availableManageButton) - .click() - .get(PRODUCTS_SELECTORS.channelsAvailabilityForm) - .contains(name) - .click() - .get(BUTTON_SELECTORS.submit) - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .get(PRODUCTS_SELECTORS.goBackButton) - .click() - .get(PRODUCTS_SELECTORS.searchProducts) - .type(name) - .get(PRODUCTS_SELECTORS.productsList) - .contains(name) - .parentsUntil("tbody") - .find(PRODUCTS_SELECTORS.channelAvailabilityColumn) - .click() - .get(PRODUCTS_SELECTORS.channelAvailabilityList) - .contains(name); - }); - }); - }); }); diff --git a/cypress/integration/products/publish.js b/cypress/integration/products/publish.js deleted file mode 100644 index aa274dee4..000000000 --- a/cypress/integration/products/publish.js +++ /dev/null @@ -1,144 +0,0 @@ -import faker from "faker"; - -import ShopInfo from "../../apiRequests/ShopInfo"; -import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; -import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; -import SearchSteps from "../../steps/frontendSteps/searchSteps"; -import { URL_LIST } from "../../url/url-list"; -import ChannelsUtils from "../../utils/channelsUtils"; -import ProductsUtils from "../../utils/productsUtils"; - -// -describe("Products", () => { - const channelsUtils = new ChannelsUtils(); - const productsUtils = new ProductsUtils(); - const searchSteps = new SearchSteps(); - - const shopInfo = new ShopInfo(); - - const startsWith = "Cy-"; - const name = `${startsWith}${faker.random.number()}`; - let productTypeId; - let attributeId; - let categoryId; - - before(() => { - cy.clearSessionData().loginUserViaRequest(); - productsUtils.deleteProducts(startsWith); - productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { - productTypeId = productsUtils.getProductTypeId(); - attributeId = productsUtils.getAttributeId(); - categoryId = productsUtils.getCategoryId(); - }); - }); - - beforeEach(() => { - cy.clearSessionData().loginUserViaRequest(); - shopInfo - .getShopInfo() - .its("body.data.shop.domain.url") - .as("shopUrl"); - }); - it("should display on frontend only published products", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - false, - false, - true - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.publishedRadioButton) - .contains("Opublikowany") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).contains(productName); - }); - }); - }); - }); - it("shouldn't display not published product for unlogged user", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - true, - false, - true - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.publishedRadioButton) - .contains("Nie opublikowano") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); - }); - }); - }); - }); - it("should display not published product for staff member", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - true, - false, - true - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.publishedRadioButton) - .contains("Nie opublikowano") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl) - .loginInShop() - .then(() => { - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).contains(productName); - }); - }); - }); - }); - }); -}); diff --git a/cypress/integration/products/publishedProducts.js b/cypress/integration/products/publishedProducts.js new file mode 100644 index 000000000..5d6a46378 --- /dev/null +++ b/cypress/integration/products/publishedProducts.js @@ -0,0 +1,94 @@ +import faker from "faker"; + +import ProductSteps from "../../steps/productSteps"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; +import ProductsUtils from "../../utils/productsUtils"; + +// +describe("Publish products", () => { + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const productSteps = new ProductSteps(); + const frontShopProductUtils = new FrontShopProductUtils(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productTypeId; + let attributeId; + let categoryId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productTypeId = productsUtils.getProductTypeId(); + attributeId = productsUtils.getAttributeId(); + categoryId = productsUtils.getCategoryId(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + it("should update product to published", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + false, + false, + true + ) + .then(() => { + const productId = productsUtils.getCreatedProductId(); + const productUrl = `${URL_LIST.products}${productId}`; + productSteps.updateProductPublish(productUrl, true); + frontShopProductUtils + .isProductVisible(productId, defaultChannel.slug, productName) + .then(isVisible => { + expect(isVisible).to.be.eq(true); + }); + }); + }); + }); + it("should update product to not published", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + const productId = productsUtils.getCreatedProductId(); + const productUrl = `${URL_LIST.products}${productId}`; + productSteps.updateProductPublish(productUrl, false); + frontShopProductUtils + .isProductVisible(productId, defaultChannel.slug, productName) + .then(isVisible => { + expect(isVisible).to.be.eq(false); + }); + cy.loginInShop().then(() => { + frontShopProductUtils + .isProductVisible(productId, defaultChannel.slug, productName) + .then(isVisible => { + expect(isVisible).to.be.eq(true); + }); + }); + }); + }); + }); +}); diff --git a/cypress/integration/products/showInListings.js b/cypress/integration/products/showInListings.js deleted file mode 100644 index a4a67fe72..000000000 --- a/cypress/integration/products/showInListings.js +++ /dev/null @@ -1,144 +0,0 @@ -import faker from "faker"; - -import ShopInfo from "../../apiRequests/ShopInfo"; -import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; -import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors"; -import SearchSteps from "../../steps/frontendSteps/searchSteps"; -import { URL_LIST } from "../../url/url-list"; -import ChannelsUtils from "../../utils/channelsUtils"; -import ProductsUtils from "../../utils/productsUtils"; - -// -describe("Products", () => { - const channelsUtils = new ChannelsUtils(); - const productsUtils = new ProductsUtils(); - const searchSteps = new SearchSteps(); - - const shopInfo = new ShopInfo(); - - const startsWith = "Cy-"; - const name = `${startsWith}${faker.random.number()}`; - let productTypeId; - let attributeId; - let categoryId; - - before(() => { - cy.clearSessionData().loginUserViaRequest(); - productsUtils.deleteProducts(startsWith); - productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { - productTypeId = productsUtils.getProductTypeId(); - attributeId = productsUtils.getAttributeId(); - categoryId = productsUtils.getCategoryId(); - }); - }); - - beforeEach(() => { - cy.clearSessionData().loginUserViaRequest(); - shopInfo - .getShopInfo() - .its("body.data.shop.domain.url") - .as("shopUrl"); - }); - it("should display on frontend only visible in listings products", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - true, - false, - false - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.visibleInListingsButton) - .contains("Wyświetlaj na liście produktów") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).contains(productName); - }); - }); - }); - }); - it("shouldn't display not visible in listing product for unlogged user", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - true, - false, - true - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.visibleInListingsButton) - .contains("Wyświetlaj na liście produktów") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl); - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); - }); - }); - }); - }); - it("should display not visible in listing product for staff member", () => { - const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productTypeId, - attributeId, - categoryId, - defaultChannel.id, - true, - false, - true - ) - .then(() => { - cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) - .get(PRODUCTS_SELECTORS.assignedChannels) - .click() - .get(PRODUCTS_SELECTORS.visibleInListingsButton) - .contains("Wyświetlaj na liście produktów") - .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate") - .get("@shopUrl") - .then(shopUrl => { - cy.visit(shopUrl) - .loginInShop() - .then(() => { - searchSteps.searchFor(productName); - cy.get(SEARCH_SELECTORS.productItem).contains(productName); - }); - }); - }); - }); - }); -}); diff --git a/cypress/integration/products/visibleInListingsProducts.js b/cypress/integration/products/visibleInListingsProducts.js new file mode 100644 index 000000000..2d6ff8614 --- /dev/null +++ b/cypress/integration/products/visibleInListingsProducts.js @@ -0,0 +1,94 @@ +import faker from "faker"; + +import ProductSteps from "../../steps/productSteps"; +import { URL_LIST } from "../../url/url-list"; +import ChannelsUtils from "../../utils/channelsUtils"; +import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; +import ProductsUtils from "../../utils/productsUtils"; + +// +describe("Products displayed in listings", () => { + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const productSteps = new ProductSteps(); + const frontShopProductUtils = new FrontShopProductUtils(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productTypeId; + let attributeId; + let categoryId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productTypeId = productsUtils.getProductTypeId(); + attributeId = productsUtils.getAttributeId(); + categoryId = productsUtils.getCategoryId(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + it("should update product to visible in listings", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + false + ) + .then(() => { + const productId = productsUtils.getCreatedProductId(); + const productUrl = `${URL_LIST.products}${productId}`; + productSteps.updateProductVisibleInListings(productUrl); + frontShopProductUtils + .isProductVisibleInSearchResult(productName, defaultChannel.slug) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); + }); + }); + }); + }); + it("should update product to not visible in listings", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + const productId = productsUtils.getCreatedProductId(); + const productUrl = `${URL_LIST.products}${productId}`; + productSteps.updateProductVisibleInListings(productUrl); + frontShopProductUtils + .isProductVisibleInSearchResult(productName, defaultChannel.slug) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(false); + }); + cy.loginInShop().then(() => { + frontShopProductUtils + .isProductVisibleInSearchResult(productName, defaultChannel.slug) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); + }); + }); + }); + }); + }); +}); diff --git a/cypress/steps/productSteps.js b/cypress/steps/productSteps.js new file mode 100644 index 000000000..244ab8392 --- /dev/null +++ b/cypress/steps/productSteps.js @@ -0,0 +1,46 @@ +import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; + +class ProductSteps { + updateProductIsAvailableForPurchase(productUrl, isAvailableForPurchase) { + let isAvailableForPurchaseSelector; + if (isAvailableForPurchase) { + isAvailableForPurchaseSelector = PRODUCTS_SELECTORS.radioButtonsValueTrue; + } else { + isAvailableForPurchaseSelector = + PRODUCTS_SELECTORS.radioButtonsValueFalse; + } + this.updateProductMenageInChannel( + productUrl, + `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}` + ); + } + updateProductPublish(productUrl, isPublished) { + let isPublishedSelector; + if (isPublished) { + isPublishedSelector = PRODUCTS_SELECTORS.radioButtonsValueTrue; + } else { + isPublishedSelector = PRODUCTS_SELECTORS.radioButtonsValueFalse; + } + this.updateProductMenageInChannel( + productUrl, + `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}` + ); + } + updateProductVisibleInListings(productUrl) { + this.updateProductMenageInChannel( + productUrl, + PRODUCTS_SELECTORS.visibleInListingsButton + ); + } + updateProductMenageInChannel(productUrl, menageSelector) { + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(menageSelector) + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate"); + } +} +export default ProductSteps; diff --git a/cypress/support/frontShop/index.js b/cypress/support/frontShop/index.js deleted file mode 100644 index ecc38b831..000000000 --- a/cypress/support/frontShop/index.js +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-disable sort-keys */ - -Cypress.Commands.add("searchInShop", searchQuery => - cy.request({ - method: "POST", - url: Cypress.env("API_URI"), - body: [ - { - operationName: "SearchProducts", - variables: { - attributes: {}, - channel: "default-channel", - pageSize: 6, - priceGte: null, - priceLte: null, - query: searchQuery, - sortBy: null - }, - query: - "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n" - } - ] - }) -); -Cypress.Commands.add("getProductDetails", (productId, channelId) => - cy.request({ - method: "POST", - url: Cypress.env("API_URI"), - body: [ - { - operationName: "ProductDetails", - variables: { - channel: channelId, - id: productId - }, - query: - "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n" - } - ] - }) -); diff --git a/cypress/support/index.js b/cypress/support/index.js index 8576cdfa2..03851e2ca 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,5 +1,4 @@ import "./user"; -import "./frontShop"; import { urlList } from "../url/urlList"; diff --git a/cypress/utils/frontShop/frontShopProductUtils.js b/cypress/utils/frontShop/frontShopProductUtils.js new file mode 100644 index 000000000..1b8411f23 --- /dev/null +++ b/cypress/utils/frontShop/frontShopProductUtils.js @@ -0,0 +1,28 @@ +import ProductDetails from "../../apiRequests/frontShop/ProductDetails"; +import Search from "../../apiRequests/frontShop/Search"; + +class FrontShopProductUtils { + isProductVisible(productId, channelSlug, name) { + const productDetails = new ProductDetails(); + return productDetails + .getProductDetails(productId, channelSlug) + .then(productDetailsResp => + productDetails.isProductExist(productDetailsResp, name) + ); + } + isProductAvailableForPurchase(productId, channelSlug) { + const productDetails = new ProductDetails(); + return productDetails + .getProductDetails(productId, channelSlug) + .then(productDetailsResp => + productDetails.isAvailableForPurchaseFromResp(productDetailsResp) + ); + } + isProductVisibleInSearchResult(productName, channelSlug) { + const search = new Search(); + return search + .searchInShop(productName, channelSlug) + .then(resp => search.isProductExist(resp, productName)); + } +} +export default FrontShopProductUtils; From 6f1190be2871172f7b0ebbb4a090fdab38e3b3d7 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 12 Feb 2021 16:48:23 +0100 Subject: [PATCH 07/16] fix tests for channels after merge --- cypress/integration/channels.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cypress/integration/channels.js b/cypress/integration/channels.js index 31db58232..e9c61f7c9 100644 --- a/cypress/integration/channels.js +++ b/cypress/integration/channels.js @@ -14,16 +14,18 @@ import { ORDERS_SELECTORS } from "../elements/orders/orders-selectors"; import { BUTTON_SELECTORS } from "../elements/shared/button-selectors"; import ChannelsSteps from "../steps/channelsSteps"; import { urlList } from "../url/urlList"; +import ChannelsUtils from "../utils/channelsUtils"; describe("Channels", () => { const channelStartsWith = "Cypress:"; const currency = "PLN"; const channels = new Channels(); const channelsSteps = new ChannelsSteps(); + const channelsUtils = new ChannelsUtils(); before(() => { cy.clearSessionData().loginUserViaRequest(); - channels.deleteTestChannels(channelStartsWith); + channelsUtils.deleteChannels(channelStartsWith); }); beforeEach(() => { From a33b13aaa20914bda12b89ad811cdbc59c21ac5f Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 18 Feb 2021 19:28:01 +0100 Subject: [PATCH 08/16] tests for available for purchase --- cypress/apiRequests/Product.js | 21 +++--- .../products/availableForPurchaseProducts.js | 72 ++++++++++--------- .../integration/products/publishedProducts.js | 27 ++++--- cypress/utils/productsUtils.js | 17 ++++- 4 files changed, 84 insertions(+), 53 deletions(-) diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index e0b41cdb3..b3cfdcbe0 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -46,6 +46,7 @@ class Product { } } }`; + cy.sendRequestWithQuery(mutation); } updateChannelPriceInVariant(variantId, channelId) { @@ -106,16 +107,16 @@ class Product { warehouse: "${warehouseId}" quantity: ${quantity} } -}) { - productVariants{ - id - name - } - bulkProductErrors{ - field - message - } -} + }) { + productVariants{ + id + name + } + bulkProductErrors{ + field + message + } + } }`; return cy.sendRequestWithQuery(mutation); } diff --git a/cypress/integration/products/availableForPurchaseProducts.js b/cypress/integration/products/availableForPurchaseProducts.js index 44d34d70f..c08b0f103 100644 --- a/cypress/integration/products/availableForPurchaseProducts.js +++ b/cypress/integration/products/availableForPurchaseProducts.js @@ -27,16 +27,24 @@ describe("Products available in listings", () => { shippingUtils.deleteShipping(startsWith); productsUtils.deleteProperProducts(startsWith); - channelsUtils.getDefaultChannel().then(channel => { - defaultChannel = channel; - cy.fixture("addresses").then(json => { - shippingUtils - .createShipping(defaultChannel, name, json.plAddress, 10) - .then(() => { - warehouse = shippingUtils.getWarehouse(); - }); + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + cy.fixture("addresses"); + }) + .then(addressesFixture => { + shippingUtils.createShipping( + defaultChannel, + name, + addressesFixture.plAddress, + 10 + ); + }) + .then(() => { + warehouse = shippingUtils.getWarehouse(); }); - }); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { productType = productsUtils.getProductType(); attribute = productsUtils.getAttribute(); @@ -50,7 +58,6 @@ describe("Products available in listings", () => { it("should update product to available for purchase", () => { const productName = `${startsWith}${faker.random.number()}`; - productsUtils.createProductInChannel(productName); productsUtils .createProductInChannel( productName, @@ -59,26 +66,27 @@ describe("Products available in listings", () => { 10, productType.id, attribute.id, - category, + category.id, + 1, true, false, - true, - 1 + true ) .then(() => { - const productUrl = `${ - URL_LIST.products - }${productsUtils.getCreatedProductId()}`; + const productUrl = `${URL_LIST.products}${ + productsUtils.getCreatedProduct().id + }`; productSteps.updateProductIsAvailableForPurchase(productUrl, true); - frontShopProductUtils - .isProductAvailableForPurchase( - productsUtils.getCreatedProductId(), - defaultChannel.slug, - productName - ) - .then(isProductVisible => { - expect(isProductVisible).to.be.eq(true); - }); + }) + .then(() => { + frontShopProductUtils.isProductAvailableForPurchase( + productsUtils.getCreatedProduct().id, + defaultChannel.slug, + productName + ); + }) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); }); }); it("should update product to not available for purchase", () => { @@ -91,20 +99,20 @@ describe("Products available in listings", () => { 10, productType.id, attribute.id, - category, + category.id, + 1, true, true, - true, - 1 + true ) .then(() => { - const productUrl = `${ - URL_LIST.products - }${productsUtils.getCreatedProductId()}`; + const productUrl = `${URL_LIST.products}${ + productsUtils.getCreatedProduct().id + }`; productSteps.updateProductIsAvailableForPurchase(productUrl, false); frontShopProductUtils .isProductAvailableForPurchase( - productsUtils.getCreatedProductId(), + productsUtils.getCreatedProduct().id, defaultChannel.slug, productName ) diff --git a/cypress/integration/products/publishedProducts.js b/cypress/integration/products/publishedProducts.js index 031437342..3e3b441fe 100644 --- a/cypress/integration/products/publishedProducts.js +++ b/cypress/integration/products/publishedProducts.js @@ -38,20 +38,23 @@ describe("Publish products", () => { productsUtils .createProductInChannel( productName, + defaultChannel.id, + null, + null, productType.id, attribute.id, category.id, - defaultChannel.id, + null, false, false, true ) .then(() => { - const productId = productsUtils.getCreatedProductId(); - const productUrl = `${URL_LIST.products}${productId}`; + const product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; productSteps.updateProductPublish(productUrl, true); frontShopProductUtils - .isProductVisible(productId, defaultChannel.slug, productName) + .isProductVisible(product.id, defaultChannel.slug, productName) .then(isVisible => { expect(isVisible).to.be.eq(true); }); @@ -68,13 +71,21 @@ describe("Publish products", () => { attribute.id, category.id, defaultChannel.id, - true, + productName, + defaultChannel.id, + null, + null, + productType.id, + attribute.id, + category.id, + null, + false, false, true ) .then(() => { - const productId = productsUtils.getCreatedProductId(); - const productUrl = `${URL_LIST.products}${productId}`; + const product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; productSteps.updateProductPublish(productUrl, false); frontShopProductUtils .isProductVisible(productId, defaultChannel.slug, productName) @@ -83,7 +94,7 @@ describe("Publish products", () => { }); cy.loginInShop().then(() => { frontShopProductUtils - .isProductVisible(productId, defaultChannel.slug, productName) + .isProductVisible(product.id, defaultChannel.slug, productName) .then(isVisible => { expect(isVisible).to.be.eq(true); }); diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index a134e7c53..c1c633bd1 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -30,11 +30,20 @@ class ProductsUtils { productTypeId, attributeId, categoryId, - price + price, + isPublished = true, + isAvailableForPurchase = true, + visibleInListings = true ) { return this.createProduct(attributeId, name, productTypeId, categoryId) .then(() => - this.productRequest.updateChannelInProduct(this.product.id, channelId) + this.productRequest.updateChannelInProduct( + this.product.id, + channelId, + isPublished, + isAvailableForPurchase, + visibleInListings + ) ) .then(() => { this.createVariant( @@ -101,7 +110,9 @@ class ProductsUtils { resp.body.data.productVariantBulkCreate.productVariants) ); } - + getCreatedProduct() { + return this.product; + } getCreatedVariants() { return this.variants; } From 1b4da95e81159a9aa80e85224687aa274079ab8d Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 18 Feb 2021 20:32:35 +0100 Subject: [PATCH 09/16] tests for products --- cypress/apiRequests/Channels.js | 3 +- cypress/apiRequests/Product.js | 25 +++-- .../integration/products/publishedProducts.js | 105 ++++++++++-------- .../products/visibleInListingsProducts.js | 93 +++++++++------- 4 files changed, 130 insertions(+), 96 deletions(-) diff --git a/cypress/apiRequests/Channels.js b/cypress/apiRequests/Channels.js index 39e8f8162..6d8872798 100644 --- a/cypress/apiRequests/Channels.js +++ b/cypress/apiRequests/Channels.js @@ -45,7 +45,8 @@ class Channels { channelErrors{ message } - }`; + } + }`; return cy.sendRequestWithQuery(deleteChannelMutation); } } diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index b3cfdcbe0..16b7d2a30 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -94,19 +94,26 @@ class Product { price = 1, costPrice = 1 ) { + const channelListings = channelId + ? `channelListings:{ + channelId:"${channelId}" + price:"${price}" + costPrice:"${costPrice}" + }` + : ""; + const stocks = warehouseId + ? `stocks:{ + warehouse:"${warehouseId}" + quantity:${quantity} + }` + : ""; + const mutation = `mutation{ productVariantBulkCreate(product: "${productId}", variants: { attributes: [] sku: "${sku}" - channelListings: { - channelId: "${channelId}" - price: "${price}" - costPrice: "${costPrice}" - } - stocks: { - warehouse: "${warehouseId}" - quantity: ${quantity} - } + ${channelListings} + ${stocks} }) { productVariants{ id diff --git a/cypress/integration/products/publishedProducts.js b/cypress/integration/products/publishedProducts.js index 3e3b441fe..7a0a0d8ba 100644 --- a/cypress/integration/products/publishedProducts.js +++ b/cypress/integration/products/publishedProducts.js @@ -34,9 +34,12 @@ describe("Publish products", () => { }); it("should update product to published", () => { const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createProductInChannel( productName, defaultChannel.id, null, @@ -44,62 +47,68 @@ describe("Publish products", () => { productType.id, attribute.id, category.id, - null, + 1, false, false, true - ) - .then(() => { - const product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; - productSteps.updateProductPublish(productUrl, true); - frontShopProductUtils - .isProductVisible(product.id, defaultChannel.slug, productName) - .then(isVisible => { - expect(isVisible).to.be.eq(true); - }); - }); - }); + ); + }) + .then(() => { + const product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; + productSteps.updateProductPublish(productUrl, true); + frontShopProductUtils.isProductVisible( + product.id, + defaultChannel.slug, + productName + ); + }) + .then(isVisible => { + expect(isVisible).to.be.eq(true); + }); }); it("should update product to not published", () => { const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( - productName, - productType.id, - attribute.id, - category.id, - defaultChannel.id, + let defaultChannel; + let product; + + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createProductInChannel( productName, defaultChannel.id, null, null, productType.id, attribute.id, - category.id, - null, - false, - false, - true - ) - .then(() => { - const product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; - productSteps.updateProductPublish(productUrl, false); - frontShopProductUtils - .isProductVisible(productId, defaultChannel.slug, productName) - .then(isVisible => { - expect(isVisible).to.be.eq(false); - }); - cy.loginInShop().then(() => { - frontShopProductUtils - .isProductVisible(product.id, defaultChannel.slug, productName) - .then(isVisible => { - expect(isVisible).to.be.eq(true); - }); - }); - }); - }); + category.id + ); + }) + .then(() => { + product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; + productSteps.updateProductPublish(productUrl, false); + frontShopProductUtils.isProductVisible( + product.id, + defaultChannel.slug, + productName + ); + }) + .then(isVisible => { + expect(isVisible).to.be.eq(false); + cy.loginInShop(); + }) + .then(() => { + frontShopProductUtils.isProductVisible( + product.id, + defaultChannel.slug, + productName + ); + }) + .then(isVisible => { + expect(isVisible).to.be.eq(true); + }); }); }); diff --git a/cypress/integration/products/visibleInListingsProducts.js b/cypress/integration/products/visibleInListingsProducts.js index 321bc1969..e55aa9fa8 100644 --- a/cypress/integration/products/visibleInListingsProducts.js +++ b/cypress/integration/products/visibleInListingsProducts.js @@ -34,61 +34,78 @@ describe("Products displayed in listings", () => { }); it("should update product to visible in listings", () => { const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createProductInChannel( productName, + defaultChannel.id, + null, + null, productType.id, attribute.id, category.id, - defaultChannel.id, + 1, true, false, false - ) - .then(() => { - const productId = productsUtils.getCreatedProductId(); - const productUrl = `${URL_LIST.products}${productId}`; - productSteps.updateProductVisibleInListings(productUrl); - frontShopProductUtils - .isProductVisibleInSearchResult(productName, defaultChannel.slug) - .then(isProductVisible => { - expect(isProductVisible).to.be.eq(true); - }); - }); - }); + ); + }) + .then(() => { + const product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; + productSteps.updateProductVisibleInListings(productUrl); + frontShopProductUtils.isProductVisibleInSearchResult( + productName, + defaultChannel.slug + ); + }) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); + }); }); it("should update product to not visible in listings", () => { const productName = `${startsWith}${faker.random.number()}`; - channelsUtils.getDefaultChannel().then(defaultChannel => { - productsUtils - .createProductInChannel( + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createProductInChannel( productName, + defaultChannel.id, + null, + null, productType.id, attribute.id, category.id, - defaultChannel.id, + 1, true, false, true - ) - .then(() => { - const productId = productsUtils.getCreatedProductId(); - const productUrl = `${URL_LIST.products}${productId}`; - productSteps.updateProductVisibleInListings(productUrl); - frontShopProductUtils - .isProductVisibleInSearchResult(productName, defaultChannel.slug) - .then(isProductVisible => { - expect(isProductVisible).to.be.eq(false); - }); - cy.loginInShop().then(() => { - frontShopProductUtils - .isProductVisibleInSearchResult(productName, defaultChannel.slug) - .then(isProductVisible => { - expect(isProductVisible).to.be.eq(true); - }); + ); + }) + .then(() => { + const product = productsUtils.getCreatedProduct(); + const productUrl = `${URL_LIST.products}${product.id}`; + productSteps.updateProductVisibleInListings(productUrl); + frontShopProductUtils + .isProductVisibleInSearchResult(productName, defaultChannel.slug) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(false); }); - }); - }); + cy.loginInShop(); + }) + .then(() => { + frontShopProductUtils.isProductVisibleInSearchResult( + productName, + defaultChannel.slug + ); + }) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(true); + }); }); }); From 76ef2e6264197891e89843594c911b41e3e27bff Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 18 Feb 2021 20:50:12 +0100 Subject: [PATCH 10/16] tests for products --- cypress/integration/homePage.js | 2 +- .../integration/products/availableForPurchaseProducts.js | 6 +++--- cypress/integration/products/publishedProducts.js | 8 ++++---- cypress/integration/products/visibleInListingsProducts.js | 6 +++--- cypress/url/url-list.js | 6 ------ 5 files changed, 11 insertions(+), 17 deletions(-) delete mode 100644 cypress/url/url-list.js diff --git a/cypress/integration/homePage.js b/cypress/integration/homePage.js index 74e8762a1..483eb49cd 100644 --- a/cypress/integration/homePage.js +++ b/cypress/integration/homePage.js @@ -11,7 +11,7 @@ import ProductsUtils from "../utils/productsUtils"; import ShippingUtils from "../utils/shippingUtils"; // -describe("User authorization", () => { +describe("Homepage analytics", () => { const startsWith = "Cy-"; const customer = new Customer(); diff --git a/cypress/integration/products/availableForPurchaseProducts.js b/cypress/integration/products/availableForPurchaseProducts.js index c08b0f103..659bd2c7e 100644 --- a/cypress/integration/products/availableForPurchaseProducts.js +++ b/cypress/integration/products/availableForPurchaseProducts.js @@ -1,7 +1,7 @@ import faker from "faker"; import ProductSteps from "../../steps/productSteps"; -import { URL_LIST } from "../../url/url-list"; +import { urlList } from "../../url/urlList"; import ChannelsUtils from "../../utils/channelsUtils"; import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../utils/productsUtils"; @@ -73,7 +73,7 @@ describe("Products available in listings", () => { true ) .then(() => { - const productUrl = `${URL_LIST.products}${ + const productUrl = `${urlList.products}${ productsUtils.getCreatedProduct().id }`; productSteps.updateProductIsAvailableForPurchase(productUrl, true); @@ -106,7 +106,7 @@ describe("Products available in listings", () => { true ) .then(() => { - const productUrl = `${URL_LIST.products}${ + const productUrl = `${urlList.products}${ productsUtils.getCreatedProduct().id }`; productSteps.updateProductIsAvailableForPurchase(productUrl, false); diff --git a/cypress/integration/products/publishedProducts.js b/cypress/integration/products/publishedProducts.js index 7a0a0d8ba..f6b23a7ca 100644 --- a/cypress/integration/products/publishedProducts.js +++ b/cypress/integration/products/publishedProducts.js @@ -1,13 +1,13 @@ import faker from "faker"; import ProductSteps from "../../steps/productSteps"; -import { URL_LIST } from "../../url/url-list"; +import { urlList } from "../../url/urlList"; import ChannelsUtils from "../../utils/channelsUtils"; import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../utils/productsUtils"; // -describe("Publish products", () => { +describe("Published products", () => { const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); const productSteps = new ProductSteps(); @@ -55,7 +55,7 @@ describe("Publish products", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; + const productUrl = `${urlList.products}${product.id}`; productSteps.updateProductPublish(productUrl, true); frontShopProductUtils.isProductVisible( product.id, @@ -88,7 +88,7 @@ describe("Publish products", () => { }) .then(() => { product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; + const productUrl = `${urlList.products}${product.id}`; productSteps.updateProductPublish(productUrl, false); frontShopProductUtils.isProductVisible( product.id, diff --git a/cypress/integration/products/visibleInListingsProducts.js b/cypress/integration/products/visibleInListingsProducts.js index e55aa9fa8..9d4729740 100644 --- a/cypress/integration/products/visibleInListingsProducts.js +++ b/cypress/integration/products/visibleInListingsProducts.js @@ -1,7 +1,7 @@ import faker from "faker"; import ProductSteps from "../../steps/productSteps"; -import { URL_LIST } from "../../url/url-list"; +import { urlList } from "../../url/urlList"; import ChannelsUtils from "../../utils/channelsUtils"; import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../utils/productsUtils"; @@ -55,7 +55,7 @@ describe("Products displayed in listings", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; + const productUrl = `${urlList.products}${product.id}`; productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils.isProductVisibleInSearchResult( productName, @@ -89,7 +89,7 @@ describe("Products displayed in listings", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${URL_LIST.products}${product.id}`; + const productUrl = `${urlList.products}${product.id}`; productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils .isProductVisibleInSearchResult(productName, defaultChannel.slug) diff --git a/cypress/url/url-list.js b/cypress/url/url-list.js deleted file mode 100644 index aa047845c..000000000 --- a/cypress/url/url-list.js +++ /dev/null @@ -1,6 +0,0 @@ -export const URL_LIST = { - dashbord: "/", - channels: "/channels/", - products: "/products/", - orders: "/orders/" -}; From 4fc9fefa7409571291b0da2ddbba1893dbc12760 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 19 Feb 2021 10:57:25 +0100 Subject: [PATCH 11/16] tests for products --- .../apiRequests/frontShop/ProductDetails.js | 34 ++++----------- cypress/apiRequests/frontShop/Search.js | 42 ++++++------------- .../availableForPurchaseProducts.js | 12 +++--- .../{ => menageProducts}/publishedProducts.js | 10 ++--- .../visibleInListingsProducts.js | 10 ++--- cypress/support/index.js | 30 +++++++++---- cypress/support/user/index.js | 2 +- .../utils/frontShop/frontShopProductUtils.js | 18 +++++--- 8 files changed, 72 insertions(+), 86 deletions(-) rename cypress/integration/products/{ => menageProducts}/availableForPurchaseProducts.js (89%) rename cypress/integration/products/{ => menageProducts}/publishedProducts.js (90%) rename cypress/integration/products/{ => menageProducts}/visibleInListingsProducts.js (91%) diff --git a/cypress/apiRequests/frontShop/ProductDetails.js b/cypress/apiRequests/frontShop/ProductDetails.js index bb6e77c95..def96af89 100644 --- a/cypress/apiRequests/frontShop/ProductDetails.js +++ b/cypress/apiRequests/frontShop/ProductDetails.js @@ -1,32 +1,12 @@ class ProductDetails { getProductDetails(productId, channelId) { - return cy.request({ - method: "POST", - url: Cypress.env("API_URI"), - headers: { - authorization: `JWT ${window.localStorage.getItem("token")}` - }, - body: [ - { - operationName: "ProductDetails", - variables: { - channel: channelId, - id: productId - }, - query: - "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n" - } - ] - }); - } - isAvailableForPurchaseFromResp(resp) { - return resp.body[0].data.product.isAvailableForPurchase; - } - isProductExist(resp, name) { - return ( - resp.body[0].data.product !== null && - resp.body[0].data.product.name === name - ); + const variables = { + channel: channelId, + id: productId + }; + const query = + "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n"; + return cy.sendFrontShopRequestWithQuery("ProductDetails", variables, query); } } export default ProductDetails; diff --git a/cypress/apiRequests/frontShop/Search.js b/cypress/apiRequests/frontShop/Search.js index 644a698ad..62feb5424 100644 --- a/cypress/apiRequests/frontShop/Search.js +++ b/cypress/apiRequests/frontShop/Search.js @@ -1,34 +1,18 @@ class Search { searchInShop(searchQuery) { - return cy.request({ - method: "POST", - url: Cypress.env("API_URI"), - headers: { - authorization: `JWT ${window.localStorage.getItem("token")}` - }, - body: [ - { - operationName: "SearchProducts", - variables: { - attributes: {}, - channel: "default-channel", - pageSize: 6, - priceGte: null, - priceLte: null, - query: searchQuery, - sortBy: null - }, - query: - "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n" - } - ] - }); - } - isProductExist(resp, name) { - return ( - resp.body[0].data.products.totalCount !== 0 && - resp.body[0].data.products.edges[0].node.name === name - ); + const variables = { + attributes: {}, + channel: "default-channel", + pageSize: 6, + priceGte: null, + priceLte: null, + query: searchQuery, + sortBy: null + }; + const query = + "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"; + + return cy.sendFrontShopRequestWithQuery("SearchProducts", variables, query); } } export default Search; diff --git a/cypress/integration/products/availableForPurchaseProducts.js b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js similarity index 89% rename from cypress/integration/products/availableForPurchaseProducts.js rename to cypress/integration/products/menageProducts/availableForPurchaseProducts.js index 659bd2c7e..49026d110 100644 --- a/cypress/integration/products/availableForPurchaseProducts.js +++ b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js @@ -1,11 +1,11 @@ import faker from "faker"; -import ProductSteps from "../../steps/productSteps"; -import { urlList } from "../../url/urlList"; -import ChannelsUtils from "../../utils/channelsUtils"; -import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; -import ProductsUtils from "../../utils/productsUtils"; -import ShippingUtils from "../../utils/shippingUtils"; +import ProductSteps from "../../../steps/productSteps"; +import { urlList } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; +import ProductsUtils from "../../../utils/productsUtils"; +import ShippingUtils from "../../../utils/shippingUtils"; // describe("Products available in listings", () => { diff --git a/cypress/integration/products/publishedProducts.js b/cypress/integration/products/menageProducts/publishedProducts.js similarity index 90% rename from cypress/integration/products/publishedProducts.js rename to cypress/integration/products/menageProducts/publishedProducts.js index f6b23a7ca..046b203ec 100644 --- a/cypress/integration/products/publishedProducts.js +++ b/cypress/integration/products/menageProducts/publishedProducts.js @@ -1,10 +1,10 @@ import faker from "faker"; -import ProductSteps from "../../steps/productSteps"; -import { urlList } from "../../url/urlList"; -import ChannelsUtils from "../../utils/channelsUtils"; -import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; -import ProductsUtils from "../../utils/productsUtils"; +import ProductSteps from "../../../steps/productSteps"; +import { urlList } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; +import ProductsUtils from "../../../utils/productsUtils"; // describe("Published products", () => { diff --git a/cypress/integration/products/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js similarity index 91% rename from cypress/integration/products/visibleInListingsProducts.js rename to cypress/integration/products/menageProducts/visibleInListingsProducts.js index 9d4729740..b7af5d51c 100644 --- a/cypress/integration/products/visibleInListingsProducts.js +++ b/cypress/integration/products/menageProducts/visibleInListingsProducts.js @@ -1,10 +1,10 @@ import faker from "faker"; -import ProductSteps from "../../steps/productSteps"; -import { urlList } from "../../url/urlList"; -import ChannelsUtils from "../../utils/channelsUtils"; -import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; -import ProductsUtils from "../../utils/productsUtils"; +import ProductSteps from "../../../steps/productSteps"; +import { urlList } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; +import ProductsUtils from "../../../utils/productsUtils"; // describe("Products displayed in listings", () => { diff --git a/cypress/support/index.js b/cypress/support/index.js index f6115623a..01d098b68 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -40,15 +40,31 @@ Cypress.Commands.add("waitForGraph", operationName => { cy.wait(`@${operationName}`); }); -Cypress.Commands.add("sendRequestWithQuery", query => - cy.request({ - body: { - method: "POST", +Cypress.Commands.add("sendRequestWithQuery", query => { + const body = { + method: "POST", + query, + url: urlList.apiUri + }; + return cy.sendRequest(body, "auth"); +}); + +Cypress.Commands.add( + "sendFrontShopRequestWithQuery", + (operationName, variables, query) => { + const body = { + operationName, query, - url: urlList.apiUri - }, + variables + }; + return cy.sendRequest(body, "token"); + } +); +Cypress.Commands.add("sendRequest", (body, authorization) => + cy.request({ + body, headers: { - Authorization: `JWT ${window.sessionStorage.getItem("auth")}` + Authorization: `JWT ${window.sessionStorage.getItem(authorization)}` }, method: "POST", url: urlList.apiUri diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 1019ac4ab..5fdb8d863 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -63,6 +63,6 @@ Cypress.Commands.add("loginInShop", () => { } ] }).then(resp => { - window.localStorage.setItem("token", resp.body[0].data.tokenCreate.token); + window.sessionStorage.setItem("token", resp.body[0].data.tokenCreate.token); }); }); diff --git a/cypress/utils/frontShop/frontShopProductUtils.js b/cypress/utils/frontShop/frontShopProductUtils.js index 1b8411f23..924666ab8 100644 --- a/cypress/utils/frontShop/frontShopProductUtils.js +++ b/cypress/utils/frontShop/frontShopProductUtils.js @@ -6,23 +6,29 @@ class FrontShopProductUtils { const productDetails = new ProductDetails(); return productDetails .getProductDetails(productId, channelSlug) - .then(productDetailsResp => - productDetails.isProductExist(productDetailsResp, name) - ); + .then(productDetailsResp => { + const product = productDetailsResp.body.data.product; + return product !== null && product.name === name; + }); } isProductAvailableForPurchase(productId, channelSlug) { const productDetails = new ProductDetails(); return productDetails .getProductDetails(productId, channelSlug) - .then(productDetailsResp => - productDetails.isAvailableForPurchaseFromResp(productDetailsResp) + .then( + productDetailsResp => + productDetailsResp.body.data.product.isAvailableForPurchase ); } isProductVisibleInSearchResult(productName, channelSlug) { const search = new Search(); return search .searchInShop(productName, channelSlug) - .then(resp => search.isProductExist(resp, productName)); + .then( + resp => + resp.body.data.products.totalCount !== 0 && + resp.body.data.products.edges[0].node.name === productName + ); } } export default FrontShopProductUtils; From e8c20ae5111b1c1f8ccb511e76bf3de39937b63b Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Tue, 23 Feb 2021 13:09:58 +0100 Subject: [PATCH 12/16] tests for products --- cypress/apiRequests/Product.js | 36 +++++++--- cypress/apiRequests/utils/Utils.js | 6 ++ cypress/integration/channels.js | 24 ++++--- cypress/integration/homePage.js | 52 +++++++------- .../availableForPurchaseProducts.js | 71 ++++++++----------- .../menageProducts/publishedProducts.js | 38 +++++----- .../visibleInListingsProducts.js | 43 +++++------ cypress/steps/homePageSteps.js | 10 +-- cypress/steps/productSteps.js | 39 ++++------ cypress/support/index.js | 3 +- cypress/utils/productsUtils.js | 16 ++--- cypress/utils/shippingUtils.js | 2 +- 12 files changed, 170 insertions(+), 170 deletions(-) create mode 100644 cypress/apiRequests/utils/Utils.js diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index 16b7d2a30..69c10d7e2 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -1,4 +1,7 @@ +import Utils from "./utils/Utils"; + class Product { + utils = new Utils(); getFirstProducts(first, search) { const filter = search ? `, filter:{ @@ -23,13 +26,13 @@ class Product { .then(resp => resp.body.data.products.edges); } - updateChannelInProduct( + updateChannelInProduct({ productId, channelId, isPublished = true, isAvailableForPurchase = true, visibleInListings = true - ) { + }) { const mutation = `mutation{ productChannelListingUpdate(id:"${productId}", input:{ @@ -94,19 +97,36 @@ class Product { price = 1, costPrice = 1 ) { - const channelListings = channelId - ? `channelListings:{ + // const channelListings = channelId + // ? `channelListings:{ + // channelId:"${channelId}" + // price:"${price}" + // costPrice:"${costPrice}" + // }` + // : ""; + + const channelListings = this.utils.getValueWithDefault( + channelId, + `channelListings:{ channelId:"${channelId}" price:"${price}" costPrice:"${costPrice}" }` - : ""; - const stocks = warehouseId - ? `stocks:{ + ); + + const stocks = this.utils.getValueWithDefault( + warehouseId, + `stocks:{ warehouse:"${warehouseId}" quantity:${quantity} }` - : ""; + ); + // warehouseId + // ? `stocks:{ + // warehouse:"${warehouseId}" + // quantity:${quantity} + // }` + // : ""; const mutation = `mutation{ productVariantBulkCreate(product: "${productId}", variants: { diff --git a/cypress/apiRequests/utils/Utils.js b/cypress/apiRequests/utils/Utils.js new file mode 100644 index 000000000..cbb37b751 --- /dev/null +++ b/cypress/apiRequests/utils/Utils.js @@ -0,0 +1,6 @@ +class Utils { + getValueWithDefault(condition, value, defaultValue = "") { + return condition ? value : defaultValue; + } +} +export default Utils; diff --git a/cypress/integration/channels.js b/cypress/integration/channels.js index bfee7abae..0f64da273 100644 --- a/cypress/integration/channels.js +++ b/cypress/integration/channels.js @@ -44,10 +44,13 @@ describe("Channels", () => { it("should create new channel", () => { const randomChannel = `${channelStartsWith} ${faker.random.number()}`; - cy.visit(urlList.channels).waitForGraph("Channels"); + cy.addAliasToGraphRequest("Channels"); + cy.visit(urlList.channels); + cy.wait("@Channels"); + cy.addAliasToGraphRequest("Channel"); channelsSteps.createChannelByView(randomChannel, currency); // New channel should be visible in channels list - cy.waitForGraph("Channel") + cy.wait("@Channel") .get(ADD_CHANNEL_FORM_SELECTORS.backToChannelsList) .click() .get(CHANNELS_SELECTORS.channelsTable) @@ -62,7 +65,9 @@ describe("Channels", () => { .click(); // new channel should be visible at product availability form - cy.visit(urlList.products).waitForGraph("InitialProductFilterData"); + cy.addAliasToGraphRequest("InitialProductFilterData"); + cy.visit(urlList.products); + cy.wait("@InitialProductFilterData"); cy.get(PRODUCTS_SELECTORS.productsList) .first() .click() @@ -103,15 +108,18 @@ describe("Channels", () => { randomChannelToDelete, currency ); - cy.visit(urlList.channels).waitForGraph("Channels"); + cy.addAliasToGraphRequest("Channels"); + cy.visit(urlList.channels); + cy.wait("@Channels"); cy.get(CHANNELS_SELECTORS.channelName) .contains(randomChannelToDelete) .parentsUntil(CHANNELS_SELECTORS.channelsTable) .find("button") - .click() - .get(BUTTON_SELECTORS.submit) - .click() - .waitForGraph("Channels"); + .click(); + cy.addAliasToGraphRequest("Channels"); + cy.get(BUTTON_SELECTORS.submit).click(); + cy.wait("@Channels"); + cy.get(CHANNELS_SELECTORS.channelName) .contains(randomChannelToDelete) .should("not.exist"); diff --git a/cypress/integration/homePage.js b/cypress/integration/homePage.js index 483eb49cd..3712afd56 100644 --- a/cypress/integration/homePage.js +++ b/cypress/integration/homePage.js @@ -48,12 +48,12 @@ describe("Homepage analytics", () => { ) .then(resp => { customerId = resp.body.data.customerCreate.user.id; - shippingUtils.createShipping( - defaultChannel.id, - randomName, - addresses.plAddress, - shippingPrice - ); + shippingUtils.createShipping({ + channelId: defaultChannel.id, + name: randomName, + address: addresses.plAddress, + price: shippingPrice + }); }) .then(() => { productsUtils.createTypeAttributeAndCategoryForProduct(randomName); @@ -63,16 +63,16 @@ describe("Homepage analytics", () => { const productType = productsUtils.getProductType(); const attribute = productsUtils.getAttribute(); const category = productsUtils.getCategory(); - productsUtils.createProductInChannel( - randomName, - defaultChannel.id, - warehouse.id, - 20, - productType.id, - attribute.id, - category.id, - productPrice - ); + productsUtils.createProductInChannel({ + name: randomName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + quantityInWarehouse: 20, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + price: productPrice + }); }); }); @@ -154,16 +154,16 @@ describe("Homepage analytics", () => { const attribute = productsUtils.getAttribute(); const category = productsUtils.getCategory(); - productsOutOfStockUtils.createProductInChannel( - productOutOfStockRandomName, - defaultChannel.id, - warehouse.id, - 0, - productType.id, - attribute.id, - category.id, - productPrice - ); + productsOutOfStockUtils.createProductInChannel({ + name: productOutOfStockRandomName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + quantityInWarehouse: 0, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + price: productPrice + }); cy.get("@productsOutOfStock").then(productsOutOfStockBefore => { const allProductsOutOfStock = productsOutOfStockBefore + 1; diff --git a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js index 49026d110..b72d5aafd 100644 --- a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js +++ b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js @@ -34,12 +34,11 @@ describe("Products available in listings", () => { cy.fixture("addresses"); }) .then(addressesFixture => { - shippingUtils.createShipping( - defaultChannel, + shippingUtils.createShipping({ + channelId: defaultChannel.id, name, - addressesFixture.plAddress, - 10 - ); + address: addressesFixture.plAddress + }); }) .then(() => { warehouse = shippingUtils.getWarehouse(); @@ -59,19 +58,15 @@ describe("Products available in listings", () => { it("should update product to available for purchase", () => { const productName = `${startsWith}${faker.random.number()}`; productsUtils - .createProductInChannel( - productName, - defaultChannel.id, - warehouse.id, - 10, - productType.id, - attribute.id, - category.id, - 1, - true, - false, - true - ) + .createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + isAvailableForPurchase: false + }) .then(() => { const productUrl = `${urlList.products}${ productsUtils.getCreatedProduct().id @@ -92,33 +87,29 @@ describe("Products available in listings", () => { it("should update product to not available for purchase", () => { const productName = `${startsWith}${faker.random.number()}`; productsUtils - .createProductInChannel( - productName, - defaultChannel.id, - warehouse.id, - 10, - productType.id, - attribute.id, - category.id, - 1, - true, - true, - true - ) + .createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id + }) .then(() => { const productUrl = `${urlList.products}${ productsUtils.getCreatedProduct().id }`; productSteps.updateProductIsAvailableForPurchase(productUrl, false); - frontShopProductUtils - .isProductAvailableForPurchase( - productsUtils.getCreatedProduct().id, - defaultChannel.slug, - productName - ) - .then(isProductVisible => { - expect(isProductVisible).to.be.eq(false); - }); + }) + .then(() => { + frontShopProductUtils.isProductAvailableForPurchase( + productsUtils.getCreatedProduct().id, + defaultChannel.slug, + productName + ); + }) + .then(isProductVisible => { + expect(isProductVisible).to.be.eq(false); }); }); }); diff --git a/cypress/integration/products/menageProducts/publishedProducts.js b/cypress/integration/products/menageProducts/publishedProducts.js index 046b203ec..3c1d78f68 100644 --- a/cypress/integration/products/menageProducts/publishedProducts.js +++ b/cypress/integration/products/menageProducts/publishedProducts.js @@ -39,19 +39,15 @@ describe("Published products", () => { .getDefaultChannel() .then(channel => { defaultChannel = channel; - productsUtils.createProductInChannel( - productName, - defaultChannel.id, - null, - null, - productType.id, - attribute.id, - category.id, - 1, - false, - false, - true - ); + productsUtils.createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + isPublished: false, + isAvailableForPurchase: false + }); }) .then(() => { const product = productsUtils.getCreatedProduct(); @@ -76,15 +72,13 @@ describe("Published products", () => { .getDefaultChannel() .then(channel => { defaultChannel = channel; - productsUtils.createProductInChannel( - productName, - defaultChannel.id, - null, - null, - productType.id, - attribute.id, - category.id - ); + productsUtils.createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id + }); }) .then(() => { product = productsUtils.getCreatedProduct(); diff --git a/cypress/integration/products/menageProducts/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js index b7af5d51c..c732a7111 100644 --- a/cypress/integration/products/menageProducts/visibleInListingsProducts.js +++ b/cypress/integration/products/menageProducts/visibleInListingsProducts.js @@ -39,19 +39,15 @@ describe("Products displayed in listings", () => { .getDefaultChannel() .then(channel => { defaultChannel = channel; - productsUtils.createProductInChannel( - productName, - defaultChannel.id, - null, - null, - productType.id, - attribute.id, - category.id, - 1, - true, - false, - false - ); + productsUtils.createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + visibleInListings: false, + isAvailableForPurchase: false + }); }) .then(() => { const product = productsUtils.getCreatedProduct(); @@ -73,19 +69,14 @@ describe("Products displayed in listings", () => { .getDefaultChannel() .then(channel => { defaultChannel = channel; - productsUtils.createProductInChannel( - productName, - defaultChannel.id, - null, - null, - productType.id, - attribute.id, - category.id, - 1, - true, - false, - true - ); + productsUtils.createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + visibleInListings: true + }); }) .then(() => { const product = productsUtils.getCreatedProduct(); diff --git a/cypress/steps/homePageSteps.js b/cypress/steps/homePageSteps.js index bd53c0972..72b9c6d96 100644 --- a/cypress/steps/homePageSteps.js +++ b/cypress/steps/homePageSteps.js @@ -1,12 +1,12 @@ import { HEADER_SELECTORS } from "../elements/header/header-selectors"; class HomePageSteps { changeChannel(channelName) { - cy.get(HEADER_SELECTORS.channelSelect) - .click() - .get(HEADER_SELECTORS.channelSelectList) + cy.get(HEADER_SELECTORS.channelSelect).click(); + cy.addAliasToGraphRequest("Home"); + cy.get(HEADER_SELECTORS.channelSelectList) .contains(channelName) - .click() - .waitForGraph("Home"); + .click(); + cy.wait("@Home"); } } export default HomePageSteps; diff --git a/cypress/steps/productSteps.js b/cypress/steps/productSteps.js index 244ab8392..eec8c3736 100644 --- a/cypress/steps/productSteps.js +++ b/cypress/steps/productSteps.js @@ -1,30 +1,20 @@ import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; class ProductSteps { + valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue; + valueFalse = PRODUCTS_SELECTORS.radioButtonsValueFalse; + updateProductIsAvailableForPurchase(productUrl, isAvailableForPurchase) { - let isAvailableForPurchaseSelector; - if (isAvailableForPurchase) { - isAvailableForPurchaseSelector = PRODUCTS_SELECTORS.radioButtonsValueTrue; - } else { - isAvailableForPurchaseSelector = - PRODUCTS_SELECTORS.radioButtonsValueFalse; - } - this.updateProductMenageInChannel( - productUrl, - `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}` - ); + const isAvailableForPurchaseSelector = isAvailableForPurchase + ? this.valueTrue + : this.valueFalse; + const availableForPurchaseSelector = `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`; + this.updateProductMenageInChannel(productUrl, availableForPurchaseSelector); } updateProductPublish(productUrl, isPublished) { - let isPublishedSelector; - if (isPublished) { - isPublishedSelector = PRODUCTS_SELECTORS.radioButtonsValueTrue; - } else { - isPublishedSelector = PRODUCTS_SELECTORS.radioButtonsValueFalse; - } - this.updateProductMenageInChannel( - productUrl, - `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}` - ); + const isPublishedSelector = isPublished ? this.valueTrue : this.valueFalse; + const publishedSelector = `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}`; + this.updateProductMenageInChannel(productUrl, publishedSelector); } updateProductVisibleInListings(productUrl) { this.updateProductMenageInChannel( @@ -37,10 +27,11 @@ class ProductSteps { .get(PRODUCTS_SELECTORS.assignedChannels) .click() .get(menageSelector) + .click(); + cy.addAliasToGraphRequest("ProductChannelListingUpdate"); + cy.get(PRODUCTS_SELECTORS.saveBtn) .click() - .get(PRODUCTS_SELECTORS.saveBtn) - .click() - .waitForGraph("ProductChannelListingUpdate"); + .wait("@ProductChannelListingUpdate"); } } export default ProductSteps; diff --git a/cypress/support/index.js b/cypress/support/index.js index 01d098b68..76973c190 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -21,7 +21,7 @@ Cypress.Commands.add("clearSessionData", () => { }); }); -Cypress.Commands.add("waitForGraph", operationName => { +Cypress.Commands.add("addAliasToGraphRequest", operationName => { cy.intercept("POST", urlList.apiUri, req => { req.statusCode = 200; const requestBody = req.body; @@ -37,7 +37,6 @@ Cypress.Commands.add("waitForGraph", operationName => { } } }); - cy.wait(`@${operationName}`); }); Cypress.Commands.add("sendRequestWithQuery", query => { diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index c1c633bd1..2ac9f0ce7 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -22,28 +22,28 @@ class ProductsUtils { ).then(() => this.createVariant(this.product.id, name)); } - createProductInChannel( + createProductInChannel({ name, channelId, - warehouseId, - quantityInWarehouse, + warehouseId = null, + quantityInWarehouse = 10, productTypeId, attributeId, categoryId, - price, + price = 1, isPublished = true, isAvailableForPurchase = true, visibleInListings = true - ) { + }) { return this.createProduct(attributeId, name, productTypeId, categoryId) .then(() => - this.productRequest.updateChannelInProduct( - this.product.id, + this.productRequest.updateChannelInProduct({ + productId: this.product.id, channelId, isPublished, isAvailableForPurchase, visibleInListings - ) + }) ) .then(() => { this.createVariant( diff --git a/cypress/utils/shippingUtils.js b/cypress/utils/shippingUtils.js index 1fb352907..75eb7501f 100644 --- a/cypress/utils/shippingUtils.js +++ b/cypress/utils/shippingUtils.js @@ -8,7 +8,7 @@ class ShippingUtils { shippingZone; warehouse; - createShipping(channelId, name, address, price) { + createShipping({ channelId, name, address, price = 1 }) { return this.createShippingZone(name, address.country) .then(() => this.createWarehouse(name, this.shippingZone.id, address)) .then(() => this.createShippingRate(name, this.shippingZone.id)) From ac7f1b19b6997946f080f188c8d5f0efba81459c Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Tue, 23 Feb 2021 15:30:52 +0100 Subject: [PATCH 13/16] tests for products - fix storefront req --- cypress/apiRequests/Product.js | 14 --------- .../apiRequests/frontShop/ProductDetails.js | 12 -------- cypress/apiRequests/frontShop/Search.js | 18 ------------ .../apiRequests/storeFront/ProductDetails.js | 18 ++++++++++++ cypress/apiRequests/storeFront/Search.js | 20 +++++++++++++ .../availableForPurchaseProducts.js | 14 ++++----- .../menageProducts/publishedProducts.js | 10 +++---- .../visibleInListingsProducts.js | 10 +++---- cypress/support/index.js | 29 +++++-------------- cypress/url/urlList.js | 1 + .../storeFrontProductUtils.js} | 8 ++--- 11 files changed, 68 insertions(+), 86 deletions(-) delete mode 100644 cypress/apiRequests/frontShop/ProductDetails.js delete mode 100644 cypress/apiRequests/frontShop/Search.js create mode 100644 cypress/apiRequests/storeFront/ProductDetails.js create mode 100644 cypress/apiRequests/storeFront/Search.js rename cypress/utils/{frontShop/frontShopProductUtils.js => storeFront/storeFrontProductUtils.js} (82%) diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index 69c10d7e2..8aa6b0ce8 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -97,14 +97,6 @@ class Product { price = 1, costPrice = 1 ) { - // const channelListings = channelId - // ? `channelListings:{ - // channelId:"${channelId}" - // price:"${price}" - // costPrice:"${costPrice}" - // }` - // : ""; - const channelListings = this.utils.getValueWithDefault( channelId, `channelListings:{ @@ -121,12 +113,6 @@ class Product { quantity:${quantity} }` ); - // warehouseId - // ? `stocks:{ - // warehouse:"${warehouseId}" - // quantity:${quantity} - // }` - // : ""; const mutation = `mutation{ productVariantBulkCreate(product: "${productId}", variants: { diff --git a/cypress/apiRequests/frontShop/ProductDetails.js b/cypress/apiRequests/frontShop/ProductDetails.js deleted file mode 100644 index def96af89..000000000 --- a/cypress/apiRequests/frontShop/ProductDetails.js +++ /dev/null @@ -1,12 +0,0 @@ -class ProductDetails { - getProductDetails(productId, channelId) { - const variables = { - channel: channelId, - id: productId - }; - const query = - "fragment BasicProductFields on Product {\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n __typename\n}\n\nfragment SelectedAttributeFields on SelectedAttribute {\n attribute {\n id\n name\n __typename\n }\n values {\n id\n name\n __typename\n }\n __typename\n}\n\nfragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductVariantFields on ProductVariant {\n id\n sku\n name\n quantityAvailable(countryCode: $countryCode)\n images {\n id\n url\n alt\n __typename\n }\n pricing {\n onSale\n priceUndiscounted {\n ...Price\n __typename\n }\n price {\n ...Price\n __typename\n }\n __typename\n }\n attributes(variantSelection: VARIANT_SELECTION) {\n attribute {\n id\n name\n slug\n __typename\n }\n values {\n id\n name\n value: name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery ProductDetails($id: ID!, $channel: String, $countryCode: CountryCode) {\n product(id: $id, channel: $channel) {\n ...BasicProductFields\n ...ProductPricingField\n description\n category {\n id\n name\n products(first: 3, channel: $channel) {\n edges {\n node {\n ...BasicProductFields\n ...ProductPricingField\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n images {\n id\n alt\n url\n __typename\n }\n attributes {\n ...SelectedAttributeFields\n __typename\n }\n variants {\n ...ProductVariantFields\n __typename\n }\n seoDescription\n seoTitle\n isAvailable\n isAvailableForPurchase\n availableForPurchase\n __typename\n }\n}\n"; - return cy.sendFrontShopRequestWithQuery("ProductDetails", variables, query); - } -} -export default ProductDetails; diff --git a/cypress/apiRequests/frontShop/Search.js b/cypress/apiRequests/frontShop/Search.js deleted file mode 100644 index 62feb5424..000000000 --- a/cypress/apiRequests/frontShop/Search.js +++ /dev/null @@ -1,18 +0,0 @@ -class Search { - searchInShop(searchQuery) { - const variables = { - attributes: {}, - channel: "default-channel", - pageSize: 6, - priceGte: null, - priceLte: null, - query: searchQuery, - sortBy: null - }; - const query = - "fragment Price on TaxedMoney {\n gross {\n amount\n currency\n __typename\n }\n net {\n amount\n currency\n __typename\n }\n __typename\n}\n\nfragment ProductPricingField on Product {\n pricing {\n onSale\n priceRangeUndiscounted {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n priceRange {\n start {\n ...Price\n __typename\n }\n stop {\n ...Price\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nquery SearchProducts($query: String!, $channel: String!, $attributes: [AttributeInput], $pageSize: Int, $sortBy: ProductOrder, $after: String) {\n products(channel: $channel, filter: {search: $query, attributes: $attributes}, first: $pageSize, sortBy: $sortBy, after: $after) {\n totalCount\n edges {\n node {\n ...ProductPricingField\n id\n name\n thumbnail {\n url\n alt\n __typename\n }\n thumbnail2x: thumbnail(size: 510) {\n url\n __typename\n }\n category {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor\n hasNextPage\n __typename\n }\n __typename\n }\n attributes(filter: {filterableInStorefront: true}, first: 100) {\n edges {\n node {\n id\n name\n slug\n values {\n id\n name\n slug\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"; - - return cy.sendFrontShopRequestWithQuery("SearchProducts", variables, query); - } -} -export default Search; diff --git a/cypress/apiRequests/storeFront/ProductDetails.js b/cypress/apiRequests/storeFront/ProductDetails.js new file mode 100644 index 000000000..9ad18c5fc --- /dev/null +++ b/cypress/apiRequests/storeFront/ProductDetails.js @@ -0,0 +1,18 @@ +class ProductDetails { + getProductDetails(productId, channelId) { + const query = `fragment BasicProductFields on Product { + id + name + } + query ProductDetails{ + product(id: "${productId}", channel: "${channelId}") { + ...BasicProductFields + isAvailable + isAvailableForPurchase + availableForPurchase + } + }`; + return cy.sendFrontShopRequestWithQuery(query); + } +} +export default ProductDetails; diff --git a/cypress/apiRequests/storeFront/Search.js b/cypress/apiRequests/storeFront/Search.js new file mode 100644 index 000000000..107be2560 --- /dev/null +++ b/cypress/apiRequests/storeFront/Search.js @@ -0,0 +1,20 @@ +class Search { + searchInShop(searchQuery) { + const query = `query SearchProducts { + products(channel: "default-channel", filter:{ + search: "${searchQuery}" + }, first:10){ + totalCount + edges{ + node{ + id + name + } + } + } + }`; + + return cy.sendFrontShopRequestWithQuery(query); + } +} +export default Search; diff --git a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js index b72d5aafd..9c4a3c468 100644 --- a/cypress/integration/products/menageProducts/availableForPurchaseProducts.js +++ b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js @@ -1,11 +1,11 @@ import faker from "faker"; import ProductSteps from "../../../steps/productSteps"; -import { urlList } from "../../../url/urlList"; +import { productDetailsUrl } from "../../../url/urlList"; import ChannelsUtils from "../../../utils/channelsUtils"; -import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../../utils/productsUtils"; import ShippingUtils from "../../../utils/shippingUtils"; +import StoreFrontProductUtils from "../../../utils/storeFront/storeFrontProductUtils"; // describe("Products available in listings", () => { @@ -13,7 +13,7 @@ describe("Products available in listings", () => { const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); const productSteps = new ProductSteps(); - const frontShopProductUtils = new FrontShopProductUtils(); + const frontShopProductUtils = new StoreFrontProductUtils(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; let productType; @@ -68,9 +68,9 @@ describe("Products available in listings", () => { isAvailableForPurchase: false }) .then(() => { - const productUrl = `${urlList.products}${ + const productUrl = productDetailsUrl( productsUtils.getCreatedProduct().id - }`; + ); productSteps.updateProductIsAvailableForPurchase(productUrl, true); }) .then(() => { @@ -96,9 +96,9 @@ describe("Products available in listings", () => { categoryId: category.id }) .then(() => { - const productUrl = `${urlList.products}${ + const productUrl = productDetailsUrl( productsUtils.getCreatedProduct().id - }`; + ); productSteps.updateProductIsAvailableForPurchase(productUrl, false); }) .then(() => { diff --git a/cypress/integration/products/menageProducts/publishedProducts.js b/cypress/integration/products/menageProducts/publishedProducts.js index 3c1d78f68..48015293f 100644 --- a/cypress/integration/products/menageProducts/publishedProducts.js +++ b/cypress/integration/products/menageProducts/publishedProducts.js @@ -1,17 +1,17 @@ import faker from "faker"; import ProductSteps from "../../../steps/productSteps"; -import { urlList } from "../../../url/urlList"; +import { productDetailsUrl } from "../../../url/urlList"; import ChannelsUtils from "../../../utils/channelsUtils"; -import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../../utils/productsUtils"; +import StoreFrontProductUtils from "../../../utils/storeFront/storeFrontProductUtils"; // describe("Published products", () => { const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); const productSteps = new ProductSteps(); - const frontShopProductUtils = new FrontShopProductUtils(); + const frontShopProductUtils = new StoreFrontProductUtils(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; @@ -51,7 +51,7 @@ describe("Published products", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductPublish(productUrl, true); frontShopProductUtils.isProductVisible( product.id, @@ -82,7 +82,7 @@ describe("Published products", () => { }) .then(() => { product = productsUtils.getCreatedProduct(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductPublish(productUrl, false); frontShopProductUtils.isProductVisible( product.id, diff --git a/cypress/integration/products/menageProducts/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js index c732a7111..19463c5ce 100644 --- a/cypress/integration/products/menageProducts/visibleInListingsProducts.js +++ b/cypress/integration/products/menageProducts/visibleInListingsProducts.js @@ -1,17 +1,17 @@ import faker from "faker"; import ProductSteps from "../../../steps/productSteps"; -import { urlList } from "../../../url/urlList"; +import { productDetailsUrl } from "../../../url/urlList"; import ChannelsUtils from "../../../utils/channelsUtils"; -import FrontShopProductUtils from "../../../utils/frontShop/frontShopProductUtils"; import ProductsUtils from "../../../utils/productsUtils"; +import StoreFrontProductUtils from "../../../utils/storeFront/storeFrontProductUtils"; // describe("Products displayed in listings", () => { const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); const productSteps = new ProductSteps(); - const frontShopProductUtils = new FrontShopProductUtils(); + const frontShopProductUtils = new StoreFrontProductUtils(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; @@ -51,7 +51,7 @@ describe("Products displayed in listings", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils.isProductVisibleInSearchResult( productName, @@ -80,7 +80,7 @@ describe("Products displayed in listings", () => { }) .then(() => { const product = productsUtils.getCreatedProduct(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils .isProductVisibleInSearchResult(productName, defaultChannel.slug) diff --git a/cypress/support/index.js b/cypress/support/index.js index 76973c190..2d9dad3db 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -39,29 +39,16 @@ Cypress.Commands.add("addAliasToGraphRequest", operationName => { }); }); -Cypress.Commands.add("sendRequestWithQuery", query => { - const body = { - method: "POST", - query, - url: urlList.apiUri - }; - return cy.sendRequest(body, "auth"); -}); - -Cypress.Commands.add( - "sendFrontShopRequestWithQuery", - (operationName, variables, query) => { - const body = { - operationName, - query, - variables - }; - return cy.sendRequest(body, "token"); - } +Cypress.Commands.add("sendFrontShopRequestWithQuery", query => + cy.sendRequestWithQuery(query, "token") ); -Cypress.Commands.add("sendRequest", (body, authorization) => +Cypress.Commands.add("sendRequestWithQuery", (query, authorization = "auth") => cy.request({ - body, + body: { + method: "POST", + query, + url: urlList.apiUri + }, headers: { Authorization: `JWT ${window.sessionStorage.getItem(authorization)}` }, diff --git a/cypress/url/urlList.js b/cypress/url/urlList.js index 2ceb592c5..c469ba6e6 100644 --- a/cypress/url/urlList.js +++ b/cypress/url/urlList.js @@ -7,3 +7,4 @@ export const urlList = { products: "products/", warehouses: "warehouses/" }; +export const productDetailsUrl = productId => `${urlList.products}${productId}`; diff --git a/cypress/utils/frontShop/frontShopProductUtils.js b/cypress/utils/storeFront/storeFrontProductUtils.js similarity index 82% rename from cypress/utils/frontShop/frontShopProductUtils.js rename to cypress/utils/storeFront/storeFrontProductUtils.js index 924666ab8..676075f19 100644 --- a/cypress/utils/frontShop/frontShopProductUtils.js +++ b/cypress/utils/storeFront/storeFrontProductUtils.js @@ -1,7 +1,7 @@ -import ProductDetails from "../../apiRequests/frontShop/ProductDetails"; -import Search from "../../apiRequests/frontShop/Search"; +import ProductDetails from "../../apiRequests/storeFront/ProductDetails"; +import Search from "../../apiRequests/storeFront/Search"; -class FrontShopProductUtils { +class StoreFrontProductUtils { isProductVisible(productId, channelSlug, name) { const productDetails = new ProductDetails(); return productDetails @@ -31,4 +31,4 @@ class FrontShopProductUtils { ); } } -export default FrontShopProductUtils; +export default StoreFrontProductUtils; From f1c86f0a4d084de32cc412b22d12afb721ef7876 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Wed, 24 Feb 2021 10:27:57 +0100 Subject: [PATCH 14/16] tests for products -change login cy command --- cypress/support/user/index.js | 60 ++++++++++------------------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 5fdb8d863..11f606ea8 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -1,5 +1,4 @@ import { LOGIN_SELECTORS } from "../../elements/account/login-selectors"; -import { urlList } from "../../url/urlList"; Cypress.Commands.add("loginUser", () => cy @@ -12,57 +11,32 @@ Cypress.Commands.add("loginUser", () => ); Cypress.Commands.add("loginUserViaRequest", () => { - const logInMutationQuery = `mutation TokenAuth($email: String!, $password: String!) { - tokenCreate(email: $email, password: $password) { + cy.sendLoginRequest().then(resp => { + window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); + }); +}); + +Cypress.Commands.add("loginInShop", () => { + cy.sendLoginRequest("token").then(resp => { + window.sessionStorage.setItem("token", resp.body[0].data.tokenCreate.token); + }); +}); + +Cypress.Commands.add("sendLoginRequest", (authorization = "auth") => { + const mutation = `mutation TokenAuth{ + tokenCreate(email: "${Cypress.env("USER_NAME")}", password: "${Cypress.env( + "USER_PASSWORD" + )}") { token errors: accountErrors { code field message - __typename } user { id - __typename } - __typename } }`; - - return cy - .request({ - body: { - operationName: "TokenAuth", - query: logInMutationQuery, - variables: { - email: Cypress.env("USER_NAME"), - password: Cypress.env("USER_PASSWORD") - } - }, - method: "POST", - url: urlList.apiUri - }) - .then(resp => { - window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); - }); -}); - -Cypress.Commands.add("loginInShop", () => { - cy.request({ - method: "POST", - url: Cypress.env("API_URI"), - body: [ - { - operationName: "TokenAuth", - variables: { - email: Cypress.env("USER_NAME"), - password: Cypress.env("USER_PASSWORD") - }, - query: - "mutation TokenAuth($email: String!, $password: String!) {\n tokenCreate(email: $email, password: $password) {\n token\n errors: accountErrors {\n code\n field\n message\n __typename\n }\n user {\n id\n __typename\n }\n __typename\n }\n}\n" - } - ] - }).then(resp => { - window.sessionStorage.setItem("token", resp.body[0].data.tokenCreate.token); - }); + return cy.sendRequestWithQuery(mutation, authorization); }); From 3a90d9bdc2332639fcc44c88b847f6d7de915b3f Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Wed, 24 Feb 2021 12:19:17 +0100 Subject: [PATCH 15/16] fix login commands --- cypress/support/user/index.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 11f606ea8..6e6fa8813 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -10,19 +10,17 @@ Cypress.Commands.add("loginUser", () => .click() ); -Cypress.Commands.add("loginUserViaRequest", () => { - cy.sendLoginRequest().then(resp => { - window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); - }); -}); +// Cypress.Commands.add("loginUserViaRequest", () => { +// cy.sendLoginRequest().then(resp => { +// window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); +// }); +// }); Cypress.Commands.add("loginInShop", () => { - cy.sendLoginRequest("token").then(resp => { - window.sessionStorage.setItem("token", resp.body[0].data.tokenCreate.token); - }); + cy.loginUserViaRequest("token"); }); -Cypress.Commands.add("sendLoginRequest", (authorization = "auth") => { +Cypress.Commands.add("loginUserViaRequest", (authorization = "auth") => { const mutation = `mutation TokenAuth{ tokenCreate(email: "${Cypress.env("USER_NAME")}", password: "${Cypress.env( "USER_PASSWORD" @@ -38,5 +36,10 @@ Cypress.Commands.add("sendLoginRequest", (authorization = "auth") => { } } }`; - return cy.sendRequestWithQuery(mutation, authorization); + return cy.sendRequestWithQuery(mutation, authorization).then(resp => { + window.sessionStorage.setItem( + authorization, + resp.body.data.tokenCreate.token + ); + }); }); From 095a5522d38154565682ace5d55fdb547d846380 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Wed, 24 Feb 2021 12:21:06 +0100 Subject: [PATCH 16/16] fix login commands --- cypress/support/user/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 6e6fa8813..33fcc4adc 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -10,12 +10,6 @@ Cypress.Commands.add("loginUser", () => .click() ); -// Cypress.Commands.add("loginUserViaRequest", () => { -// cy.sendLoginRequest().then(resp => { -// window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token); -// }); -// }); - Cypress.Commands.add("loginInShop", () => { cy.loginUserViaRequest("token"); });