diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index 3e1763da0..e0eda23c9 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,22 @@ class Product { price = 1, costPrice = 1 ) { - const channelListings = channelId - ? `channelListings:{ + 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} }` - : ""; + ); 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 bb6e77c95..000000000 --- a/cypress/apiRequests/frontShop/ProductDetails.js +++ /dev/null @@ -1,32 +0,0 @@ -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 deleted file mode 100644 index 644a698ad..000000000 --- a/cypress/apiRequests/frontShop/Search.js +++ /dev/null @@ -1,34 +0,0 @@ -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/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/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/availableForPurchaseProducts.js b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js similarity index 58% rename from cypress/integration/products/availableForPurchaseProducts.js rename to cypress/integration/products/menageProducts/availableForPurchaseProducts.js index 659bd2c7e..9c4a3c468 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 { productDetailsUrl } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +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; @@ -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,23 +58,19 @@ 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}${ + const productUrl = productDetailsUrl( productsUtils.getCreatedProduct().id - }`; + ); productSteps.updateProductIsAvailableForPurchase(productUrl, true); }) .then(() => { @@ -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}${ + const productUrl = productDetailsUrl( 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/publishedProducts.js b/cypress/integration/products/menageProducts/publishedProducts.js similarity index 69% rename from cypress/integration/products/publishedProducts.js rename to cypress/integration/products/menageProducts/publishedProducts.js index f6b23a7ca..48015293f 100644 --- a/cypress/integration/products/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 ChannelsUtils from "../../utils/channelsUtils"; -import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; -import ProductsUtils from "../../utils/productsUtils"; +import ProductSteps from "../../../steps/productSteps"; +import { productDetailsUrl } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +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()}`; @@ -39,23 +39,19 @@ 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(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductPublish(productUrl, true); frontShopProductUtils.isProductVisible( product.id, @@ -76,19 +72,17 @@ 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(); - 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/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js similarity index 68% rename from cypress/integration/products/visibleInListingsProducts.js rename to cypress/integration/products/menageProducts/visibleInListingsProducts.js index 9d4729740..19463c5ce 100644 --- a/cypress/integration/products/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 ChannelsUtils from "../../utils/channelsUtils"; -import FrontShopProductUtils from "../../utils/frontShop/frontShopProductUtils"; -import ProductsUtils from "../../utils/productsUtils"; +import ProductSteps from "../../../steps/productSteps"; +import { productDetailsUrl } from "../../../url/urlList"; +import ChannelsUtils from "../../../utils/channelsUtils"; +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()}`; @@ -39,23 +39,19 @@ 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(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils.isProductVisibleInSearchResult( productName, @@ -73,23 +69,18 @@ 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(); - const productUrl = `${urlList.products}${product.id}`; + const productUrl = productDetailsUrl(product.id); productSteps.updateProductVisibleInListings(productUrl); frontShopProductUtils .isProductVisibleInSearchResult(productName, defaultChannel.slug) diff --git a/cypress/integration/products/productsVariants.js b/cypress/integration/products/productsVariants.js new file mode 100644 index 000000000..f6f024f51 --- /dev/null +++ b/cypress/integration/products/productsVariants.js @@ -0,0 +1,200 @@ +import faker from "faker"; +import { visit } from "graphql"; + +import Channels from "../../apiRequests/Channels"; +import Product from "../../apiRequests/Product"; +import ProductDetails from "../../apiRequests/storeFront/ProductDetails"; +import VariantsSteps from "../../steps/products/VariantsSteps"; +import { urlList } from "../../url/urlList"; +import ChannelsUtils from "../../utils/channelsUtils"; +import ProductsUtils from "../../utils/productsUtils"; +import ShippingUtils from "../../utils/shippingUtils"; +import StoreFrontProductUtils from "../../utils/storeFront/storeFrontProductUtils"; + +// +describe("creating variants", () => { + const startsWith = "Cy-"; + + const productUtils = new ProductsUtils(); + const channelsUtils = new ChannelsUtils(); + const shippingUtils = new ShippingUtils(); + const storeFrontProductUtils = new StoreFrontProductUtils(); + const product = new Product(); + const channels = new Channels(); + const productDetails = new ProductDetails(); + + const variantsSteps = new VariantsSteps(); + + let defaultChannel; + let warehouse; + let attribute; + let productType; + let category; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + shippingUtils.deleteShipping(startsWith); + productUtils.deleteProperProducts(startsWith); + + const name = `${startsWith}${faker.random.number()}`; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + cy.fixture("addresses"); + }) + .then(fixtureAddresses => + shippingUtils.createShipping({ + channelId: defaultChannel.id, + name, + address: fixtureAddresses.plAddress + }) + ) + .then(() => (warehouse = shippingUtils.getWarehouse())); + + productUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + attribute = productUtils.getAttribute(); + productType = productUtils.getProductType(); + category = productUtils.getCategory(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should create variant visible on frontend", () => { + const name = `${startsWith}${faker.random.number()}`; + const price = 10; + let createdProduct; + + product + .createProduct(attribute.id, name, productType.id, category.id) + .then(resp => { + createdProduct = resp.body.data.productCreate.product; + product.updateChannelInProduct({ + productId: createdProduct.id, + channelId: defaultChannel.id + }); + cy.visit(`${urlList.products}${createdProduct.id}`); + variantsSteps.createFirstVariant(name, warehouse.id, price); + storeFrontProductUtils.getProductVariants( + createdProduct.id, + defaultChannel.slug + ); + // productDetails + // .getProductDetails(createdProduct.id, defaultChannel.slug) + }) + .then(variants => { + // expect(productDetailsResp.body[0].data.product.name).to.equal(name); + expect(variants[0].name).to.equal(name); + expect(variants[0].pricing.price.gross.amount).to.equal(price); + // expect( + // productDetailsResp.body[0].data.product.variants[0].pricing.price + // .gross.amount + // ).to.equal(price); + }); + }); + it("should create several variants", () => { + const name = `${startsWith}${faker.random.number()}`; + const secondVariantSku = `${startsWith}${faker.random.number()}`; + const variantsPrice = 5; + let createdProduct; + + productUtils + .createProductInChannel({ + name, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + productTypeId: productType.id, + categoryId: category.id, + price: variantsPrice + }) + .then(() => { + createdProduct = productUtils.getCreatedProduct(); + cy.visit(`${urlList.products}${createdProduct.id}`); + variantsSteps.createVariant( + secondVariantSku, + warehouse.name, + variantsPrice + ); + }) + .then( + () => + storeFrontProductUtils.getProductVariants( + createdProduct.id, + defaultChannel.slug + ) + // productDetails.getProductDetails(createdProduct.id, defaultChannel.slug) + ) + .then(variants => { + expect(variants).to.have.length(2); + expect(variants[0].pricing.price.gross.amount).to.equal(variantsPrice); + expect(variants[1].pricing.price.gross.amount).to.equal(variantsPrice); + // expect(productDetailsResp.body[0].data.product.name).to.equal(name); + // expect(productDetailsResp.body[0].data.product.variants).to.have.length( + // 2 + // ); + // expect( + // productDetailsResp.body[0].data.product.variants[0].pricing.price + // .gross.amount + // ).to.equal(variantsPrice); + // expect( + // productDetailsResp.body[0].data.product.variants[1].pricing.price + // .gross.amount + // ).to.equal(variantsPrice); + }); + }); + it("should create variant for many channels", () => { + const name = `${startsWith}${faker.random.number()}`; + const variantsPrice = 10; + let newChannel; + let createdProduct; + channels + .createChannel(true, name, name, "PLN") + .then(resp => { + newChannel = resp.body.data.channelCreate.channel; + productUtils.createProduct( + attribute.id, + name, + productType.id, + category.id + ); + }) + .then(() => { + createdProduct = productUtils.getCreatedProduct(); + product.updateChannelInProduct({ + productId: createdProduct.id, + channelId: defaultChannel.id + }); + }) + .then(() => { + product.updateChannelInProduct({ + productId: createdProduct.id, + channelId: newChannel.id + }); + }) + .then(() => { + cy.visit(`${urlList.products}${createdProduct.id}`); + variantsSteps.createFirstVariant(name, warehouse.id, variantsPrice); + // productDetails.getProductDetails(product.id, defaultChannel.slug); + storeFrontProductUtils.getProductVariants( + product.id, + defaultChannel.slug + ); + }) + .then(variants => { + expect(variants[0].pricing.price.gross.amount).to.equal(variantsPrice); + }) + .then(() => { + storeFrontProductUtils.getProductVariants( + product.id, + defaultChannel.slug + ); + // productDetails.getProductDetails(product.id, newChannel.slug); + }) + .then(variants => { + expect(variants[0].pricing.price.gross.amount).to.equal(variantsPrice); + }); + }); +}); diff --git a/cypress/integration/productsVariants.js b/cypress/integration/productsVariants.js deleted file mode 100644 index a8b7d98e6..000000000 --- a/cypress/integration/productsVariants.js +++ /dev/null @@ -1,194 +0,0 @@ -import faker from "faker"; -import { visit } from "graphql"; - -import Channels from "../apiRequests/Channels"; -import ProductDetails from "../apiRequests/frontShop/ProductDetails"; -import Product from "../apiRequests/Product"; -import VariantsSteps from "../steps/products/VariantsSteps"; -import { urlList } from "../url/urlList"; -import ChannelsUtils from "../utils/channelsUtils"; -import ProductsUtils from "../utils/productsUtils"; -import ShippingUtils from "../utils/shippingUtils"; - -// -describe("creating variants", () => { - const startsWith = "Cy-"; - - const productUtils = new ProductsUtils(); - const channelsUtils = new ChannelsUtils(); - const shippingUtils = new ShippingUtils(); - const product = new Product(); - const channels = new Channels(); - const productDetails = new ProductDetails(); - - const variantsSteps = new VariantsSteps(); - - let defaultChannel; - let warehouse; - let attribute; - let productType; - let category; - - before(() => { - cy.clearSessionData().loginUserViaRequest(); - shippingUtils.deleteShipping(startsWith); - productUtils.deleteProperProducts(startsWith); - - const name = `${startsWith}${faker.random.number()}`; - channelsUtils - .getDefaultChannel() - .then(channel => { - defaultChannel = channel; - cy.fixture("addresses"); - }) - .then(fixtureAddresses => - shippingUtils.createShipping( - defaultChannel.id, - name, - fixtureAddresses.plAddress, - 10 - ) - ) - .then(() => (warehouse = shippingUtils.getWarehouse())); - - productUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { - attribute = productUtils.getAttribute(); - productType = productUtils.getProductType(); - category = productUtils.getCategory(); - }); - }); - - beforeEach(() => { - cy.clearSessionData().loginUserViaRequest(); - }); - - it("should create variant visible on frontend", () => { - const name = `${startsWith}${faker.random.number()}`; - const price = 10; - let createdProduct; - - product - .createProduct(attribute.id, name, productType.id, category.id) - .then(resp => { - createdProduct = resp.body.data.productCreate.product; - product.updateChannelInProduct( - createdProduct.id, - defaultChannel.id, - true, - true, - true - ); - cy.visit(`${urlList.products}${createdProduct.id}`); - variantsSteps.createFirstVariant(name, warehouse.id, price); - productDetails - .getProductDetails(createdProduct.id, defaultChannel.slug) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal(name); - expect( - productDetailsResp.body[0].data.product.variants[0].pricing.price - .gross.amount - ).to.equal(price); - }); - }); - }); - it("should create several variants", () => { - const name = `${startsWith}${faker.random.number()}`; - const secondVariantSku = `${startsWith}${faker.random.number()}`; - const variantsPrice = 5; - let createdProduct; - productUtils - .createProductInChannel( - name, - defaultChannel.id, - warehouse.id, - 1, - productType.id, - attribute.id, - category.id, - variantsPrice - ) - .then(() => { - createdProduct = productUtils.getCreatedProduct(); - cy.visit(`${urlList.products}${createdProduct.id}`); - variantsSteps.createVariant( - secondVariantSku, - warehouse.name, - variantsPrice - ); - }) - .then(() => - productDetails.getProductDetails(createdProduct.id, defaultChannel.slug) - ) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal(name); - expect(productDetailsResp.body[0].data.product.variants).to.have.length( - 2 - ); - expect( - productDetailsResp.body[0].data.product.variants[0].pricing.price - .gross.amount - ).to.equal(variantsPrice); - expect( - productDetailsResp.body[0].data.product.variants[1].pricing.price - .gross.amount - ).to.equal(variantsPrice); - }); - }); - it("should create variant for many channels", () => { - const name = `${startsWith}${faker.random.number()}`; - let newChannel; - let createdProduct; - channels - .createChannel(true, name, name, "PLN") - .then(resp => { - newChannel = resp.body.data.channelCreate.channel; - productUtils.createProduct( - attribute.id, - name, - productType.id, - category.id - ); - }) - .then(() => { - createdProduct = productUtils.getCreatedProduct(); - product.updateChannelInProduct( - createdProduct.id, - defaultChannel.id, - true, - true, - true - ); - }) - .then(() => { - product.updateChannelInProduct( - createdProduct.id, - newChannel.id, - true, - true, - true - ); - }) - .then(() => { - cy.visit(`${urlList.products}${createdProduct.id}`); - variantsSteps.createFirstVariant(warehouse.id); - productDetails.getProductDetails(product.id, defaultChannel.slug); - }) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal(name); - expect( - productDetailsResp.body[0].data.product.variants[0].pricing.price - .gross.amount - ).to.equal(10); - }) - .then(() => { - productDetails.getProductDetails(product.id, newChannel.slug); - }) - .then(productDetailsResp => { - expect(productDetailsResp.body[0].data.product.name).to.equal(name); - expect( - productDetailsResp.body[0].data.product.variants[0].pricing.price - .gross.amount - ).to.equal(10); - }); - }); -}); 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/steps/products/VariantsSteps.js b/cypress/steps/products/VariantsSteps.js index 98a20bb69..7a71c657b 100644 --- a/cypress/steps/products/VariantsSteps.js +++ b/cypress/steps/products/VariantsSteps.js @@ -8,16 +8,20 @@ class VariantsSteps { .first() .click() .get(VARIANTS_SELECTORS.nextButton) - .click() - .get(VARIANTS_SELECTORS.priceInput) - .forEach(priceInput => cy.priceInput.type(price)); + .click(); + const priceInput = cy.get(VARIANTS_SELECTORS.priceInput); + if (Array.isArray(priceInput)) { + priceInput.forEach(input => input.type(price)); + } else { + priceInput.type(price); + } cy.get(`[name*='${warehouseId}']`) .click() .get(VARIANTS_SELECTORS.nextButton) .click() .get(VARIANTS_SELECTORS.skuInput) .type(sku); - cy.waitForGraph("ProductVariantBulkCreate"); + cy.addAliasToGraphRequest("ProductVariantBulkCreate"); cy.get(VARIANTS_SELECTORS.nextButton).click(); cy.wait("@ProductVariantBulkCreate"); } @@ -38,7 +42,7 @@ class VariantsSteps { .get(VARIANTS_SELECTORS.addWarehouseButton) .click(); cy.contains(VARIANTS_SELECTORS.warehouseOption, warehouseName).click(); - cy.waitForGraph("VariantCreate"); + cy.addAliasToGraphRequest("VariantCreate"); cy.get(VARIANTS_SELECTORS.saveButton).click(); cy.wait("@VariantCreate"); } diff --git a/cypress/support/index.js b/cypress/support/index.js index 3c72c2f3f..2d9dad3db 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; @@ -39,7 +39,10 @@ Cypress.Commands.add("waitForGraph", operationName => { }); }); -Cypress.Commands.add("sendRequestWithQuery", query => +Cypress.Commands.add("sendFrontShopRequestWithQuery", query => + cy.sendRequestWithQuery(query, "token") +); +Cypress.Commands.add("sendRequestWithQuery", (query, authorization = "auth") => cy.request({ body: { method: "POST", @@ -47,7 +50,7 @@ Cypress.Commands.add("sendRequestWithQuery", query => url: urlList.apiUri }, 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..33fcc4adc 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 @@ -11,58 +10,30 @@ Cypress.Commands.add("loginUser", () => .click() ); -Cypress.Commands.add("loginUserViaRequest", () => { - const logInMutationQuery = `mutation TokenAuth($email: String!, $password: String!) { - tokenCreate(email: $email, password: $password) { +Cypress.Commands.add("loginInShop", () => { + cy.loginUserViaRequest("token"); +}); + +Cypress.Commands.add("loginUserViaRequest", (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.localStorage.setItem("token", resp.body[0].data.tokenCreate.token); + return cy.sendRequestWithQuery(mutation, authorization).then(resp => { + window.sessionStorage.setItem( + authorization, + resp.body.data.tokenCreate.token + ); }); }); 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/frontShop/frontShopProductUtils.js deleted file mode 100644 index 1b8411f23..000000000 --- a/cypress/utils/frontShop/frontShopProductUtils.js +++ /dev/null @@ -1,28 +0,0 @@ -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; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index 94948cdb5..36c49c080 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -22,28 +22,28 @@ class ProductsUtils { ).then(() => this.createVariant(this.product.id, name, attributeId)); } - 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)) diff --git a/cypress/utils/storeFront/storeFrontProductUtils.js b/cypress/utils/storeFront/storeFrontProductUtils.js new file mode 100644 index 000000000..3e1935926 --- /dev/null +++ b/cypress/utils/storeFront/storeFrontProductUtils.js @@ -0,0 +1,39 @@ +import ProductDetails from "../../apiRequests/storeFront/ProductDetails"; +import Search from "../../apiRequests/storeFront/Search"; + +class StoreFrontProductUtils { + productDetails = new ProductDetails(); + + isProductVisible(productId, channelSlug, name) { + return this.productDetails + .getProductDetails(productId, channelSlug) + .then(productDetailsResp => { + const product = productDetailsResp.body.data.product; + return product !== null && product.name === name; + }); + } + isProductAvailableForPurchase(productId, channelSlug) { + return this.productDetails + .getProductDetails(productId, channelSlug) + .then( + productDetailsResp => + productDetailsResp.body.data.product.isAvailableForPurchase + ); + } + isProductVisibleInSearchResult(productName, channelSlug) { + const search = new Search(); + return search + .searchInShop(productName, channelSlug) + .then( + resp => + resp.body.data.products.totalCount !== 0 && + resp.body.data.products.edges[0].node.name === productName + ); + } + getProductVariants(productId, channelSlug) { + return this.productDetails + .getProductDetails(productId, channelSlug) + .then(resp => resp.body.data.product.variants.edges); + } +} +export default StoreFrontProductUtils;