diff --git a/cypress/apiRequests/Attribute.js b/cypress/apiRequests/Attribute.js index f1c180d01..b0f23be23 100644 --- a/cypress/apiRequests/Attribute.js +++ b/cypress/apiRequests/Attribute.js @@ -1,20 +1,20 @@ class Attribute { createAttribute(name) { const mutation = `mutation{ - attributeCreate(input:{ - name:"${name}" - valueRequired:false - type:PRODUCT_TYPE - }){ - attribute{ - id + attributeCreate(input:{ + name:"${name}" + valueRequired:false + type:PRODUCT_TYPE + }){ + attribute{ + id + } + attributeErrors{ + field + message + } } - attributeErrors{ - field - message - } - } - }`; + }`; return cy.sendRequestWithQuery(mutation); } diff --git a/cypress/apiRequests/Collections.js b/cypress/apiRequests/Collections.js new file mode 100644 index 000000000..8a9e1aa87 --- /dev/null +++ b/cypress/apiRequests/Collections.js @@ -0,0 +1,37 @@ +class Collections { + getCollections(search) { + const filter = search + ? `, filter:{ + search:"" + }` + : ""; + const query = `query{ + collections(first:100 ${filter}){ + edges{ + node{ + id + name + } + } + } + }`; + return cy + .sendRequestWithQuery(query) + .then(resp => resp.body.data.collections.edges); + } + deleteCollection(collectionId) { + const mutation = `mutation{ + collectionDelete(id:"${collectionId}"){ + collection{ + id + } + collectionErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Collections; diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index b22ecf97b..8aa6b0ce8 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:{ @@ -17,44 +20,50 @@ class Product { } } } - } - `; + `; return cy .sendRequestWithQuery(query) .then(resp => resp.body.data.products.edges); } - updateChannelInProduct(productId, channelId) { + updateChannelInProduct({ + productId, + channelId, + isPublished = true, + isAvailableForPurchase = true, + visibleInListings = true + }) { const mutation = `mutation{ - productChannelListingUpdate(id:"${productId}", - input:{ - addChannels:{ - channelId:"${channelId}" - isPublished:true - isAvailableForPurchase:true + productChannelListingUpdate(id:"${productId}", + input:{ + addChannels:{ + channelId:"${channelId}" + isPublished:${isPublished} + isAvailableForPurchase:${isAvailableForPurchase} + visibleInListings:${visibleInListings} + } + }){ + product{ + id + name + } } - }){ - product{ - id - name - } - } - }`; - return cy.sendRequestWithQuery(mutation); + }`; + cy.sendRequestWithQuery(mutation); } updateChannelPriceInVariant(variantId, channelId) { const mutation = `mutation{ - productVariantChannelListingUpdate(id: "${variantId}", input:{ - channelId: "${channelId}" - price: 10 - costPrice: 10 - }){ - productChannelListingErrors{ - message - } - } - }`; + productVariantChannelListingUpdate(id: "${variantId}", input: { + channelId: "${channelId}" + price: 10 + costPrice: 10 + }){ + productChannelListingErrors{ + message + } + } +} `; return cy.sendRequestWithQuery(mutation); } createProduct(attributeId, name, productType, category) { @@ -88,27 +97,30 @@ 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:{ - attributes:[] - sku:"${sku}" + productVariantBulkCreate(product: "${productId}", variants: { + attributes: [] + sku: "${sku}" ${channelListings} ${stocks} - }){ + }) { productVariants{ id name @@ -124,33 +136,33 @@ class Product { createTypeProduct(name, attributeId, slug = name) { const mutation = `mutation{ - productTypeCreate(input:{ - name:"${name}" + productTypeCreate(input: { + name: "${name}" slug: "${slug}" - isShippingRequired:true - productAttributes:"${attributeId}" - }){ - productErrors{ - field - message - } - productType{ - id - } - } - }`; + isShippingRequired: true + productAttributes: "${attributeId}" + }){ + productErrors{ + field + message + } + productType{ + id + } + } +} `; return cy.sendRequestWithQuery(mutation); } deleteProduct(productId) { const mutation = `mutation{ - productDelete(id:"${productId}"){ - productErrors{ - field - message - } - } - }`; + productDelete(id: "${productId}"){ + productErrors{ + field + message + } + } +} `; return cy.sendRequestWithQuery(mutation); } diff --git a/cypress/apiRequests/frontShop/Collections.js b/cypress/apiRequests/frontShop/Collections.js deleted file mode 100644 index 2ff9e4fe2..000000000 --- a/cypress/apiRequests/frontShop/Collections.js +++ /dev/null @@ -1,52 +0,0 @@ -class Collections { - getCollection(collectionId, channelSlug) { - const operationName = "Collection"; - const variables = { - attributes: {}, - priceGte: null, - priceLte: null, - sortBy: null, - channel: channelSlug, - id: collectionId, - pageSize: 6 - }; - const query = - "query Collection($id: ID!, $channel: String!) {\n collection(id: $id, channel: $channel) {\n id\n slug\n name\n seoDescription\n seoTitle\n backgroundImage {\n url\n __typename\n }\n __typename\n }\n attributes(filter: {channel: $channel, inCollection: $id, 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.sendFrontShopRequest(operationName, query, variables); - } - getCollections(search) { - const filter = search - ? `, filter:{ - search:"" - }` - : ""; - const query = `query{ - collections(first:100 ${filter}){ - edges{ - node{ - id - name - } - } - } - }`; - return cy - .sendRequestWithQuery(query) - .then(resp => resp.body.data.collections.edges); - } - deleteCollection(collectionId) { - const mutation = `mutation{ - collectionDelete(id:"${collectionId}"){ - collection{ - id - } - collectionErrors{ - field - message - } - } - }`; - return cy.sendRequestWithQuery(mutation); - } -} -export default Collections; diff --git a/cypress/apiRequests/storeFront/Collections.js b/cypress/apiRequests/storeFront/Collections.js new file mode 100644 index 000000000..0b50dbf54 --- /dev/null +++ b/cypress/apiRequests/storeFront/Collections.js @@ -0,0 +1,21 @@ +class Collections { + getCollection(collectionId, channelSlug) { + const query = `query Collection{ + collection(id: "${collectionId}", channel: "${channelSlug}") { + id + slug + name + products(first:100){ + edges{ + node{ + id + name + } + } + } + } + }`; + return cy.sendFrontShopRequestWithQuery(query); + } +} +export default Collections; 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/elements/catalog/products/product-selectors.js b/cypress/elements/catalog/products/product-selectors.js index e2ef305c0..2fd93e118 100644 --- a/cypress/elements/catalog/products/product-selectors.js +++ b/cypress/elements/catalog/products/product-selectors.js @@ -13,9 +13,20 @@ export const PRODUCTS_SELECTORS = { saveBtn: "[data-test='button-bar-confirm']", confirmationMsg: "[data-test='notification-success']", channelAvailabilityItem: "[data-test='channel-availability-item']", + searchProducts: "[placeholder='Search Products...']", availableManageButton: "[data-test-id='channels-availiability-manage-button']", channelsAvailabilityForm: "[data-test-id='manage-products-channels-availiability-list']", + channelAvailabilityColumn: + "[data-test='availability'][data-test-availability='true']", + channelAvailabilityList: "ul[role='menu']", + goBackButton: "[data-test-id='app-header-back-button']", + assignedChannels: "[data-test='channel-availability-item']", + publishedRadioButtons: "[name*='isPublished']", + availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']", + radioButtonsValueTrue: "[value='true']", + radioButtonsValueFalse: "[value='false']", + visibleInListingsButton: "[name*='visibleInListings']", emptyProductRow: "[class*='Skeleton']" }; diff --git a/cypress/integration/channels.js b/cypress/integration/channels.js index 3176552ea..91999d5e7 100644 --- a/cypress/integration/channels.js +++ b/cypress/integration/channels.js @@ -44,10 +44,10 @@ describe("Channels", () => { it("should create new channel", () => { const randomChannel = `${channelStartsWith} ${faker.random.number()}`; - cy.waitForGraph("Channels"); + cy.addAliasToGraphRequest("Channels"); cy.visit(urlList.channels); - cy.wait("Channels"); - cy.waitForGraph("Channel"); + cy.wait("@Channels"); + cy.addAliasToGraphRequest("Channel"); channelsSteps.createChannelByView(randomChannel, currency); // New channel should be visible in channels list cy.wait("@Channel") @@ -65,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() @@ -106,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/collections.js b/cypress/integration/collections.js index 0ce8c3ec2..0e32fb2cc 100644 --- a/cypress/integration/collections.js +++ b/cypress/integration/collections.js @@ -8,18 +8,26 @@ import ChannelsUtils from "../utils/channelsUtils"; import CollectionsUtils from "../utils/collectionsUtils"; import ProductsUtils from "../utils/productsUtils"; import ShippingUtils from "../utils/shippingUtils"; +import StoreFrontCollectionUtils from "../utils/storeFront/collectionsUtils"; +import StoreFrontProductUtils from "../utils/storeFront/storeFrontProductUtils"; describe("Collections", () => { const productRequest = new Product(); const channelsUtils = new ChannelsUtils(); const productsUtils = new ProductsUtils(); + const storeFrontProductUtils = new StoreFrontProductUtils(); const collectionsUtils = new CollectionsUtils(); + const storeFrontCollectionUtils = new StoreFrontCollectionUtils(); const shippingUtils = new ShippingUtils(); const collectionsSteps = new CollectionsSteps(); const startsWith = "Cy-"; const name = `${startsWith}${faker.random.number()}`; + let attribute; + let productType; + let category; + let defaultChannel; before(() => { @@ -35,19 +43,16 @@ describe("Collections", () => { productsUtils.createTypeAttributeAndCategoryForProduct(name); }) .then(() => { - const attribute = productsUtils.getAttribute(); - const productType = productsUtils.getProductType(); - const category = productsUtils.getCategory(); - productsUtils.createProductInChannel( + attribute = productsUtils.getAttribute(); + productType = productsUtils.getProductType(); + category = productsUtils.getCategory(); + productsUtils.createProductInChannel({ name, - defaultChannel.id, - null, - null, - productType.id, - attribute.id, - category.id, - 1 - ); + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id + }); }); }); @@ -63,7 +68,7 @@ describe("Collections", () => { .createCollection(collectionName, false, defaultChannel) .then(collection => { collectionsSteps.assignProductsToCollection(name); - collectionsUtils.isCollectionVisible( + storeFrontCollectionUtils.isCollectionVisible( collection.id, defaultChannel.slug ); @@ -79,55 +84,71 @@ describe("Collections", () => { .createCollection(collectionName, true, defaultChannel) .then(collection => { collectionsSteps.assignProductsToCollection(name); - collectionsUtils.isCollectionVisible( + storeFrontCollectionUtils.isCollectionVisible( collection.id, defaultChannel.slug ); }) .then(isVisible => expect(isVisible).to.equal(true)); }); - xit("should not display unavailable in channel collections", () => { - channelsUtils.createChannel(true, name, name, "PLN").then(() => { - productRequest.updateChannelInProduct( - productsUtils.getCreatedProduct().id, - channelsUtils.getCreatedChannel().id - ); + it("should not display unavailable in channel collections", () => { + const collectionName = `${startsWith}${faker.random.number()}`; + channelsUtils + .createChannel({ name: collectionName }) + .then(() => { + productRequest.updateChannelInProduct( + productsUtils.getCreatedProduct().id, + channelsUtils.getCreatedChannel().id + ); + }) + .then(() => { + cy.visit(urlList.collections); + collectionsSteps.createCollection( + collectionName, + true, + channelsUtils.getCreatedChannel() + ); + }) + .then(collection => { + collectionsSteps.assignProductsToCollection(name); + storeFrontCollectionUtils.isCollectionVisible( + collection.id, + defaultChannel.slug + ); + }) + .then(isVisible => expect(isVisible).to.equal(false)); + }); + it("should display products hidden in listing, only in collection", () => { + const randomName = `${startsWith}${faker.random.number()}`; + const hiddenProductUtils = new ProductsUtils(); + hiddenProductUtils.createProductInChannel({ + name: randomName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + visibleInListings: false }); cy.visit(urlList.collections); collectionsSteps - .createCollection(collectionName, true, channelsUtils.getCreatedChannel()) + .createCollection(randomName, true, defaultChannel) .then(collection => { - collectionsSteps.assignProductsToCollection(name); - collectionsUtils.isCollectionVisible( + collectionsSteps.assignProductsToCollection(randomName); + storeFrontCollectionUtils.isProductInCollectionVisible( collection.id, - defaultChannel.slug - ); - }) - .then(isVisible => expect(isVisible).to.equal(true)); - }); - xit("should display products hidden in listing, only in collection", () => { - productsUtils.createProductInChannel( - name, - defaultChannel.id, - null, - null, - productsUtils.getProductType().id, - productsUtils.getAttribute().id, - productsUtils.getCategory().id, - 1 - ); - collectionsSteps - .createCollection(collectionName, true, defaultChannel) - .then(collection => { - collectionsSteps.assignProductsToCollection(name); - collectionsUtils.isProductInCollectionVisible( - collection.id, - defaultChannel.slug + defaultChannel.slug, + hiddenProductUtils.getCreatedProduct().id ); }) .then(isVisible => { expect(isVisible).to.equal(true); - // productsUtils.searchForProduct + storeFrontProductUtils.isProductVisibleInSearchResult( + hiddenProductUtils.getCreatedProduct().name, + defaultChannel.slug + ); + }) + .then(isVisible => { + expect(isVisible).to.equal(false); }); }); }); diff --git a/cypress/integration/homePage.js b/cypress/integration/homePage.js index 74e8762a1..3712afd56 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(); @@ -48,12 +48,12 @@ describe("User authorization", () => { ) .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("User authorization", () => { 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("User authorization", () => { 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 new file mode 100644 index 000000000..9c4a3c468 --- /dev/null +++ b/cypress/integration/products/menageProducts/availableForPurchaseProducts.js @@ -0,0 +1,115 @@ +import faker from "faker"; + +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", () => { + const shippingUtils = new ShippingUtils(); + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const productSteps = new ProductSteps(); + const frontShopProductUtils = new StoreFrontProductUtils(); + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productType; + let attribute; + let category; + let defaultChannel; + let warehouse; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + shippingUtils.deleteShipping(startsWith); + productsUtils.deleteProperProducts(startsWith); + + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + cy.fixture("addresses"); + }) + .then(addressesFixture => { + shippingUtils.createShipping({ + channelId: defaultChannel.id, + name, + address: addressesFixture.plAddress + }); + }) + .then(() => { + warehouse = shippingUtils.getWarehouse(); + }); + + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productType = productsUtils.getProductType(); + attribute = productsUtils.getAttribute(); + category = productsUtils.getCategory(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should update product to available for purchase", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id, + isAvailableForPurchase: false + }) + .then(() => { + const productUrl = productDetailsUrl( + productsUtils.getCreatedProduct().id + ); + productSteps.updateProductIsAvailableForPurchase(productUrl, 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", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id + }) + .then(() => { + const productUrl = productDetailsUrl( + productsUtils.getCreatedProduct().id + ); + productSteps.updateProductIsAvailableForPurchase(productUrl, 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 new file mode 100644 index 000000000..48015293f --- /dev/null +++ b/cypress/integration/products/menageProducts/publishedProducts.js @@ -0,0 +1,108 @@ +import faker from "faker"; + +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 StoreFrontProductUtils(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productType; + let attribute; + let category; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProperProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productType = productsUtils.getProductType(); + attribute = productsUtils.getAttribute(); + category = productsUtils.getCategory(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + it("should update product to published", () => { + const productName = `${startsWith}${faker.random.number()}`; + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + 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 = productDetailsUrl(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()}`; + let defaultChannel; + let product; + + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createProductInChannel({ + name: productName, + channelId: defaultChannel.id, + productTypeId: productType.id, + attributeId: attribute.id, + categoryId: category.id + }); + }) + .then(() => { + product = productsUtils.getCreatedProduct(); + const productUrl = productDetailsUrl(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/menageProducts/visibleInListingsProducts.js b/cypress/integration/products/menageProducts/visibleInListingsProducts.js new file mode 100644 index 000000000..19463c5ce --- /dev/null +++ b/cypress/integration/products/menageProducts/visibleInListingsProducts.js @@ -0,0 +1,102 @@ +import faker from "faker"; + +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 StoreFrontProductUtils(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + let productType; + let attribute; + let category; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + productsUtils.deleteProperProducts(startsWith); + productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { + productType = productsUtils.getProductType(); + attribute = productsUtils.getAttribute(); + category = productsUtils.getCategory(); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + it("should update product to visible in listings", () => { + const productName = `${startsWith}${faker.random.number()}`; + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + 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 = productDetailsUrl(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()}`; + let defaultChannel; + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + 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 = productDetailsUrl(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); + }); + }); +}); diff --git a/cypress/integration/products.js b/cypress/integration/products/products.js similarity index 87% rename from cypress/integration/products.js rename to cypress/integration/products/products.js index f1e8f5468..87960c269 100644 --- a/cypress/integration/products.js +++ b/cypress/integration/products/products.js @@ -1,7 +1,7 @@ // -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"; +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(() => { diff --git a/cypress/steps/collectionsSteps.js b/cypress/steps/collectionsSteps.js index 90ef78fa2..a2eb97db8 100644 --- a/cypress/steps/collectionsSteps.js +++ b/cypress/steps/collectionsSteps.js @@ -2,13 +2,11 @@ import { COLLECTION_SELECTORS } from "../elements/catalog/collection-selectors"; import { ASSIGN_PRODUCTS_SELECTORS } from "../elements/catalog/products/assign-products-selectors"; import { MENAGE_CHANNEL_AVAILABILITY_FORM } from "../elements/channels/menage-channel-availability-form"; import { BUTTON_SELECTORS } from "../elements/shared/button-selectors"; -import CollectionsUtils from "../utils/collectionsUtils"; class CollectionsSteps { createCollection(collectionName, isPublished, channel) { const publishedSelector = isPublished ? MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueTrue : MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueFalse; - const collectionsUtils = new CollectionsUtils(); cy.get(COLLECTION_SELECTORS.createCollectionButton) .click() @@ -28,11 +26,12 @@ class CollectionsSteps { .get( `${MENAGE_CHANNEL_AVAILABILITY_FORM.publishedCheckbox}${publishedSelector}` ) - .click() - .waitForGraph("CreateCollection") - .get(COLLECTION_SELECTORS.saveButton) .click(); - return collectionsUtils.waitForCreateCollectionRequest(); + cy.addAliasToGraphRequest("CreateCollection"); + cy.get(COLLECTION_SELECTORS.saveButton).click(); + return cy + .wait("@CreateCollection") + .its("response.body.data.collectionCreate.collection"); } assignProductsToCollection(productName) { cy.get(COLLECTION_SELECTORS.addProductButton) 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 new file mode 100644 index 000000000..eec8c3736 --- /dev/null +++ b/cypress/steps/productSteps.js @@ -0,0 +1,37 @@ +import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; + +class ProductSteps { + valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue; + valueFalse = PRODUCTS_SELECTORS.radioButtonsValueFalse; + + updateProductIsAvailableForPurchase(productUrl, isAvailableForPurchase) { + const isAvailableForPurchaseSelector = isAvailableForPurchase + ? this.valueTrue + : this.valueFalse; + const availableForPurchaseSelector = `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`; + this.updateProductMenageInChannel(productUrl, availableForPurchaseSelector); + } + updateProductPublish(productUrl, isPublished) { + const isPublishedSelector = isPublished ? this.valueTrue : this.valueFalse; + const publishedSelector = `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}`; + this.updateProductMenageInChannel(productUrl, publishedSelector); + } + updateProductVisibleInListings(productUrl) { + this.updateProductMenageInChannel( + productUrl, + PRODUCTS_SELECTORS.visibleInListingsButton + ); + } + updateProductMenageInChannel(productUrl, menageSelector) { + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(menageSelector) + .click(); + cy.addAliasToGraphRequest("ProductChannelListingUpdate"); + cy.get(PRODUCTS_SELECTORS.saveBtn) + .click() + .wait("@ProductChannelListingUpdate"); + } +} +export default ProductSteps; diff --git a/cypress/support/index.js b/cypress/support/index.js index 4e9a2361d..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,27 +50,9 @@ 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 }) ); -Cypress.Commands.add( - "sendFrontShopRequest", - (operationName, query, variables) => - cy.request({ - method: "POST", - url: urlList.apiUri, - headers: { - authorization: `JWT ${window.localStorage.getItem("token")}` - }, - body: [ - { - operationName, - variables, - query - } - ] - }) -); diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index b0f7f66b0..3ba659b6d 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,40 +10,32 @@ 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); - }); + return cy.sendRequestWithQuery(mutation, authorization).then(resp => { + window.sessionStorage.setItem( + authorization, + resp.body.data.tokenCreate.token + ); + }); }); Cypress.Commands.add("loginInShop", () => { cy.request({ diff --git a/cypress/url/urlList.js b/cypress/url/urlList.js index dec3bea5e..995a67ccc 100644 --- a/cypress/url/urlList.js +++ b/cypress/url/urlList.js @@ -8,3 +8,4 @@ export const urlList = { warehouses: "warehouses/", collections: "collections/" }; +export const productDetailsUrl = productId => `${urlList.products}${productId}`; diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js index c5e9dc6d0..a67a1b404 100644 --- a/cypress/utils/channelsUtils.js +++ b/cypress/utils/channelsUtils.js @@ -38,7 +38,7 @@ class ChannelsUtils { })); }); } - createChannel(isActive, name, slug, currencyCode) { + createChannel({ isActive = true, name, slug = name, currencyCode = "PLN" }) { return this.channels .createChannel(isActive, name, slug, currencyCode) .then( @@ -46,7 +46,7 @@ class ChannelsUtils { ); } getCreatedChannel() { - return channel; + return this.createdChannel; } } export default ChannelsUtils; diff --git a/cypress/utils/collectionsUtils.js b/cypress/utils/collectionsUtils.js index b510af4fc..bf80f36ec 100644 --- a/cypress/utils/collectionsUtils.js +++ b/cypress/utils/collectionsUtils.js @@ -1,21 +1,8 @@ -import Collections from "../apiRequests/frontShop/Collections"; +import Collections from "../apiRequests/Collections"; class CollectionsUtils { collectionsRequest = new Collections(); - isCollectionVisible(collectionId, channelSlug) { - return this.collectionsRequest - .getCollection(collectionId, channelSlug) - .then(resp => { - const collection = resp.body[0].data.collection; - return collection !== null && collection.id === collectionId; - }); - } - waitForCreateCollectionRequest() { - return cy - .wait(`@CreateCollection`) - .its("response.body.data.collectionCreate.collection"); - } deleteProperCollections(startsWith) { cy.deleteProperElements( this.collectionsRequest.deleteCollection, diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index 43fba4be3..2ac9f0ce7 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -13,19 +13,37 @@ class ProductsUtils { attribute; category; - createProductInChannel( + createProductWithVariant(name, attributeId, productTypeId, categoryId) { + return this.createProduct( + attributeId, + name, + productTypeId, + categoryId + ).then(() => this.createVariant(this.product.id, name)); + } + + 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, channelId) + this.productRequest.updateChannelInProduct({ + productId: this.product.id, + channelId, + isPublished, + isAvailableForPurchase, + visibleInListings + }) ) .then(() => { this.createVariant( @@ -92,7 +110,9 @@ class ProductsUtils { resp.body.data.productVariantBulkCreate.productVariants) ); } - + getCreatedProduct() { + return this.product; + } getCreatedVariants() { return this.variants; } 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/collectionsUtils.js b/cypress/utils/storeFront/collectionsUtils.js new file mode 100644 index 000000000..3a25953c0 --- /dev/null +++ b/cypress/utils/storeFront/collectionsUtils.js @@ -0,0 +1,23 @@ +import Collections from "../../apiRequests/storeFront/Collections"; + +class CollectionsUtils { + collectionsRequest = new Collections(); + + isCollectionVisible(collectionId, channelSlug) { + return this.collectionsRequest + .getCollection(collectionId, channelSlug) + .then(resp => { + const collection = resp.body.data.collection; + return collection !== null && collection.id === collectionId; + }); + } + isProductInCollectionVisible(collectionId, channelSlug, productId) { + return this.collectionsRequest + .getCollection(collectionId, channelSlug) + .then(resp => { + const product = resp.body.data.collection.products.edges[0].node; + return product !== null && product.id === productId; + }); + } +} +export default CollectionsUtils; diff --git a/cypress/utils/storeFront/storeFrontProductUtils.js b/cypress/utils/storeFront/storeFrontProductUtils.js new file mode 100644 index 000000000..676075f19 --- /dev/null +++ b/cypress/utils/storeFront/storeFrontProductUtils.js @@ -0,0 +1,34 @@ +import ProductDetails from "../../apiRequests/storeFront/ProductDetails"; +import Search from "../../apiRequests/storeFront/Search"; + +class StoreFrontProductUtils { + isProductVisible(productId, channelSlug, name) { + const productDetails = new ProductDetails(); + return productDetails + .getProductDetails(productId, channelSlug) + .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 => + 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 + ); + } +} +export default StoreFrontProductUtils;