Merge pull request #996 from mirumee/SALEOR-1744-Tests-for-Collections

Saleor 1744 tests for collections
This commit is contained in:
Karolina 2021-03-04 16:15:20 +01:00 committed by GitHub
commit 8075d72c17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 420 additions and 62 deletions

View file

@ -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;

View file

@ -0,0 +1,22 @@
class Collections {
getCollection(collectionId, channelSlug) {
const query = `query Collection{
collection(id: "${collectionId}", channel: "${channelSlug}") {
id
slug
name
products(first:100){
totalCount
edges{
node{
id
name
}
}
}
}
}`;
return cy.sendRequestWithQuery(query, "token");
}
}
export default Collections;

View file

@ -0,0 +1,6 @@
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']"
};

View file

@ -0,0 +1,6 @@
export const ASSIGN_PRODUCTS_SELECTORS = {
searchInput: "[name='query']",
tableRow: "[class*='MuiTableRow']",
checkbox: "[type='checkbox']",
submitButton: "[type='submit']"
};

View file

@ -1,4 +1,3 @@
/* eslint-disable sort-keys */
export const PRODUCTS_SELECTORS = {
productsList: "[data-test-id][data-test='id']",
products: "[data-test='submenu-item-label'][data-test-id='products']",

View file

@ -0,0 +1,10 @@
export const MENAGE_CHANNEL_AVAILABILITY_FORM = {
channelsMenageButton: "[data-test-id='channels-availiability-manage-button']",
allChannelsCheckbox: "[name='allChannels']",
channelRow: "[data-test-id='channel-row']",
channelCheckbox: "[class*='MuiCheckbox']",
channelsAvailabilityItem: "[data-test='channel-availability-item']",
publishedCheckbox: "[name='isPublished']",
radioButtonsValueTrue: "[value='true']",
radioButtonsValueFalse: "[value='false']"
};

View file

@ -1,4 +1,3 @@
/* eslint-disable sort-keys */
export const CONFIGURATION_SELECTORS = {
channels: "[data-testid='channels']"
};

View file

@ -1,4 +1,3 @@
/* eslint-disable sort-keys */
export const BUTTON_SELECTORS = {
back: '[data-test="back"]',
submit: '[data-test="submit"]'

View file

@ -3,7 +3,7 @@ import faker from "faker";
import Channels from "../apiRequests/Channels";
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/products/product-selectors";
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
@ -111,8 +111,7 @@ describe("Channels", () => {
cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels);
cy.wait("@Channels");
cy.get(CHANNELS_SELECTORS.channelName)
.contains(randomChannelToDelete)
cy.contains(CHANNELS_SELECTORS.channelName, randomChannelToDelete)
.parentsUntil(CHANNELS_SELECTORS.channelsTable)
.find("button")
.click();

View file

@ -0,0 +1,177 @@
// <reference types="cypress" />
import faker from "faker";
import Product from "../apiRequests/Product";
import Collections from "../apiRequests/storeFront/Collections";
import Search from "../apiRequests/storeFront/Search";
import CollectionsSteps from "../steps/collectionsSteps";
import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils";
import CollectionsUtils from "../utils/collectionsUtils";
import ProductsUtils from "../utils/productsUtils";
import ShippingUtils from "../utils/shippingUtils";
import {
isCollectionVisible,
isProductInCollectionVisible
} from "../utils/storeFront/collectionsUtils";
import { isProductVisibleInSearchResult } from "../utils/storeFront/storeFrontProductUtils";
describe("Collections", () => {
const productRequest = new Product();
const collectionsRequest = new Collections();
const search = new Search();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const collectionsUtils = new CollectionsUtils();
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(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
collectionsUtils.deleteProperCollections(startsWith);
shippingUtils.deleteShipping(startsWith);
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createTypeAttributeAndCategoryForProduct(name);
})
.then(() => {
attribute = productsUtils.getAttribute();
productType = productsUtils.getProductType();
category = productsUtils.getCategory();
productsUtils.createProductInChannel({
name,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id
});
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should not display hidden collections", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
cy.visit(urlList.collections);
let collection;
collectionsSteps
.createCollection(collectionName, false, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
collectionsSteps.assignProductsToCollection(name);
})
.then(() => {
collectionsRequest.getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(false);
});
});
it("should display collections", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
let collection;
cy.visit(urlList.collections);
collectionsSteps
.createCollection(collectionName, true, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
collectionsSteps.assignProductsToCollection(name);
collectionsRequest.getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(true);
});
});
it("should not display collection not set as available in channel", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
let collection;
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(collectionResp => {
collection = collectionResp;
collectionsSteps.assignProductsToCollection(name);
collectionsRequest.getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(false);
});
});
it("should display products hidden in listing", () => {
// Products "hidden in listings" are not displayed in Category listings or search results,
// but are listed on Collections
const randomName = `${startsWith}${faker.random.number()}`;
const hiddenProductUtils = new ProductsUtils();
let collection;
hiddenProductUtils.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
visibleInListings: false
});
cy.visit(urlList.collections);
collectionsSteps
.createCollection(randomName, true, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
collectionsSteps.assignProductsToCollection(randomName);
})
.then(() => {
collectionsRequest.getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isProductInCollectionVisible(
resp,
hiddenProductUtils.getCreatedProduct().id
);
expect(isVisible).to.equal(true);
})
.then(() => {
search.searchInShop(hiddenProductUtils.getCreatedProduct().name);
})
.then(resp => {
const isVisible = isProductVisibleInSearchResult(
resp,
hiddenProductUtils.getCreatedProduct().name
);
expect(isVisible).to.equal(false);
});
});
});

View file

@ -1,5 +1,6 @@
import faker from "faker";
import ProductDetails from "../../../apiRequests/storeFront/ProductDetails";
import ProductSteps from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
@ -9,6 +10,7 @@ import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFr
// <reference types="cypress" />
describe("Products available in listings", () => {
const productDetails = new ProductDetails();
const shippingUtils = new ShippingUtils();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
@ -73,14 +75,13 @@ describe("Products available in listings", () => {
productSteps.updateProductIsAvailableForPurchase(productUrl, true);
})
.then(() => {
isProductAvailableForPurchase(
productDetails.getProductDetails(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
defaultChannel.slug
);
})
.then(isVisibleResp => {
expect(isVisibleResp).to.be.eq(true);
.then(resp => {
expect(isProductAvailableForPurchase(resp)).to.be.eq(true);
});
});
it("should update product to not available for purchase", () => {
@ -101,14 +102,13 @@ describe("Products available in listings", () => {
productSteps.updateProductIsAvailableForPurchase(productUrl, false);
})
.then(() => {
isProductAvailableForPurchase(
productDetails.getProductDetails(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
defaultChannel.slug
);
})
.then(isProductVisible => {
expect(isProductVisible).to.be.eq(false);
.then(resp => {
expect(isProductAvailableForPurchase(resp)).to.be.eq(false);
});
});
});

View file

@ -1,5 +1,6 @@
import faker from "faker";
import ProductDetails from "../../../apiRequests/storeFront/ProductDetails";
import ProductSteps from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
@ -8,6 +9,7 @@ import { isProductVisible } from "../../../utils/storeFront/storeFrontProductUti
// <reference types="cypress" />
describe("Published products", () => {
const productDetails = new ProductDetails();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
@ -52,9 +54,10 @@ describe("Published products", () => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, true);
isProductVisible(product.id, defaultChannel.slug, productName);
productDetails.getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(true);
});
});
@ -79,16 +82,18 @@ describe("Published products", () => {
product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, false);
isProductVisible(product.id, defaultChannel.slug, productName);
productDetails.getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(false);
cy.loginInShop();
})
.then(() => {
isProductVisible(product.id, defaultChannel.slug, productName);
productDetails.getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(true);
});
});

View file

@ -1,5 +1,6 @@
import faker from "faker";
import Search from "../../../apiRequests/storeFront/Search";
import ProductSteps from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
@ -8,6 +9,7 @@ import { isProductVisibleInSearchResult } from "../../../utils/storeFront/storeF
// <reference types="cypress" />
describe("Products displayed in listings", () => {
const search = new Search();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
@ -52,9 +54,13 @@ describe("Products displayed in listings", () => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug);
search.searchInShop(productName);
})
.then(isProductVisible => {
.then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(true);
});
});
@ -78,17 +84,24 @@ describe("Products displayed in listings", () => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug).then(
isProductVisible => {
expect(isProductVisible).to.be.eq(false);
}
);
search.searchInShop(productName).then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(false);
});
cy.loginInShop();
})
.then(() => {
isProductVisibleInSearchResult(productName, defaultChannel.slug);
search.searchInShop(productName);
})
.then(isProductVisible => {
.then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(true);
});
});

View file

@ -1,6 +1,6 @@
// <reference types="cypress" />
import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
import { urlList } from "../../url/urlList";
describe("Products", () => {

View file

@ -0,0 +1,49 @@
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";
class CollectionsSteps {
createCollection(collectionName, isPublished, channel) {
const publishedSelector = isPublished
? MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueTrue
: MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueFalse;
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, channel.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}${publishedSelector}`
)
.click();
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)
.click()
.get(ASSIGN_PRODUCTS_SELECTORS.searchInput)
.type(productName);
cy.contains(ASSIGN_PRODUCTS_SELECTORS.tableRow, productName)
.find(ASSIGN_PRODUCTS_SELECTORS.checkbox)
.click();
cy.addAliasToGraphRequest("CollectionAssignProduct");
cy.get(ASSIGN_PRODUCTS_SELECTORS.submitButton).click();
cy.wait("@CollectionAssignProduct");
}
}
export default CollectionsSteps;

View file

@ -1,4 +1,4 @@
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
import { VARIANTS_SELECTORS } from "../../elements/catalog/variants-selectors";
class VariantsSteps {

View file

@ -1,4 +1,4 @@
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
class ProductSteps {
valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue;

View file

@ -5,6 +5,7 @@ export const urlList = {
homePage: "/",
orders: "orders/",
products: "products/",
warehouses: "warehouses/"
warehouses: "warehouses/",
collections: "collections/"
};
export const productDetailsUrl = productId => `${urlList.products}${productId}`;

View file

@ -2,6 +2,7 @@ import Channels from "../apiRequests/Channels";
class ChannelsUtils {
channels = new Channels();
createdChannel;
deleteChannels(nameStartsWith) {
this.channels.getChannels().then(resp => {
@ -37,5 +38,15 @@ class ChannelsUtils {
}));
});
}
createChannel({ isActive = true, name, slug = name, currencyCode = "PLN" }) {
return this.channels
.createChannel(isActive, name, slug, currencyCode)
.then(
resp => (this.createdChannel = resp.body.data.channelCreate.channel)
);
}
getCreatedChannel() {
return this.createdChannel;
}
}
export default ChannelsUtils;

View file

@ -0,0 +1,15 @@
import Collections from "../apiRequests/Collections";
class CollectionsUtils {
collectionsRequest = new Collections();
deleteProperCollections(startsWith) {
cy.deleteProperElements(
this.collectionsRequest.deleteCollection,
this.collectionsRequest.getCollections,
startsWith,
"collection"
);
}
}
export default CollectionsUtils;

View file

@ -0,0 +1,11 @@
export const isCollectionVisible = (resp, collectionId) => {
const collection = resp.body.data.collection;
return collection !== null && collection.id === collectionId;
};
export const isProductInCollectionVisible = (resp, productId) => {
const productsList = resp.body.data.collection.products;
return (
productsList.totalCount !== 0 && productsList.edges[0].node.id === productId
);
};

View file

@ -1,35 +1,21 @@
import ProductDetails from "../../apiRequests/storeFront/ProductDetails";
import Search from "../../apiRequests/storeFront/Search";
export const 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;
});
export const isProductVisible = (resp, name) => {
const product = resp.body.data.product;
return product !== null && product.name === name;
};
export const isProductAvailableForPurchase = (productId, channelSlug) => {
const productDetails = new ProductDetails();
return productDetails
.getProductDetails(productId, channelSlug)
.then(
productDetailsResp =>
productDetailsResp.body.data.product.isAvailableForPurchase
);
export const isProductAvailableForPurchase = resp => {
const product = resp.body.data.product;
return product.isAvailableForPurchase;
};
export const 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 const isProductVisibleInSearchResult = (resp, productName) => {
const productsList = resp.body.data.products;
return (
productsList.totalCount !== 0 &&
productsList.edges[0].node.name === productName
);
};
export const getProductVariants = (productId, channelSlug) => {

View file

@ -55,6 +55,7 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
disabled={disabled}
variant="contained"
onClick={onAdd}
data-test-id="create-collection"
>
<FormattedMessage
defaultMessage="Create collection"

View file

@ -108,6 +108,7 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
}
toolbar={
<Button
data-test-id="add-product"
disabled={disabled}
variant="text"
color="primary"

View file

@ -83,7 +83,11 @@ export const ChannelsAvailabilityContent: React.FC<ChannelsAvailabilityContentPr
>
{filteredChannels?.length ? (
filteredChannels.map(option => (
<div key={option.id} className={classes.option}>
<div
key={option.id}
className={classes.option}
data-test-id="channel-row"
>
<ControlledCheckbox
checked={isSelected(option)}
name={option.name}

View file

@ -50667,6 +50667,7 @@ exports[`Storyshots Views / Collections / Collection details default 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id"
data-test-id="add-product"
tabindex="0"
type="button"
>
@ -51957,6 +51958,7 @@ exports[`Storyshots Views / Collections / Collection details form errors 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id"
data-test-id="add-product"
tabindex="0"
type="button"
>
@ -52961,6 +52963,7 @@ exports[`Storyshots Views / Collections / Collection details loading 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id MuiButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="add-product"
disabled=""
tabindex="-1"
type="button"
@ -53940,6 +53943,7 @@ exports[`Storyshots Views / Collections / Collection details no products 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id MuiButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="add-product"
disabled=""
tabindex="-1"
type="button"
@ -54251,6 +54255,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-test-id="create-collection"
tabindex="0"
type="button"
>
@ -54944,6 +54949,7 @@ exports[`Storyshots Views / Collections / Collection list loading 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id MuiButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="create-collection"
disabled=""
tabindex="-1"
type="button"
@ -55362,6 +55368,7 @@ exports[`Storyshots Views / Collections / Collection list no data 1`] = `
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-test-id="create-collection"
tabindex="0"
type="button"
>