tests for publish products

This commit is contained in:
Karolina Rakoczy 2021-02-04 12:15:27 +01:00
parent 6477d62409
commit cb528a7744
17 changed files with 886 additions and 48 deletions

View file

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

View file

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

View file

@ -0,0 +1,53 @@
class Channels {
createChannel(isActive, name, slug, currencyCode) {
const createChannelMutation = `mutation{
channelCreate(input: {
isActive: ${isActive}
name: "${name}"
slug: "${slug}"
currencyCode: "${currencyCode}"
}){
channel{
name
slug
}
channelErrors{
code
message
}
}
}`;
return cy.sendRequestWithQuery(createChannelMutation);
}
getChannels() {
const getChannelsInfoQuery = `query{
channels{
name
id
isActive
slug
currencyCode
}
}
`;
return cy.sendRequestWithQuery(getChannelsInfoQuery);
}
deleteChannel(channelId, targetChennelId) {
const deleteChannelMutation = `mutation{
channelDelete(id: "${channelId}", input:{
targetChannel: "${targetChennelId}"
}){
channel{
name
}
channelErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(deleteChannelMutation);
}
}
export default Channels;

View file

@ -0,0 +1,192 @@
class Product {
getFirstProducts(first, search) {
let filter = "";
if (search) {
filter = `, filter:{
search:"${search}"
}`;
}
const query = `query{
products(first:${first}${filter}){
edges{
node{
id
name
variants{
id
}
}
}
}
}
`;
return cy.sendRequestWithQuery(query);
}
updateChannelInProduct(
productId,
channelId,
isPublished,
isAvailableForPurchase
) {
const mutation = `mutation{
productChannelListingUpdate(id:"${productId}",
input:{
addChannels:{
channelId:"${channelId}"
isPublished:${isPublished}
isAvailableForPurchase:${isAvailableForPurchase}
visibleInListings:true
}
}){
product{
id
name
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
updateChannelPriceInVariant(variantId, channelId) {
const mutation = `mutation{
productVariantChannelListingUpdate(id: "${variantId}", input:{
channelId: "${channelId}"
price: 10
costPrice: 10
}){
productChannelListingErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
createProduct(attributeId, name, productType, category) {
const mutation = `mutation{
productCreate(input:{
attributes:[{
id:"${attributeId}"
}]
name:"${name}"
productType:"${productType}"
category:"${category}"
}){
product{
id
}
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
createVariant(
productId,
sku,
warehouseId,
quantity,
channelId,
price = 1,
costPrice = 1
) {
let channelListings = "";
let stocks = "";
if (channelId) {
channelListings = `channelListings:{
channelId:"${channelId}"
price:"${price}"
costPrice:"${costPrice}"
}`;
}
if (warehouseId) {
stocks = `stocks:{
warehouse:"${warehouseId}"
quantity:${quantity}
}`;
}
const mutation = `mutation{
productVariantBulkCreate(product:"${productId}", variants:{
attributes:[]
sku:"${sku}"
${channelListings}
${stocks}
}){
productVariants{
id
name
}
bulkProductErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
createTypeProduct(name, attributeId, slug = name) {
const mutation = `mutation{
productTypeCreate(input:{
name:"${name}"
slug: "${slug}"
isShippingRequired:true
productAttributes:"${attributeId}"
}){
productErrors{
field
message
}
productType{
id
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
deleteProduct(productId) {
const mutation = `mutation{
productDelete(id:"${productId}"){
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
getProductTypes(first, search) {
const query = `query{
productTypes(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy.sendRequestWithQuery(query);
}
deleteProductType(productTypeId) {
const mutation = `mutation{
productTypeDelete(id:"${productTypeId}"){
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
}
export default Product;

View file

@ -0,0 +1,13 @@
class ShopInfo {
getShopInfo() {
const query = `query{
shop{
domain{
url
}
}
}`;
return cy.sendRequestWithQuery(query);
}
}
export default ShopInfo;

View file

@ -1,5 +1,6 @@
/* eslint-disable sort-keys */
export const PRODUCTS_SELECTORS = {
productsList: "[data-test-id][data-test='id']",
products: "[data-test='submenu-item-label'][data-test-id='products']",
createProductBtn: "[data-test='add-product']",
productNameInput: "[name='name']",
@ -11,5 +12,16 @@ export const PRODUCTS_SELECTORS = {
visibleRadioBtn: "[name='isPublished']",
saveBtn: "[data-test='button-bar-confirm']",
confirmationMsg: "[data-test='notification-success']",
channelAvailabilityItem: "[data-test='channel-availability-item']"
channelAvailabilityItem: "[data-test='channel-availability-item']",
searchProducts: "[placeholder='Search Products...']",
availableManageButton:
"[data-test-id='channels-availiability-manage-button']",
channelsAvailabilityForm:
"[data-test-id='manage-products-channels-availiability-list']",
channelAvailabilityColumn:
"[data-test='availability'][data-test-availability='true']",
channelAvailabilityList: "ul[role='menu']",
goBackButton: "[data-test-id='app-header-back-button']",
assignedChannels: "[data-test='channel-availability-item']",
publishedRadioButton: "[role=radiogroup]"
};

View file

@ -0,0 +1,5 @@
export const SEARCH_SELECTORS = {
searchButton: "[data-test='menuSearchOverlayLink']",
searchInputField: "[placeholder='search']",
productItem: ".search__products__item"
};

View file

@ -0,0 +1,5 @@
{
"plAddress":{
"currency": "PLN"
}
}

View file

@ -1,47 +0,0 @@
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
// <reference types="cypress" />
describe("Products", () => {
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should add new visible product", () => {
cy.visit("/")
.get(LEFT_MENU_SELECTORS.catalog)
.click()
.get(PRODUCTS_SELECTORS.products)
.click()
.get(PRODUCTS_SELECTORS.createProductBtn)
.click()
.get(PRODUCTS_SELECTORS.productNameInput)
.click()
.type("Visible test product")
.get(PRODUCTS_SELECTORS.productTypeInput)
.click()
.get(PRODUCTS_SELECTORS.autocompleteDropdown) // trying to fill autocomplete before dropdown will cause error
.get(PRODUCTS_SELECTORS.productTypeInput)
.click()
.type("Cushion")
.get(PRODUCTS_SELECTORS.categoryItem)
.should("have.length", 1)
.get(PRODUCTS_SELECTORS.firstCategoryItem)
.click()
.get(PRODUCTS_SELECTORS.categoryInput)
.click()
.get(PRODUCTS_SELECTORS.categoryItem)
.first()
.click()
.get(PRODUCTS_SELECTORS.channelAvailabilityItem)
.first()
.click()
.get(PRODUCTS_SELECTORS.visibleRadioBtn)
.first()
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.get(PRODUCTS_SELECTORS.confirmationMsg)
.contains("Product created");
});
});

View file

@ -0,0 +1,109 @@
import faker from "faker";
import Channels from "../../apiRequests/Channels";
import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
import { URL_LIST } from "../../url/url-list";
import ChannelsUtils from "../../utils/channelsUtils";
import ProductsUtils from "../../utils/productsUtils";
// <reference types="cypress" />
describe("Products", () => {
const channels = new Channels();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const startsWith = "Cy-";
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProducts(startsWith);
channelsUtils.deleteChannels(startsWith);
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
xit("should add new visible product", () => {
cy.visit("/")
.get(LEFT_MENU_SELECTORS.catalog)
.click()
.get(PRODUCTS_SELECTORS.products)
.click()
.get(PRODUCTS_SELECTORS.createProductBtn)
.click()
.get(PRODUCTS_SELECTORS.productNameInput)
.click()
.type("Visible test product")
.get(PRODUCTS_SELECTORS.productTypeInput)
.click()
.get(PRODUCTS_SELECTORS.autocompleteDropdown) // trying to fill autocomplete before dropdown will cause error
.get(PRODUCTS_SELECTORS.productTypeInput)
.click()
.type("Cushion")
.get(PRODUCTS_SELECTORS.categoryItem)
.should("have.length", 1)
.get(PRODUCTS_SELECTORS.firstCategoryItem)
.click()
.get(PRODUCTS_SELECTORS.categoryInput)
.click()
.get(PRODUCTS_SELECTORS.categoryItem)
.first()
.click()
.get(PRODUCTS_SELECTORS.channelAvailabilityItem)
.first()
.click()
.get(PRODUCTS_SELECTORS.visibleRadioBtn)
.first()
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.get(PRODUCTS_SELECTORS.confirmationMsg)
.contains("Product created");
});
it("should display correct availibility for product in channel", () => {
const name = `${startsWith}${faker.random.number()}`;
cy.fixture("addresses").then(json => {
channels.createChannel(true, name, name, json.plAddress.currency);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
const productTypeId = productsUtils.getProductTypeId();
const attributeId = productsUtils.getAttributeId();
const categoryId = productsUtils.getCategoryId();
productsUtils.createProduct(
name,
attributeId,
productTypeId,
categoryId
);
cy.visit(URL_LIST.products)
.get(PRODUCTS_SELECTORS.searchProducts)
.type(name)
.get(PRODUCTS_SELECTORS.productsList)
.contains(name)
.click()
.get(PRODUCTS_SELECTORS.availableManageButton)
.click()
.get(PRODUCTS_SELECTORS.channelsAvailabilityForm)
.contains(name)
.click()
.get(BUTTON_SELECTORS.submit)
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.get(PRODUCTS_SELECTORS.goBackButton)
.click()
.get(PRODUCTS_SELECTORS.searchProducts)
.type(name)
.get(PRODUCTS_SELECTORS.productsList)
.contains(name)
.parentsUntil("tbody")
.find(PRODUCTS_SELECTORS.channelAvailabilityColumn)
.click()
.get(PRODUCTS_SELECTORS.channelAvailabilityList)
.contains(name);
});
});
});
});

View file

@ -0,0 +1,141 @@
import faker from "faker";
import ShopInfo from "../../apiRequests/ShopInfo";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors";
import SearchSteps from "../../steps/frontendSteps/searchSteps";
import { URL_LIST } from "../../url/url-list";
import ChannelsUtils from "../../utils/channelsUtils";
import ProductsUtils from "../../utils/productsUtils";
// <reference types="cypress" />
describe("Products", () => {
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const searchSteps = new SearchSteps();
const shopInfo = new ShopInfo();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productTypeId;
let attributeId;
let categoryId;
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProducts(startsWith);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productTypeId = productsUtils.getProductTypeId();
attributeId = productsUtils.getAttributeId();
categoryId = productsUtils.getCategoryId();
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
shopInfo
.getShopInfo()
.its("body.data.shop.domain.url")
.as("shopUrl");
});
xit("should display on frontend only published products", () => {
const productName = `${startsWith}${faker.random.number()}`;
channelsUtils.getDefaultChannel().then(defaultChannel => {
productsUtils
.createProductInChannel(
productName,
productTypeId,
attributeId,
categoryId,
defaultChannel.id,
false,
false
)
.then(() => {
cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`)
.get(PRODUCTS_SELECTORS.assignedChannels)
.click()
.get(PRODUCTS_SELECTORS.publishedRadioButton)
.contains("Opublikowany")
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.waitForGraph("ProductChannelListingUpdate")
.get("@shopUrl")
.then(shopUrl => {
cy.visit(shopUrl);
searchSteps.searchFor(name);
cy.get(SEARCH_SELECTORS.productItem).contains(name);
});
});
});
});
it("shouldn't display not published product for unlogged user", () => {
const productName = `${startsWith}${faker.random.number()}`;
channelsUtils.getDefaultChannel().then(defaultChannel => {
productsUtils
.createProductInChannel(
productName,
productTypeId,
attributeId,
categoryId,
defaultChannel.id,
true,
false
)
.then(() => {
cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`)
.get(PRODUCTS_SELECTORS.assignedChannels)
.click()
.get(PRODUCTS_SELECTORS.publishedRadioButton)
.contains("Nie opublikowano")
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.waitForGraph("ProductChannelListingUpdate")
.get("@shopUrl")
.then(shopUrl => {
cy.visit(shopUrl);
searchSteps.searchFor(name);
cy.get(SEARCH_SELECTORS.productItem).should("not.exist");
});
});
});
});
xit("should display not published product for staff member", () => {
const productName = `${startsWith}${faker.random.number()}`;
channelsUtils.getDefaultChannel().then(defaultChannel => {
productsUtils
.createProductInChannel(
productName,
productTypeId,
attributeId,
categoryId,
defaultChannel.id,
true,
false
)
.then(() => {
cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`)
.get(PRODUCTS_SELECTORS.assignedChannels)
.click()
.get(PRODUCTS_SELECTORS.publishedRadioButton)
.contains("Nie opublikowano")
.click()
.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.waitForGraph("ProductChannelListingUpdate")
.get("@shopUrl")
.then(shopUrl => {
cy.visit(shopUrl)
.loginInShop()
.then(() => {
searchSteps.searchFor(name);
cy.get(SEARCH_SELECTORS.productItem).contains(name);
});
});
});
});
});
});

View file

@ -0,0 +1,11 @@
import { SEARCH_SELECTORS } from "../../elements/frontend-elements/search-selectors";
class SearchSteps {
searchFor(query) {
cy.get(SEARCH_SELECTORS.searchButton)
.click()
.get(SEARCH_SELECTORS.searchInputField)
.type(query);
}
}
export default SearchSteps;

View file

@ -17,3 +17,38 @@ Cypress.Commands.add("clearSessionData", () => {
}
});
});
Cypress.Commands.add("sendRequestWithQuery", query =>
cy.request({
method: "POST",
body: {
method: "POST",
url: Cypress.env("API_URI"),
query
},
headers: {
Authorization: `JWT ${window.sessionStorage.getItem("auth")}`
},
url: Cypress.env("API_URI")
})
);
Cypress.Commands.add("waitForGraph", operationName => {
const GRAPH_URL = "/graphql";
cy.intercept("POST", GRAPH_URL, req => {
req.statusCode = 200;
const requestBody = req.body;
if (Array.isArray(requestBody)) {
requestBody.forEach(element => {
if (element.operationName === operationName) {
req.alias = operationName;
}
});
} else {
if (requestBody.operationName === operationName) {
req.alias = operationName;
}
}
});
cy.wait(`@${operationName}`);
});

View file

@ -46,3 +46,23 @@ Cypress.Commands.add("loginUserViaRequest", () => {
window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token);
});
});
Cypress.Commands.add("loginInShop", () => {
cy.request({
method: "POST",
url: Cypress.env("API_URI"),
body: [
{
operationName: "TokenAuth",
variables: {
email: Cypress.env("USER_NAME"),
password: Cypress.env("USER_PASSWORD")
},
query:
"mutation TokenAuth($email: String!, $password: String!) {\n tokenCreate(email: $email, password: $password) {\n token\n errors: accountErrors {\n code\n field\n message\n __typename\n }\n user {\n id\n __typename\n }\n __typename\n }\n}\n"
}
]
}).then(resp => {
window.localStorage.setItem("token", resp.body[0].data.tokenCreate.token);
});
});

6
cypress/url/url-list.js Normal file
View file

@ -0,0 +1,6 @@
export const URL_LIST = {
dashbord: "/",
channels: "/channels/",
products: "/products/",
orders: "/orders/"
};

View file

@ -0,0 +1,38 @@
import Channels from "../apiRequests/Channels";
class ChannelsUtils {
channels = new Channels();
deleteChannels(nameStartsWith) {
this.channels.getChannels().then(resp => {
const channelsArray = new Set(resp.body.data.channels);
if (channelsArray) {
channelsArray.forEach(element => {
if (element.name.startsWith(nameStartsWith)) {
const targetChannels = Array.from(channelsArray).filter(function(
channelElement
) {
return (
element.currencyCode === channelElement.currencyCode &&
element.id !== channelElement.id
);
});
if (targetChannels[0]) {
this.channels.deleteChannel(element.id, targetChannels[0].id);
channelsArray.delete(element);
}
}
});
}
});
}
getDefaultChannel() {
return this.channels.getChannels().then(resp => {
const channelsArray = resp.body.data.channels;
return channelsArray.find(function(channelElement) {
return channelElement.slug === "default-channel";
});
});
}
}
export default ChannelsUtils;

View file

@ -0,0 +1,153 @@
import Attribute from "../apiRequests/Attribute";
import Category from "../apiRequests/Category";
import Product from "../apiRequests/Product";
class ProductsUtils {
createdVariantId;
createdProductId;
productTypeId;
attributeId;
categoryId;
updateChannelInProduct(productsList, channelId) {
const product = new Product();
productsList.forEach(productElement => {
product.updateChannelInProduct(productElement.node.id, channelId);
const variants = productElement.node.variants;
variants.forEach(variant => {
product.updateChannelPriceInVariant(variant.id, channelId);
});
});
}
createProduct(name, attributeId, productTypeId, categoryId) {
const product = new Product();
return product
.createProduct(attributeId, name, productTypeId, categoryId)
.then(createProductResp => {
this.createdProductId =
createProductResp.body.data.productCreate.product.id;
return product
.createVariant(this.createdProductId, name)
.then(createVariantResp => {
this.createdVariantId =
createVariantResp.body.data.productVariantBulkCreate.productVariants;
});
});
}
createProductInChannel(
name,
productTypeId,
attributeId,
categoryId,
channelId,
isPublished,
isAvailableForPurchase,
warehouseId,
quantityInWarehouse,
price
) {
const product = new Product();
return product
.createProduct(attributeId, name, productTypeId, categoryId)
.then(createProductResp => {
this.createdProductId =
createProductResp.body.data.productCreate.product.id;
return product
.updateChannelInProduct(
this.createdProductId,
channelId,
isPublished,
isAvailableForPurchase
)
.then(() =>
product
.createVariant(
this.createdProductId,
name,
warehouseId,
quantityInWarehouse,
channelId,
price
)
.then(createVariantResp => {
this.createdVariantId =
createVariantResp.body.data.productVariantBulkCreate.productVariants;
})
);
});
}
createTypeAttributeAndCategoryForProduct(name) {
const attribute = new Attribute();
const category = new Category();
const product = new Product();
return attribute.createAttribute(name).then(createAttributeResp => {
this.attributeId =
createAttributeResp.body.data.attributeCreate.attribute.id;
return product
.createTypeProduct(name, this.attributeId)
.then(createTypeProductResp => {
this.productTypeId =
createTypeProductResp.body.data.productTypeCreate.productType.id;
return category.createCategory(name).then(categoryResp => {
this.categoryId = categoryResp.body.data.categoryCreate.category.id;
});
});
});
}
getCreatedVariants() {
return this.createdVariantId;
}
getProductTypeId() {
return this.productTypeId;
}
getAttributeId() {
return this.attributeId;
}
getCategoryId() {
return this.categoryId;
}
getCreatedProductId() {
return this.createdProductId;
}
deleteProducts(startsWith) {
const product = new Product();
const attribute = new Attribute();
const category = new Category();
product.getProductTypes(100, startsWith).then(resp => {
const productTypes = resp.body.data.productTypes.edges;
productTypes.forEach(productType => {
if (productType.node.name.includes(startsWith)) {
product.deleteProductType(productType.node.id);
}
});
});
attribute.getAttributes(100, startsWith).then(resp => {
const attributes = resp.body.data.attributes.edges;
attributes.forEach(attributeElement => {
if (attributeElement.node.name.includes(startsWith)) {
attribute.deleteAttribute(attributeElement.node.id);
}
});
});
category.getCategories(100, startsWith).then(resp => {
const categories = resp.body.data.categories.edges;
categories.forEach(categoryElement => {
if (categoryElement.node.name.includes(startsWith)) {
category.deleteCategory(categoryElement.node.id);
}
});
});
product.getFirstProducts(100, startsWith).then(getProductResp => {
const products = getProductResp.body.data.products.edges;
products.forEach(productElement => {
if (productElement.node.name.includes(startsWith)) {
product.deleteProducts(productElement.node.id);
}
});
});
}
}
export default ProductsUtils;