From 5b05db92fcc2d7467859a75a37db707d25c93cb8 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 18 Feb 2021 10:00:08 +0100 Subject: [PATCH] add get collection request --- cypress/apiRequests/Attribute.js | 49 +++++ cypress/apiRequests/Category.js | 43 ++++ cypress/apiRequests/Channels.js | 14 +- cypress/apiRequests/Product.js | 193 ++++++++++++++++++ cypress/apiRequests/frontShop/Collections.js | 18 ++ .../elements/catalog/collection-selectors.js | 7 + .../products/assign-products-selectors.js | 7 + .../{ => products}/product-selectors.js | 0 .../menage-channel-availability-form.js | 10 + cypress/integration/collections.js | 114 +++++++++++ cypress/support/index.js | 21 +- cypress/support/user/index.js | 23 ++- cypress/url/urlList.js | 3 +- cypress/utils/channelsUtils.js | 16 ++ cypress/utils/collectionsUtils.js | 19 ++ cypress/utils/productsUtils.js | 132 ++++++++++++ .../CollectionListPage/CollectionListPage.tsx | 1 + .../CollectionProducts/CollectionProducts.tsx | 1 + 18 files changed, 665 insertions(+), 6 deletions(-) create mode 100644 cypress/apiRequests/Attribute.js create mode 100644 cypress/apiRequests/Category.js create mode 100644 cypress/apiRequests/Product.js create mode 100644 cypress/apiRequests/frontShop/Collections.js create mode 100644 cypress/elements/catalog/collection-selectors.js create mode 100644 cypress/elements/catalog/products/assign-products-selectors.js rename cypress/elements/catalog/{ => products}/product-selectors.js (100%) create mode 100644 cypress/elements/channels/menage-channel-availability-form.js create mode 100644 cypress/integration/collections.js create mode 100644 cypress/utils/channelsUtils.js create mode 100644 cypress/utils/collectionsUtils.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..75b13c08d --- /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..a20d10a23 --- /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 index 9018fd675..603d442ed 100644 --- a/cypress/apiRequests/Channels.js +++ b/cypress/apiRequests/Channels.js @@ -49,7 +49,19 @@ class Channels { }); }); } - + getChannels() { + const getChannelsInfoQuery = `query{ + channels{ + name + id + isActive + slug + currencyCode + } + } + `; + return cy.sendRequestWithQuery(getChannelsInfoQuery); + } deleteChannel(channelId, targetChennelId) { const deleteChannelMutation = `mutation{ channelDelete(id: "${channelId}", input:{ diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js new file mode 100644 index 000000000..72e0df27c --- /dev/null +++ b/cypress/apiRequests/Product.js @@ -0,0 +1,193 @@ +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 = true, + isAvailableForPurchase = true, + visibleInListings = true + ) { + const mutation = `mutation{ + productChannelListingUpdate(id:"${productId}", + input:{ + addChannels:{ + channelId:"${channelId}" + isPublished:${isPublished} + isAvailableForPurchase:${isAvailableForPurchase} + visibleInListings:${visibleInListings} + } + }){ + 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/frontShop/Collections.js b/cypress/apiRequests/frontShop/Collections.js new file mode 100644 index 000000000..43719b771 --- /dev/null +++ b/cypress/apiRequests/frontShop/Collections.js @@ -0,0 +1,18 @@ +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); + } +} +export default Collections; diff --git a/cypress/elements/catalog/collection-selectors.js b/cypress/elements/catalog/collection-selectors.js new file mode 100644 index 000000000..8d013cf68 --- /dev/null +++ b/cypress/elements/catalog/collection-selectors.js @@ -0,0 +1,7 @@ +/* eslint-disable sort-keys */ +export const COLLECTION_SELECTORS = { + createCollectionButton: "[data-test-id = 'create-collection']", + nameInput: "[name='name']", + saveButton: "[data-test='button-bar-confirm']", + addProductButton: "[data-test-id='add-product']" +}; diff --git a/cypress/elements/catalog/products/assign-products-selectors.js b/cypress/elements/catalog/products/assign-products-selectors.js new file mode 100644 index 000000000..381a55f5a --- /dev/null +++ b/cypress/elements/catalog/products/assign-products-selectors.js @@ -0,0 +1,7 @@ +/* eslint-disable sort-keys */ +export const ASSIGN_PRODUCTS_SELECTORS = { + searchInput: "[name='query']", + tableRow: "[class*='MuiTableRow']", + checkbox: "[class*='checkboxCell']", + submitButton: "[type='submit']" +}; diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/products/product-selectors.js similarity index 100% rename from cypress/elements/catalog/product-selectors.js rename to cypress/elements/catalog/products/product-selectors.js diff --git a/cypress/elements/channels/menage-channel-availability-form.js b/cypress/elements/channels/menage-channel-availability-form.js new file mode 100644 index 000000000..873a635f9 --- /dev/null +++ b/cypress/elements/channels/menage-channel-availability-form.js @@ -0,0 +1,10 @@ +export const MENAGE_CHANNEL_AVAILABILITY_FORM = { + channelsMenageButton: "[data-test-id='channels-availiability-manage-button']", + allChannelsCheckbox: "[name='allChannels']", + channelRow: "[class*='ChannelsAvailabilityContent-option']", + channelCheckbox: "[class*='MuiCheckbox']", + channelsAvailabilityItem: "[data-test='channel-availability-item']", + publishedCheckbox: "[name='isPublished']", + radioButtonsValueTrue: "[value='true']", + radioButtonsValueFalse: "[value='false']" +}; diff --git a/cypress/integration/collections.js b/cypress/integration/collections.js new file mode 100644 index 000000000..a3ea0d8aa --- /dev/null +++ b/cypress/integration/collections.js @@ -0,0 +1,114 @@ +// +import faker from "faker"; + +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 { urlList } from "../url/urlList"; +import ChannelsUtils from "../utils/channelsUtils"; +import CollectionsUtils from "../utils/collectionsUtils"; +import ProductsUtils from "../utils/productsUtils"; + +describe("Collections", () => { + const channelsUtils = new ChannelsUtils(); + const productsUtils = new ProductsUtils(); + const collectionsFrontUtils = new CollectionsUtils(); + + const startsWith = "Cy-"; + const name = `${startsWith}${faker.random.number()}`; + + let defaultChannel; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + channelsUtils + .getDefaultChannel() + .then(channel => { + defaultChannel = channel; + productsUtils.createTypeAttributeAndCategoryForProduct(name); + }) + .then(() => { + const attribute = productsUtils.getAttribute(); + const productType = productsUtils.getProductType(); + const category = productsUtils.getCategory(); + productsUtils.createProductInChannel( + name, + defaultChannel.id, + null, + null, + productType.id, + attribute.id, + category.id, + 1 + ); + }); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + cy.visit(urlList.collections); + }); + + it("should not display hidden collections", () => { + const collectionName = `${startsWith}${faker.random.number()}`; + + cy.get(COLLECTION_SELECTORS.createCollectionButton) + .click() + .get(COLLECTION_SELECTORS.nameInput) + .type(collectionName) + .get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsMenageButton) + .click() + .get(MENAGE_CHANNEL_AVAILABILITY_FORM.allChannelsCheckbox) + .click(); + cy.contains( + MENAGE_CHANNEL_AVAILABILITY_FORM.channelRow, + defaultChannel.name + ) + .find(MENAGE_CHANNEL_AVAILABILITY_FORM.channelCheckbox) + .click() + .get(BUTTON_SELECTORS.submit) + .click() + .get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsAvailabilityItem) + .click() + .get( + `${MENAGE_CHANNEL_AVAILABILITY_FORM.publishedCheckbox}${MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueFalse}` + ) + .click() + .waitForGraph("CreateCollection") + .get(COLLECTION_SELECTORS.saveButton) + .click(); + collectionsFrontUtils.getCreatedCollection().then(collection => { + cy.get(COLLECTION_SELECTORS.addProductButton) + .click() + .get(ASSIGN_PRODUCTS_SELECTORS.searchInput) + .type(name); + cy.contains(ASSIGN_PRODUCTS_SELECTORS.tableRow, name) + .find(ASSIGN_PRODUCTS_SELECTORS.checkbox) + .click() + .get(ASSIGN_PRODUCTS_SELECTORS.submitButton) + .click() + .loginInShop(); + collectionsFrontUtils.isCollectionVisible( + collection.id, + defaultChannel.slug + ); + }); + }); + // xit("should display collections", () => { + // createVisibleCollection + // addProductToCollection + // checkIfCollectionIsNotDisplayed + // }); + // xit("should not display unavailable collections", () => { + // createunavailableCollection + // addProductToCollection + // checkIfCollectionIsNotDisplayed + // }); + // xit("should display products hidden in listing only in collection", () => { + // createHiddenInListingsProduct + // createVisibleCollection + // addProductToCollection + // checkIfCollectionIsNotDisplayed + // }); +}); diff --git a/cypress/support/index.js b/cypress/support/index.js index f952dd391..ddfb59da7 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -35,7 +35,6 @@ Cypress.Commands.add("waitForGraph", operationName => { } } }); - cy.wait(`@${operationName}`); }); Cypress.Commands.add("sendRequestWithQuery", query => @@ -43,7 +42,7 @@ Cypress.Commands.add("sendRequestWithQuery", query => body: { method: "POST", query, - url: urlList.apiUri, + url: urlList.apiUri }, headers: { Authorization: `JWT ${window.sessionStorage.getItem("auth")}` @@ -52,3 +51,21 @@ Cypress.Commands.add("sendRequestWithQuery", query => 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 1656ad3c1..b0f7f66b0 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -37,12 +37,31 @@ Cypress.Commands.add("loginUserViaRequest", () => { variables: { email: Cypress.env("USER_NAME"), password: Cypress.env("USER_PASSWORD") - }, + } }, method: "POST", - url: urlList.apiUri, + 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); + }); +}); diff --git a/cypress/url/urlList.js b/cypress/url/urlList.js index 2ceb592c5..dec3bea5e 100644 --- a/cypress/url/urlList.js +++ b/cypress/url/urlList.js @@ -5,5 +5,6 @@ export const urlList = { homePage: "/", orders: "orders/", products: "products/", - warehouses: "warehouses/" + warehouses: "warehouses/", + collections: "collections/" }; diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js new file mode 100644 index 000000000..7d93ae3eb --- /dev/null +++ b/cypress/utils/channelsUtils.js @@ -0,0 +1,16 @@ +import Channels from "../apiRequests/Channels"; + +class ChannelsUtils { + channels = new Channels(); + getDefaultChannel() { + return this.channels.getChannels().then(resp => { + const channelsArray = resp.body.data.channels; + return (this.defaultChannel = channelsArray.find(function( + channelElement + ) { + return channelElement.slug === "default-channel"; + })); + }); + } +} +export default ChannelsUtils; diff --git a/cypress/utils/collectionsUtils.js b/cypress/utils/collectionsUtils.js new file mode 100644 index 000000000..51deb3579 --- /dev/null +++ b/cypress/utils/collectionsUtils.js @@ -0,0 +1,19 @@ +import Collections from "../apiRequests/frontShop/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 && collection.id === collectionId; + }); + } + getCreatedCollection() { + return cy + .wait(`@CreateCollection`) + .its("response.body.data.collectionCreate.collection"); + } +} +export default CollectionsUtils; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js new file mode 100644 index 000000000..43fba4be3 --- /dev/null +++ b/cypress/utils/productsUtils.js @@ -0,0 +1,132 @@ +import Attribute from "../apiRequests/Attribute"; +import Category from "../apiRequests/Category"; +import Product from "../apiRequests/Product"; + +class ProductsUtils { + productRequest = new Product(); + attributeRequest = new Attribute(); + categoryRequest = new Category(); + + product; + variants; + productType; + attribute; + category; + + createProductInChannel( + name, + channelId, + warehouseId, + quantityInWarehouse, + productTypeId, + attributeId, + categoryId, + price + ) { + return this.createProduct(attributeId, name, productTypeId, categoryId) + .then(() => + this.productRequest.updateChannelInProduct(this.product.id, channelId) + ) + .then(() => { + this.createVariant( + this.product.id, + name, + warehouseId, + quantityInWarehouse, + channelId, + price + ); + }); + } + + createTypeAttributeAndCategoryForProduct(name) { + return this.createAttribute(name) + .then(() => this.createTypeProduct(name, this.attribute.id)) + .then(() => this.createCategory(name)); + } + createAttribute(name) { + return this.attributeRequest + .createAttribute(name) + .then( + resp => (this.attribute = resp.body.data.attributeCreate.attribute) + ); + } + createTypeProduct(name, attributeId) { + return this.productRequest + .createTypeProduct(name, attributeId) + .then( + resp => + (this.productType = resp.body.data.productTypeCreate.productType) + ); + } + createCategory(name) { + return this.categoryRequest + .createCategory(name) + .then(resp => (this.category = resp.body.data.categoryCreate.category)); + } + createProduct(attributeId, name, productTypeId, categoryId) { + return this.productRequest + .createProduct(attributeId, name, productTypeId, categoryId) + .then(resp => (this.product = resp.body.data.productCreate.product)); + } + createVariant( + productId, + name, + warehouseId, + quantityInWarehouse, + channelId, + price + ) { + return this.productRequest + .createVariant( + productId, + name, + warehouseId, + quantityInWarehouse, + channelId, + price + ) + .then( + resp => + (this.variants = + resp.body.data.productVariantBulkCreate.productVariants) + ); + } + + getCreatedVariants() { + return this.variants; + } + getProductType() { + return this.productType; + } + getAttribute() { + return this.attribute; + } + getCategory() { + return this.category; + } + deleteProperProducts(startsWith) { + const product = new Product(); + const attribute = new Attribute(); + const category = new Category(); + cy.deleteProperElements( + product.deleteProductType, + product.getProductTypes, + startsWith, + "productType" + ); + cy.deleteProperElements( + attribute.deleteAttribute, + attribute.getAttributes, + startsWith, + "attributes" + ); + cy.deleteProperElements( + category.deleteCategory, + category.getCategories, + startsWith, + "categories" + ); + } +} +export default ProductsUtils; diff --git a/src/collections/components/CollectionListPage/CollectionListPage.tsx b/src/collections/components/CollectionListPage/CollectionListPage.tsx index 31c89ad86..ddb5d1e78 100644 --- a/src/collections/components/CollectionListPage/CollectionListPage.tsx +++ b/src/collections/components/CollectionListPage/CollectionListPage.tsx @@ -55,6 +55,7 @@ const CollectionListPage: React.FC = ({ disabled={disabled} variant="contained" onClick={onAdd} + data-test-id="create-collection" > = props => { } toolbar={