From f1d4b11a81f734deb9d292dfcf87674970c5f632 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Tue, 19 Jan 2021 22:17:49 +0100 Subject: [PATCH 01/56] test for elements visibility, TODO - api requests for creating order --- .../elements/dashboard/dashboard-selectors.js | 11 +++ cypress/fixtures/addresses.json | 12 +++ cypress/integration/dashboard.js | 36 +++++++++ cypress/support/customer/index.js | 37 +++++++++ cypress/support/index.js | 3 + cypress/support/shippingMethod.js | 48 +++++++++++ cypress/support/softAsserations/index.js | 81 +++++++++++++++++++ 7 files changed, 228 insertions(+) create mode 100644 cypress/elements/dashboard/dashboard-selectors.js create mode 100644 cypress/fixtures/addresses.json create mode 100644 cypress/integration/dashboard.js create mode 100644 cypress/support/customer/index.js create mode 100644 cypress/support/shippingMethod.js create mode 100644 cypress/support/softAsserations/index.js diff --git a/cypress/elements/dashboard/dashboard-selectors.js b/cypress/elements/dashboard/dashboard-selectors.js new file mode 100644 index 000000000..5c71fe47e --- /dev/null +++ b/cypress/elements/dashboard/dashboard-selectors.js @@ -0,0 +1,11 @@ +export const DASHBOARD_SELECTORS = { + sales: "div:nth-child(1) > [class*='HomeAnalyticsCard-cardContent']", + orders: "div:nth-child(2) > [class*='HomeAnalyticsCard-cardContent']", + activity: "[class*='Grid-root'] > div:nth-child(2) > [class*='MuiPaper']", + topProducts: + "[class*='Grid-root'] > div:nth-child(1) > [class*='MuiPaper']:nth-child(4)", + ordersReadyToFulfill: "[class*='HomeNotificationTable'] > tr:nth-child(1)", + paymentsWaitingForCapture: + "[class*='HomeNotificationTable'] > tr:nth-child(2)", + productsOutOfStock: "[class*='HomeNotificationTable'] > tr:nth-child(3)" +}; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json new file mode 100644 index 000000000..742bd227e --- /dev/null +++ b/cypress/fixtures/addresses.json @@ -0,0 +1,12 @@ +{ + "plAddress": { + "companyName": "Test3", + "streetAddress1": "Smolna", + "streetAddress2": "13/1", + "city": "Wrocław", + "postalCode": "53-346", + "country": "PL", + "countryArea": "Dolny Śląsk", + "phone": "123456787" + } +} \ No newline at end of file diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js new file mode 100644 index 000000000..ad6ee2945 --- /dev/null +++ b/cypress/integration/dashboard.js @@ -0,0 +1,36 @@ +// import { DASHBOARD_SELECTORS } from "../elements/dashboard/dashboard-selectors"; + +// // +// describe("User authorization", () => { +// beforeEach(() => { +// cy.clearSessionData().loginUserViaRequest(); +// }); + +// xit("should all elements be visible on the dashboard", () => { +// cy.visit("/"); +// softAssertVisibility(DASHBOARD_SELECTORS.sales); +// softAssertVisibility(DASHBOARD_SELECTORS.orders); +// softAssertVisibility(DASHBOARD_SELECTORS.activity); +// softAssertVisibility(DASHBOARD_SELECTORS.topProducts); +// softAssertVisibility(DASHBOARD_SELECTORS.ordersReadyToFulfill); +// softAssertVisibility(DASHBOARD_SELECTORS.paymentsWaitingForCapture); +// softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); +// }); + +// xit("aa", () => { +// // cy.fixture('addresses').then((json) => { +// // cy.createCustomer("Test9","Test9", json.plAddress); +// // }); +// // createChannel(); +// // createRateShipping(); +// // addChannelToProduct(); +// // createOrder(); +// }); + +// function softAssertVisibility(selector){ +// const {softExpect} = chai; +// cy.get(selector).then( element => { +// softExpect(element).to.be.visible; +// }) +// } +// }); diff --git a/cypress/support/customer/index.js b/cypress/support/customer/index.js new file mode 100644 index 000000000..0a900581c --- /dev/null +++ b/cypress/support/customer/index.js @@ -0,0 +1,37 @@ +Cypress.Commands.add( + "createCustomer", + (email, name, address, isActive = false) => { + const mustation = ` + mutation{ + customerCreate(input:{ + firstName: "${name}" + lastName: "${name}" + email: "${email}" + isActive: ${isActive} + defaultBillingAddress: { + companyName: "${address.companyName}" + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + defaultShippingAddress: { + companyName: "${address.companyName}" + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + }){ + accountErrors{ + code + } + } + } + `; + } +); diff --git a/cypress/support/index.js b/cypress/support/index.js index 531b8aab9..c863c2167 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,4 +1,7 @@ import "./user"; +import "./softAsserations"; +import "./customer"; +import "./shippingMethod"; Cypress.Commands.add("clearSessionData", () => { // Because of known cypress bug, not all local storage data are cleared. diff --git a/cypress/support/shippingMethod.js b/cypress/support/shippingMethod.js new file mode 100644 index 000000000..84fa3cfd7 --- /dev/null +++ b/cypress/support/shippingMethod.js @@ -0,0 +1,48 @@ +Cypress.Commands.add("createShippingRate", (name, shippingZone) => { + const mutation = ` + mutation{ + CreateShippingRate(input:{ + maximumDeliveryDays: null + minimumDeliveryDays: null + name: "${name}" + shippingZone: "${shippingZone}" + type: "PRICE" + }) + } + `; +}); + +Cypress.Commands.add("createShippingZone", (name, country) => { + const mutation = ` + mutation{ + shippingZoneCreate(input:{ + name: "${name}" + countries: "${country}" + }){ + shippingZone{ + id + } + } + } + `; +}); + +Cypress.Commands.add("", (shippingRateId, channelId) => { + const mutation = ` + mutation{ + shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ + addChannels: { + channelId:"${channelId}" + } + }){ + shippingMethod{ + id + } + shippingErrors{ + code + message + } + } + } + `; +}); diff --git a/cypress/support/softAsserations/index.js b/cypress/support/softAsserations/index.js new file mode 100644 index 000000000..3405514da --- /dev/null +++ b/cypress/support/softAsserations/index.js @@ -0,0 +1,81 @@ +// let isSoftAssertion = false; +// let errors = []; + +// chai.softExpect = function ( ...args ) { +// isSoftAssertion = true; +// return chai.expect(...args); +// }, +// chai.softAssert = function ( ...args ) { +// isSoftAssertion = true; +// return chai.assert(...args); +// } + +// const origAssert = chai.Assertion.prototype.assert; +// chai.Assertion.prototype.assert = function (...args) { +// if ( isSoftAssertion ) { +// try { +// origAssert.call(this, ...args) +// } catch ( error ) { +// errors.push(error); +// } +// isSoftAssertion = false; +// } else { + +// origAssert.call(this, ...args) +// } +// }; + +// // monkey-patch `Cypress.log` so that the last `cy.then()` isn't logged to command log +// const origLog = Cypress.log; +// Cypress.log = function ( data ) { +// if ( data && data.error && /soft assertions/i.test(data.error.message) ) { +// data.error.message = '\n\n\t' + data.error.message + '\n\n'; +// throw data.error; +// } +// return origLog.call(Cypress, ...arguments); +// }; + +// // monkey-patch `it` callback so we insert `cy.then()` as a last command +// // to each test case where we'll assert if there are any soft assertion errors +// function itCallback ( func ) { +// func(); +// cy.then(() => { +// if ( errors.length ) { +// const _ = Cypress._; +// let msg = ''; + +// if ( Cypress.browser.isHeaded ) { + +// msg = 'Failed soft assertions... check log above ↑'; +// } else { + +// _.each( errors, error => { +// msg += '\n' + error; +// }); + +// msg = msg.replace(/^/gm, '\t'); +// } + +// throw new Error(msg); +// } +// }); +// } + +// const origIt = window.it; +// window.it = (title, func) => { +// origIt(title, func && (() => itCallback(func))); +// }; +// window.it.only = (title, func) => { +// origIt.only(title, func && (() => itCallback(func))); +// }; +// window.it.skip = (title, func) => { +// origIt.skip(title, func); +// }; + +// beforeEach(() => { +// errors = []; +// }); +// afterEach(() => { +// errors = []; +// isSoftAssertion = false; +// }); From f9e80901207c297dba5cce4d801114e1648569de Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 21 Jan 2021 11:05:54 +0100 Subject: [PATCH 02/56] Add mutation query --- cypress/fixtures/example.json | 5 + cypress/integration/dashboard.js | 61 ++++---- .../index.js} | 3 + cypress/support/softAsserations/index.js | 141 +++++++++--------- 4 files changed, 106 insertions(+), 104 deletions(-) create mode 100644 cypress/fixtures/example.json rename cypress/support/{shippingMethod.js => shippingMethod/index.js} (94%) diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 000000000..da18d9352 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js index ad6ee2945..d4f1c0749 100644 --- a/cypress/integration/dashboard.js +++ b/cypress/integration/dashboard.js @@ -1,36 +1,33 @@ -// import { DASHBOARD_SELECTORS } from "../elements/dashboard/dashboard-selectors"; +import { DASHBOARD_SELECTORS } from "../elements/dashboard/dashboard-selectors"; -// // -// describe("User authorization", () => { -// beforeEach(() => { -// cy.clearSessionData().loginUserViaRequest(); -// }); +// +describe("User authorization", () => { + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); -// xit("should all elements be visible on the dashboard", () => { -// cy.visit("/"); -// softAssertVisibility(DASHBOARD_SELECTORS.sales); -// softAssertVisibility(DASHBOARD_SELECTORS.orders); -// softAssertVisibility(DASHBOARD_SELECTORS.activity); -// softAssertVisibility(DASHBOARD_SELECTORS.topProducts); -// softAssertVisibility(DASHBOARD_SELECTORS.ordersReadyToFulfill); -// softAssertVisibility(DASHBOARD_SELECTORS.paymentsWaitingForCapture); -// softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); -// }); + it("should all elements be visible on the dashboard", () => { + cy.visit("/"); + softAssertVisibility(DASHBOARD_SELECTORS.sales); + softAssertVisibility(DASHBOARD_SELECTORS.orders); + softAssertVisibility(DASHBOARD_SELECTORS.activity); + softAssertVisibility(DASHBOARD_SELECTORS.topProducts); + softAssertVisibility(DASHBOARD_SELECTORS.ordersReadyToFulfill); + softAssertVisibility(DASHBOARD_SELECTORS.paymentsWaitingForCapture); + softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); + }); -// xit("aa", () => { -// // cy.fixture('addresses').then((json) => { -// // cy.createCustomer("Test9","Test9", json.plAddress); -// // }); -// // createChannel(); -// // createRateShipping(); -// // addChannelToProduct(); -// // createOrder(); -// }); + xit("aa", () => { + cy.fixture("addresses").then(json => { + cy.createCustomer("Test9", "Test9", json.plAddress); + }); + createChannel(); + createRateShipping(); + addChannelToProduct(); + createOrder(); + }); -// function softAssertVisibility(selector){ -// const {softExpect} = chai; -// cy.get(selector).then( element => { -// softExpect(element).to.be.visible; -// }) -// } -// }); + function softAssertVisibility(selector) { + cy.get(selector).then(element => chai.softExpect(element).to.be.visible); + } +}); diff --git a/cypress/support/shippingMethod.js b/cypress/support/shippingMethod/index.js similarity index 94% rename from cypress/support/shippingMethod.js rename to cypress/support/shippingMethod/index.js index 84fa3cfd7..949b97651 100644 --- a/cypress/support/shippingMethod.js +++ b/cypress/support/shippingMethod/index.js @@ -10,6 +10,7 @@ Cypress.Commands.add("createShippingRate", (name, shippingZone) => { }) } `; + return mutation; }); Cypress.Commands.add("createShippingZone", (name, country) => { @@ -25,6 +26,7 @@ Cypress.Commands.add("createShippingZone", (name, country) => { } } `; + return mutation; }); Cypress.Commands.add("", (shippingRateId, channelId) => { @@ -45,4 +47,5 @@ Cypress.Commands.add("", (shippingRateId, channelId) => { } } `; + return mutation; }); diff --git a/cypress/support/softAsserations/index.js b/cypress/support/softAsserations/index.js index 3405514da..07dc3dcff 100644 --- a/cypress/support/softAsserations/index.js +++ b/cypress/support/softAsserations/index.js @@ -1,81 +1,78 @@ -// let isSoftAssertion = false; -// let errors = []; +let isSoftAssertion = false; +let errors = []; -// chai.softExpect = function ( ...args ) { -// isSoftAssertion = true; -// return chai.expect(...args); -// }, -// chai.softAssert = function ( ...args ) { -// isSoftAssertion = true; -// return chai.assert(...args); -// } +chai.softExpect = function(...args) { + isSoftAssertion = true; + return chai.expect(...args); +}; +chai.softAssert = function(...args) { + isSoftAssertion = true; + return chai.assert(...args); +}; -// const origAssert = chai.Assertion.prototype.assert; -// chai.Assertion.prototype.assert = function (...args) { -// if ( isSoftAssertion ) { -// try { -// origAssert.call(this, ...args) -// } catch ( error ) { -// errors.push(error); -// } -// isSoftAssertion = false; -// } else { +const origAssert = chai.Assertion.prototype.assert; +chai.Assertion.prototype.assert = function(...args) { + if (isSoftAssertion) { + try { + origAssert.call(this, ...args); + } catch (error) { + errors.push(error); + } + isSoftAssertion = false; + } else { + origAssert.call(this, ...args); + } +}; -// origAssert.call(this, ...args) -// } -// }; +// monkey-patch `Cypress.log` so that the last `cy.then()` isn't logged to command log +const origLog = Cypress.log; +Cypress.log = function(data) { + if (data && data.error && /soft assertions/i.test(data.error.message)) { + data.error.message = "\n\n\t" + data.error.message + "\n\n"; + throw data.error; + } + return origLog.call(Cypress, ...arguments); +}; -// // monkey-patch `Cypress.log` so that the last `cy.then()` isn't logged to command log -// const origLog = Cypress.log; -// Cypress.log = function ( data ) { -// if ( data && data.error && /soft assertions/i.test(data.error.message) ) { -// data.error.message = '\n\n\t' + data.error.message + '\n\n'; -// throw data.error; -// } -// return origLog.call(Cypress, ...arguments); -// }; +// monkey-patch `it` callback so we insert `cy.then()` as a last command +// to each test case where we'll assert if there are any soft assertion errors +function itCallback(func) { + func(); + cy.then(() => { + if (errors.length) { + const _ = Cypress._; + let msg = ""; -// // monkey-patch `it` callback so we insert `cy.then()` as a last command -// // to each test case where we'll assert if there are any soft assertion errors -// function itCallback ( func ) { -// func(); -// cy.then(() => { -// if ( errors.length ) { -// const _ = Cypress._; -// let msg = ''; + if (Cypress.browser.isHeaded) { + msg = "Failed soft assertions... check log above ↑"; + } else { + _.each(errors, error => { + msg += "\n" + error; + }); -// if ( Cypress.browser.isHeaded ) { + msg = msg.replace(/^/gm, "\t"); + } -// msg = 'Failed soft assertions... check log above ↑'; -// } else { + throw new Error(msg); + } + }); +} -// _.each( errors, error => { -// msg += '\n' + error; -// }); +const origIt = window.it; +window.it = (title, func) => { + origIt(title, func && (() => itCallback(func))); +}; +window.it.only = (title, func) => { + origIt.only(title, func && (() => itCallback(func))); +}; +window.it.skip = (title, func) => { + origIt.skip(title, func); +}; -// msg = msg.replace(/^/gm, '\t'); -// } - -// throw new Error(msg); -// } -// }); -// } - -// const origIt = window.it; -// window.it = (title, func) => { -// origIt(title, func && (() => itCallback(func))); -// }; -// window.it.only = (title, func) => { -// origIt.only(title, func && (() => itCallback(func))); -// }; -// window.it.skip = (title, func) => { -// origIt.skip(title, func); -// }; - -// beforeEach(() => { -// errors = []; -// }); -// afterEach(() => { -// errors = []; -// isSoftAssertion = false; -// }); +beforeEach(() => { + errors = []; +}); +afterEach(() => { + errors = []; + isSoftAssertion = false; +}); From 95be7b5163a7cbc691a70b511fc166b5dbfb9ffa Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Wed, 27 Jan 2021 10:41:55 +0100 Subject: [PATCH 03/56] order ready to fulfill created --- cypress/api/Customer.js | 84 ++++++++++++++++++ cypress/api/Order.js | 60 +++++++++++++ cypress/api/Product.js | 55 ++++++++++++ cypress/api/ShippingMethod.js | 97 +++++++++++++++++++++ cypress/integration/dashboard.js | 111 ++++++++++++++++++++++-- cypress/support/customer/index.js | 37 -------- cypress/support/index.js | 17 +++- cypress/support/shippingMethod/index.js | 51 ----------- 8 files changed, 414 insertions(+), 98 deletions(-) create mode 100644 cypress/api/Customer.js create mode 100644 cypress/api/Order.js create mode 100644 cypress/api/Product.js create mode 100644 cypress/api/ShippingMethod.js delete mode 100644 cypress/support/customer/index.js delete mode 100644 cypress/support/shippingMethod/index.js diff --git a/cypress/api/Customer.js b/cypress/api/Customer.js new file mode 100644 index 000000000..c9beb7042 --- /dev/null +++ b/cypress/api/Customer.js @@ -0,0 +1,84 @@ +export class Customer { + createCustomer(email, customerName, address, isActive = false) { + const mutation = ` + mutation{ + customerCreate(input:{ + firstName: "${customerName}" + lastName: "${customerName}" + email: "${email}" + isActive: ${isActive} + defaultBillingAddress: { + companyName: "${address.companyName}" + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + defaultShippingAddress: { + companyName: "${address.companyName}" + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + }){ + user{ + id + } + accountErrors{ + code + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + deleteCustomers(startsWith) { + this.getCustomers(startsWith).then(resp => { + if (resp.body.data.customers) { + const customers = resp.body.data.customers.edges; + customers.forEach(element => { + if (element.node.email.includes(startsWith)) { + this.deleteCustomer(element.node.id); + } + }); + } + }); + } + + deleteCustomer(customerId) { + const mutation = `mutation{ + customerDelete(id:"${customerId}"){ + accountErrors{ + code + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + getCustomers(startsWith) { + const query = `query{ + customers(first:100, filter: { + search: "${startsWith}" + }){ + edges{ + node{ + id + email + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } +} +export default Customer; diff --git a/cypress/api/Order.js b/cypress/api/Order.js new file mode 100644 index 000000000..571f27fb0 --- /dev/null +++ b/cypress/api/Order.js @@ -0,0 +1,60 @@ +class Order { + markOrderAsPaid(orderId) { + const mutation = `mutation{ + orderMarkAsPaid(id:"${orderId}"){ + orderErrors{ + message + } + } + }`; + cy.sendRequestWithQuery(mutation); + } + + addProductToOrder(orderId, variantId, quantity = 1) { + const mutation = `mutation{ + draftOrderLinesCreate(id:"${orderId}", input:{ + quantity:${quantity} + variantId: "${variantId}" + }){ + orderErrors{ + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + + createDraftOrder(customerId, shippingMethodId, channelId) { + const mutation = ` + mutation{ + draftOrderCreate(input:{ + user:"${customerId}" + shippingMethod:"${shippingMethodId}" + channel: "${channelId}" + }){ + orderErrors{ + message + } + order{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + completeOrder(orderId) { + const mutation = `mutation{ + draftOrderComplete(id:"${orderId}"){ + order{ + id + } + orderErrors{ + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Order; diff --git a/cypress/api/Product.js b/cypress/api/Product.js new file mode 100644 index 000000000..bbffa4312 --- /dev/null +++ b/cypress/api/Product.js @@ -0,0 +1,55 @@ +class Product { + getFirstProducts(first) { + const query = `query{ + products(first:${first}){ + edges{ + node{ + id + name + variants{ + id + } + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } + + updateChannelInProduct(productId, channelId) { + const mutation = `mutation{ + productChannelListingUpdate(id:"${productId}", input:{ + addChannels:{ + channelId:"${channelId}" + isPublished: true + isAvailableForPurchase:true + 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); + } +} + +export default Product; diff --git a/cypress/api/ShippingMethod.js b/cypress/api/ShippingMethod.js new file mode 100644 index 000000000..179f1b824 --- /dev/null +++ b/cypress/api/ShippingMethod.js @@ -0,0 +1,97 @@ +class ShippingMethod { + createShippingRate(name, shippingZone) { + const mutation = ` + mutation{ + shippingPriceCreate(input:{ + name: "${name}" + shippingZone: "${shippingZone}" + type: PRICE + }){ + shippingMethod{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + createShippingZone(name, country) { + const mutation = ` + mutation{ + shippingZoneCreate(input:{ + name: "${name}" + countries: "${country}" + }){ + shippingZone{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + addChannelToShippingMethod(shippingRateId, channelId) { + const mutation = ` + mutation{ + shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ + addChannels: { + channelId:"${channelId}" + price:10 + } + }){ + shippingMethod{ + id + } + shippingErrors{ + code + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + deleteShippingZones(startsWith) { + this.getShippingZones().then(resp => { + if (resp.body.data.shippingZones) { + const shippingZone = resp.body.data.shippingZones.edges; + shippingZone.forEach(element => { + if (element.node.name.includes(startsWith)) { + this.deleteShippingZone(element.node.id); + } + }); + } + }); + } + + deleteShippingZone(shippingZoneId) { + const mutation = `mutation{ + shippingZoneDelete(id:"${shippingZoneId}"){ + shippingErrors{ + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + getShippingZones() { + const query = `query{ + shippingZones(first:100){ + edges{ + node{ + name + id + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } +} +export default ShippingMethod; diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js index d4f1c0749..44213df2e 100644 --- a/cypress/integration/dashboard.js +++ b/cypress/integration/dashboard.js @@ -1,12 +1,30 @@ +import faker from "faker"; + +import Customer from "../api/Customer"; +import Order from "../api/Order"; +import Product from "../api/Product"; +import ShippingMethod from "../api/ShippingMethod"; import { DASHBOARD_SELECTORS } from "../elements/dashboard/dashboard-selectors"; // describe("User authorization", () => { + const startsWith = "Cy-"; + + const customer = new Customer(); + const product = new Product(); + const order = new Order(); + const shippingMethod = new ShippingMethod(); + + before(() => { + customer.deleteCustomers(startsWith); + shippingMethod.deleteShippingZones(startsWith); + }); + beforeEach(() => { cy.clearSessionData().loginUserViaRequest(); }); - it("should all elements be visible on the dashboard", () => { + xit("should all elements be visible on the dashboard", () => { cy.visit("/"); softAssertVisibility(DASHBOARD_SELECTORS.sales); softAssertVisibility(DASHBOARD_SELECTORS.orders); @@ -17,17 +35,94 @@ describe("User authorization", () => { softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); }); - xit("aa", () => { - cy.fixture("addresses").then(json => { - cy.createCustomer("Test9", "Test9", json.plAddress); + it("should correct amount of orders be displayed", () => { + faker = require("faker"); + const randomName = startsWith + faker.random.number(); + const randomEmail = randomName + "@example.com"; + product.getFirstProducts(3).then(productsResp => { + const productsList = productsResp.body.data.products.edges; + productsList.forEach(productElement => { + product.updateChannelInProduct( + "Q2hhbm5lbDoxNzk=", + productElement.node.id + ); + const variants = productElement.node.variants; + variants.forEach(variant => { + product.updateChannelPriceInVariant(variant.id, "Q2hhbm5lbDoxNzk="); + }); + }); + cy.fixture("addresses").then(json => { + customer + .createCustomer(randomEmail, randomName, json.plAddress) + .as("createCustomerResponse") + .then(resp => { + const customerId = resp.body.data.customerCreate.user.id; + shippingMethod + .createShippingZone(randomName, "PL") + .then(shippingZoneResp => { + shippingMethod + .createShippingRate( + randomName, + shippingZoneResp.body.data.shippingZoneCreate.shippingZone + .id + ) + .then(rateResp => { + const shippingMethodId = + rateResp.body.data.shippingPriceCreate.shippingMethod.id; + shippingMethod + .addChannelToShippingMethod( + shippingMethodId, + "Q2hhbm5lbDoxNzk=" + ) + .then(shippingMethodResp => { + createReadyToFullfillOrder( + customerId, + shippingMethodId, + "Q2hhbm5lbDoxNzk=", + productsList + ); + }); + }); + }); + }); + }); }); - createChannel(); - createRateShipping(); - addChannelToProduct(); - createOrder(); + cy.visit("/"); + softAssertMatch(DASHBOARD_SELECTORS.orders, /^0/); + softAssertMatch(DASHBOARD_SELECTORS.ordersReadyToFulfill, /^Brak/); + softAssertMatch(DASHBOARD_SELECTORS.paymentsWaitingForCapture, /^Brak/); + softAssertMatch(DASHBOARD_SELECTORS.productsOutOfStock, /^Brak/); }); + function createReadyToFullfillOrder( + customerId, + shippingMethodId, + channelId, + productsList + ) { + order + .createDraftOrder(customerId, shippingMethodId, channelId) + .then(draftOrderResp => { + const orderId = draftOrderResp.body.data.draftOrderCreate.order.id; + productsList.forEach(productElement => { + productElement.node.variants.forEach(variantElement => { + order.addProductToOrder(orderId, variantElement.id); + }); + }); + order.markOrderAsPaid(orderId); + order.completeOrder(orderId); + }); + } + function softAssertVisibility(selector) { cy.get(selector).then(element => chai.softExpect(element).to.be.visible); } + + function softAssertMatch(selector, regexp) { + cy.get(selector) + .invoke("text") + .then(text => + chai.softExpect(assert.match(text, regexp, "regexp matches")) + ); + } }); diff --git a/cypress/support/customer/index.js b/cypress/support/customer/index.js deleted file mode 100644 index 0a900581c..000000000 --- a/cypress/support/customer/index.js +++ /dev/null @@ -1,37 +0,0 @@ -Cypress.Commands.add( - "createCustomer", - (email, name, address, isActive = false) => { - const mustation = ` - mutation{ - customerCreate(input:{ - firstName: "${name}" - lastName: "${name}" - email: "${email}" - isActive: ${isActive} - defaultBillingAddress: { - companyName: "${address.companyName}" - streetAddress1: "${address.streetAddress1}" - streetAddress2: "${address.streetAddress2}" - city: "${address.city}" - postalCode: "${address.postalCode}" - country: ${address.country} - phone: "${address.phone}" - } - defaultShippingAddress: { - companyName: "${address.companyName}" - streetAddress1: "${address.streetAddress1}" - streetAddress2: "${address.streetAddress2}" - city: "${address.city}" - postalCode: "${address.postalCode}" - country: ${address.country} - phone: "${address.phone}" - } - }){ - accountErrors{ - code - } - } - } - `; - } -); diff --git a/cypress/support/index.js b/cypress/support/index.js index c863c2167..3cfc0f3cf 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,7 +1,5 @@ import "./user"; import "./softAsserations"; -import "./customer"; -import "./shippingMethod"; Cypress.Commands.add("clearSessionData", () => { // Because of known cypress bug, not all local storage data are cleared. @@ -20,3 +18,18 @@ 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") + }) +); diff --git a/cypress/support/shippingMethod/index.js b/cypress/support/shippingMethod/index.js deleted file mode 100644 index 949b97651..000000000 --- a/cypress/support/shippingMethod/index.js +++ /dev/null @@ -1,51 +0,0 @@ -Cypress.Commands.add("createShippingRate", (name, shippingZone) => { - const mutation = ` - mutation{ - CreateShippingRate(input:{ - maximumDeliveryDays: null - minimumDeliveryDays: null - name: "${name}" - shippingZone: "${shippingZone}" - type: "PRICE" - }) - } - `; - return mutation; -}); - -Cypress.Commands.add("createShippingZone", (name, country) => { - const mutation = ` - mutation{ - shippingZoneCreate(input:{ - name: "${name}" - countries: "${country}" - }){ - shippingZone{ - id - } - } - } - `; - return mutation; -}); - -Cypress.Commands.add("", (shippingRateId, channelId) => { - const mutation = ` - mutation{ - shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ - addChannels: { - channelId:"${channelId}" - } - }){ - shippingMethod{ - id - } - shippingErrors{ - code - message - } - } - } - `; - return mutation; -}); From fe9f55ee81a5ccebee616190a3518a36eb04d5a1 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Tue, 2 Feb 2021 12:34:10 +0100 Subject: [PATCH 04/56] created orders and products, missing data-testid --- cypress/api/Product.js | 55 ------ cypress/apiRequests/Attribute.js | 49 +++++ cypress/apiRequests/Category.js | 43 +++++ cypress/apiRequests/Channels.js | 74 ++++++++ cypress/apiRequests/Checkout.js | 70 +++++++ cypress/{api => apiRequests}/Customer.js | 1 + cypress/{api => apiRequests}/Order.js | 2 +- cypress/apiRequests/Product.js | 178 ++++++++++++++++++ .../{api => apiRequests}/ShippingMethod.js | 17 +- cypress/apiRequests/Warehouse.js | 55 ++++++ cypress/elements/header/header-selectors.js | 4 + cypress/fixtures/addresses.json | 3 +- cypress/fixtures/example.json | 5 - cypress/integration/dashboard.js | 167 ++++++++-------- cypress/utils/ordersUtils.js | 46 +++++ cypress/utils/productsUtils.js | 124 ++++++++++++ cypress/utils/shippingUtils.js | 71 +++++++ 17 files changed, 808 insertions(+), 156 deletions(-) delete mode 100644 cypress/api/Product.js create mode 100644 cypress/apiRequests/Attribute.js create mode 100644 cypress/apiRequests/Category.js create mode 100644 cypress/apiRequests/Channels.js create mode 100644 cypress/apiRequests/Checkout.js rename cypress/{api => apiRequests}/Customer.js (99%) rename cypress/{api => apiRequests}/Order.js (96%) create mode 100644 cypress/apiRequests/Product.js rename cypress/{api => apiRequests}/ShippingMethod.js (80%) create mode 100644 cypress/apiRequests/Warehouse.js create mode 100644 cypress/elements/header/header-selectors.js delete mode 100644 cypress/fixtures/example.json create mode 100644 cypress/utils/ordersUtils.js create mode 100644 cypress/utils/productsUtils.js create mode 100644 cypress/utils/shippingUtils.js diff --git a/cypress/api/Product.js b/cypress/api/Product.js deleted file mode 100644 index bbffa4312..000000000 --- a/cypress/api/Product.js +++ /dev/null @@ -1,55 +0,0 @@ -class Product { - getFirstProducts(first) { - const query = `query{ - products(first:${first}){ - edges{ - node{ - id - name - variants{ - id - } - } - } - } - } - `; - return cy.sendRequestWithQuery(query); - } - - updateChannelInProduct(productId, channelId) { - const mutation = `mutation{ - productChannelListingUpdate(id:"${productId}", input:{ - addChannels:{ - channelId:"${channelId}" - isPublished: true - isAvailableForPurchase:true - 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); - } -} - -export default Product; diff --git a/cypress/apiRequests/Attribute.js b/cypress/apiRequests/Attribute.js new file mode 100644 index 000000000..a7253f8b8 --- /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..7ea65eb7a --- /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 new file mode 100644 index 000000000..9afa7cc6b --- /dev/null +++ b/cypress/apiRequests/Channels.js @@ -0,0 +1,74 @@ +class Channels { + createChannel(isActive, name, slug, currencyCode) { + const createChannelMutation = `mutation{ + channelCreate(input: { + isActive: ${isActive} + name: "${name}" + slug: "${slug}" + currencyCode: "${currencyCode}" + }){ + channel{ + id + name + slug + } + channelErrors{ + code + message + } + } + }`; + return cy.sendRequestWithQuery(createChannelMutation); + } + + deleteTestChannels(nameStartsWith) { + const getChannelsInfoQuery = `query{ + channels{ + name + id + isActive + slug + currencyCode + } + } + `; + cy.sendRequestWithQuery(getChannelsInfoQuery).then(resp => { + const channels = new Set(resp.body.data.channels); + if (channels) { + channels.forEach(element => { + if (element.name.startsWith(nameStartsWith)) { + const targetChannels = Array.from(channels).filter(function( + channel + ) { + return ( + element.currencyCode === channel.currencyCode && + element.id !== channel.id + ); + }); + if (targetChannels[0]) { + this.deleteChannel(element.id, targetChannels[0].id); + channels.delete(element); + } + } + }); + } + }); + } + + deleteChannel(channelId, targetChennelId) { + const deleteChannelMutation = `mutation{ + channelDelete(id: "${channelId}", input:{ + targetChannel: "${targetChennelId}" + }){ + channel{ + name + } + channelErrors{ + message + } + } + }`; + return cy.sendRequestWithQuery(deleteChannelMutation); + } +} +export default Channels; diff --git a/cypress/apiRequests/Checkout.js b/cypress/apiRequests/Checkout.js new file mode 100644 index 000000000..ca63560f4 --- /dev/null +++ b/cypress/apiRequests/Checkout.js @@ -0,0 +1,70 @@ +class Checkout { + createCheckout(channelSlug, email, productQuantity, variantsList) { + const lines = []; + variantsList.forEach(variant => { + lines.push(`{quantity:${productQuantity} + variantId:"${variant.id}"}`); + }); + const mutation = `mutation{ + checkoutCreate(input:{ + channel:"${channelSlug}" + email:"${email}" + lines: [${lines.join()}] + }){ + checkoutErrors{ + field + message + } + created + checkout{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + addShippingMethod(checkoutId, shippingMethodId) { + const mutation = `mutation{ + checkoutShippingMethodUpdate(checkoutId:"${checkoutId}", + shippingMethodId:"${shippingMethodId}"){ + checkoutErrors{ + message + field + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + addPayment(checkoutId, gateway, token) { + const mutation = `mutation{ + checkoutPaymentCreate(checkoutId:"${checkoutId}", + input:{ + gateway: "${gateway}" + token:"${token}" + }){ + paymentErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + compliteCheckout(checkoutId) { + const mutation = `mutation{ + checkoutComplete(checkoutId:"${checkoutId}"){ + order{ + id + } + confirmationNeeded + confirmationData + checkoutErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Checkout; diff --git a/cypress/api/Customer.js b/cypress/apiRequests/Customer.js similarity index 99% rename from cypress/api/Customer.js rename to cypress/apiRequests/Customer.js index c9beb7042..336dbf698 100644 --- a/cypress/api/Customer.js +++ b/cypress/apiRequests/Customer.js @@ -28,6 +28,7 @@ export class Customer { }){ user{ id + email } accountErrors{ code diff --git a/cypress/api/Order.js b/cypress/apiRequests/Order.js similarity index 96% rename from cypress/api/Order.js rename to cypress/apiRequests/Order.js index 571f27fb0..0005d344a 100644 --- a/cypress/api/Order.js +++ b/cypress/apiRequests/Order.js @@ -7,7 +7,7 @@ class Order { } } }`; - cy.sendRequestWithQuery(mutation); + return cy.sendRequestWithQuery(mutation); } addProductToOrder(orderId, variantId, quantity = 1) { diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js new file mode 100644 index 000000000..b747d238f --- /dev/null +++ b/cypress/apiRequests/Product.js @@ -0,0 +1,178 @@ +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) { + const mutation = `mutation{ + productChannelListingUpdate(id:"${productId}", + input:{ + addChannels:{ + channelId:"${channelId}" + isPublished:true + isAvailableForPurchase: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 + ) { + const mutation = `mutation{ + productVariantBulkCreate(product:"${productId}", variants:{ + attributes:[] + sku:"${sku}" + channelListings:{ + channelId:"${channelId}" + price:"${price}" + costPrice:"${costPrice}" + } + stocks:{ + warehouse:"${warehouseId}" + quantity:${quantity} + } + }){ + 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/api/ShippingMethod.js b/cypress/apiRequests/ShippingMethod.js similarity index 80% rename from cypress/api/ShippingMethod.js rename to cypress/apiRequests/ShippingMethod.js index 179f1b824..93ecb8f80 100644 --- a/cypress/api/ShippingMethod.js +++ b/cypress/apiRequests/ShippingMethod.js @@ -32,13 +32,13 @@ class ShippingMethod { return cy.sendRequestWithQuery(mutation); } - addChannelToShippingMethod(shippingRateId, channelId) { + addChannelToShippingMethod(shippingRateId, channelId, price) { const mutation = ` mutation{ shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ addChannels: { channelId:"${channelId}" - price:10 + price: ${price} } }){ shippingMethod{ @@ -54,19 +54,6 @@ class ShippingMethod { return cy.sendRequestWithQuery(mutation); } - deleteShippingZones(startsWith) { - this.getShippingZones().then(resp => { - if (resp.body.data.shippingZones) { - const shippingZone = resp.body.data.shippingZones.edges; - shippingZone.forEach(element => { - if (element.node.name.includes(startsWith)) { - this.deleteShippingZone(element.node.id); - } - }); - } - }); - } - deleteShippingZone(shippingZoneId) { const mutation = `mutation{ shippingZoneDelete(id:"${shippingZoneId}"){ diff --git a/cypress/apiRequests/Warehouse.js b/cypress/apiRequests/Warehouse.js new file mode 100644 index 000000000..478d28726 --- /dev/null +++ b/cypress/apiRequests/Warehouse.js @@ -0,0 +1,55 @@ +class Warehouse { + createWarehouse(name, shippingZone, address, slug = name) { + const mutation = `mutation{ + createWarehouse(input:{ + name:"${name}" + slug:"${slug}" + shippingZones:"${shippingZone}" + address:{ + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + }){ + warehouseErrors{ + field + message + } + warehouse{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + getWarehouses(first, search) { + const query = `query{ + warehouses(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(query); + } + deleteWarehouse(warehouseId) { + const mutation = `mutation{ + deleteWarehouse(id:"${warehouseId}"){ + warehouseErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Warehouse; diff --git a/cypress/elements/header/header-selectors.js b/cypress/elements/header/header-selectors.js new file mode 100644 index 000000000..935421c34 --- /dev/null +++ b/cypress/elements/header/header-selectors.js @@ -0,0 +1,4 @@ +export const HEADER_SELECTORS = { + channelSelect: "[data-test-id='app-channel-select']", + channelSelectList: "[class*='MuiMenu-paper']" +}; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json index 742bd227e..08fad0466 100644 --- a/cypress/fixtures/addresses.json +++ b/cypress/fixtures/addresses.json @@ -7,6 +7,7 @@ "postalCode": "53-346", "country": "PL", "countryArea": "Dolny Śląsk", - "phone": "123456787" + "phone": "123456787", + "currency": "PLN" } } \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d9352..000000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js index 44213df2e..4251afa72 100644 --- a/cypress/integration/dashboard.js +++ b/cypress/integration/dashboard.js @@ -1,23 +1,29 @@ import faker from "faker"; -import Customer from "../api/Customer"; -import Order from "../api/Order"; -import Product from "../api/Product"; -import ShippingMethod from "../api/ShippingMethod"; +import Channels from "../apiRequests/Channels"; +import Customer from "../apiRequests/Customer"; import { DASHBOARD_SELECTORS } from "../elements/dashboard/dashboard-selectors"; +import { HEADER_SELECTORS } from "../elements/header/header-selectors"; +import OrdersUtils from "../utils/ordersUtils"; +import ProductsUtils from "../utils/productsUtils"; +import ShippingUtils from "../utils/shippingUtils"; // describe("User authorization", () => { const startsWith = "Cy-"; const customer = new Customer(); - const product = new Product(); - const order = new Order(); - const shippingMethod = new ShippingMethod(); + const channels = new Channels(); + const productsUtils = new ProductsUtils(); + const shippingUtils = new ShippingUtils(); + const ordersUtils = new OrdersUtils(); before(() => { + cy.clearSessionData().loginUserViaRequest(); customer.deleteCustomers(startsWith); - shippingMethod.deleteShippingZones(startsWith); + shippingUtils.deleteShipping(startsWith); + productsUtils.deleteProducts(startsWith); + channels.deleteTestChannels(startsWith); }); beforeEach(() => { @@ -36,84 +42,87 @@ describe("User authorization", () => { }); it("should correct amount of orders be displayed", () => { - faker = require("faker"); const randomName = startsWith + faker.random.number(); const randomEmail = randomName + "@example.com"; - product.getFirstProducts(3).then(productsResp => { - const productsList = productsResp.body.data.products.edges; - productsList.forEach(productElement => { - product.updateChannelInProduct( - "Q2hhbm5lbDoxNzk=", - productElement.node.id - ); - const variants = productElement.node.variants; - variants.forEach(variant => { - product.updateChannelPriceInVariant(variant.id, "Q2hhbm5lbDoxNzk="); + const randomNameProductOutOfStock = `${startsWith}${faker.random.number()}`; + const shippingPrice = 12; + const productPrice = 22; + cy.fixture("addresses").then(json => { + channels + .createChannel(true, randomName, randomName, json.plAddress.currency) + .then(channelsResp => { + const channelId = channelsResp.body.data.channelCreate.channel.id; + const channelSlug = channelsResp.body.data.channelCreate.channel.slug; + customer + .createCustomer(randomEmail, randomName, json.plAddress) + .then(resp => { + const customerId = resp.body.data.customerCreate.user.id; + const customerEmail = resp.body.data.customerCreate.user.email; + shippingUtils + .createShipping( + channelId, + randomName, + json.plAddress, + shippingPrice + ) + .then(() => { + const shippingId = shippingUtils.getShippingMethodId(); + const warehouseId = shippingUtils.getWarehouseId(); + productsUtils + .createTypeAttributeAndCategoryForProduct(randomName) + .then(() => { + const productTypeId = productsUtils.getProductTypeId(); + const attributeId = productsUtils.getAttributeId(); + const categoryId = productsUtils.getCategoryId(); + productsUtils + .createProductInChannel( + randomName, + channelId, + warehouseId, + 10, + productTypeId, + attributeId, + categoryId, + productPrice + ) + .then(() => { + const variantsList = productsUtils.getCreatedVariants(); + ordersUtils.createReadyToFullfillOrder( + customerId, + shippingId, + channelId, + variantsList + ); + ordersUtils.createWaitingForCaptureOrder( + channelSlug, + customerEmail, + variantsList, + shippingId + ); + }); + productsUtils.createProductInChannel( + randomNameProductOutOfStock, + channelId, + warehouseId, + 0, + productTypeId, + attributeId, + categoryId, + productPrice + ); + }); + }); + }); }); - }); - cy.fixture("addresses").then(json => { - customer - .createCustomer(randomEmail, randomName, json.plAddress) - .as("createCustomerResponse") - .then(resp => { - const customerId = resp.body.data.customerCreate.user.id; - shippingMethod - .createShippingZone(randomName, "PL") - .then(shippingZoneResp => { - shippingMethod - .createShippingRate( - randomName, - shippingZoneResp.body.data.shippingZoneCreate.shippingZone - .id - ) - .then(rateResp => { - const shippingMethodId = - rateResp.body.data.shippingPriceCreate.shippingMethod.id; - shippingMethod - .addChannelToShippingMethod( - shippingMethodId, - "Q2hhbm5lbDoxNzk=" - ) - .then(shippingMethodResp => { - createReadyToFullfillOrder( - customerId, - shippingMethodId, - "Q2hhbm5lbDoxNzk=", - productsList - ); - }); - }); - }); - }); - }); }); cy.visit("/"); - softAssertMatch(DASHBOARD_SELECTORS.orders, /^0/); - softAssertMatch(DASHBOARD_SELECTORS.ordersReadyToFulfill, /^Brak/); - softAssertMatch(DASHBOARD_SELECTORS.paymentsWaitingForCapture, /^Brak/); - softAssertMatch(DASHBOARD_SELECTORS.productsOutOfStock, /^Brak/); + cy.get(HEADER_SELECTORS.channelSelect) + .click() + .get(HEADER_SELECTORS.channelSelectList) + .contains(randomName) + .click(); }); - function createReadyToFullfillOrder( - customerId, - shippingMethodId, - channelId, - productsList - ) { - order - .createDraftOrder(customerId, shippingMethodId, channelId) - .then(draftOrderResp => { - const orderId = draftOrderResp.body.data.draftOrderCreate.order.id; - productsList.forEach(productElement => { - productElement.node.variants.forEach(variantElement => { - order.addProductToOrder(orderId, variantElement.id); - }); - }); - order.markOrderAsPaid(orderId); - order.completeOrder(orderId); - }); - } - function softAssertVisibility(selector) { cy.get(selector).then(element => chai.softExpect(element).to.be.visible); } diff --git a/cypress/utils/ordersUtils.js b/cypress/utils/ordersUtils.js new file mode 100644 index 000000000..dad9ebc29 --- /dev/null +++ b/cypress/utils/ordersUtils.js @@ -0,0 +1,46 @@ +import Checkout from "../apiRequests/Checkout"; +import Order from "../apiRequests/Order"; + +class OrdersUtils { + createWaitingForCaptureOrder( + channelSlug, + email, + variantsList, + shippingMethodId + ) { + const checkout = new Checkout(); + return checkout + .createCheckout(channelSlug, email, 1, variantsList) + .then(createCheckoutResp => { + const checkoutId = + createCheckoutResp.body.data.checkoutCreate.checkout.id; + return checkout + .addShippingMethod(checkoutId, shippingMethodId) + .then(() => + checkout + .addPayment(checkoutId, "mirumee.payments.dummy", "not-charged") + .then(() => checkout.compliteCheckout(checkoutId)) + ); + }); + } + createReadyToFullfillOrder( + customerId, + shippingMethodId, + channelId, + variantsList + ) { + const order = new Order(); + return order + .createDraftOrder(customerId, shippingMethodId, channelId) + .then(draftOrderResp => { + const orderId = draftOrderResp.body.data.draftOrderCreate.order.id; + variantsList.forEach(variantElement => { + order.addProductToOrder(orderId, variantElement.id); + }); + return order + .markOrderAsPaid(orderId) + .then(() => order.completeOrder(orderId)); + }); + } +} +export default OrdersUtils; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js new file mode 100644 index 000000000..d02efde08 --- /dev/null +++ b/cypress/utils/productsUtils.js @@ -0,0 +1,124 @@ +import Attribute from "../apiRequests/Attribute"; +import Category from "../apiRequests/Category"; +import Product from "../apiRequests/Product"; + +class ProductsUtils { + createdVariantId; + 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); + }); + }); + } + createProductInChannel( + name, + channelId, + warehouseId, + quantityInWarehouse, + productTypeId, + attributeId, + categoryId, + price + ) { + const product = new Product(); + return product + .createProduct(attributeId, name, productTypeId, categoryId) + .then(createProductResp => { + const productId = createProductResp.body.data.productCreate.product.id; + return product.updateChannelInProduct(productId, channelId).then(() => + product + .createVariant( + productId, + 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; + } + + 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; diff --git a/cypress/utils/shippingUtils.js b/cypress/utils/shippingUtils.js new file mode 100644 index 000000000..5806ac82d --- /dev/null +++ b/cypress/utils/shippingUtils.js @@ -0,0 +1,71 @@ +import ShippingMethod from "../apiRequests/ShippingMethod"; +import Warehouse from "../apiRequests/Warehouse"; +class ShippingUtils { + shippingMethodId; + shippingZoneId; + warehouseId; + + createShipping(channelId, name, address, price) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + return shippingMethod + .createShippingZone(name, address.country) + .then(shippingZoneResp => { + this.shippingZoneId = + shippingZoneResp.body.data.shippingZoneCreate.shippingZone.id; + return warehouse + .createWarehouse(name, this.shippingZoneId, address) + .then(createWarehouseResp => { + this.warehouseId = + createWarehouseResp.body.data.createWarehouse.warehouse.id; + return shippingMethod + .createShippingRate(name, this.shippingZoneId) + .then(rateResp => { + this.shippingMethodId = + rateResp.body.data.shippingPriceCreate.shippingMethod.id; + return shippingMethod.addChannelToShippingMethod( + this.shippingMethodId, + channelId, + price + ); + }); + }); + }); + } + + getShippingMethodId() { + return this.shippingMethodId; + } + + getShippingZoneId() { + return this.shippingZoneId; + } + + getWarehouseId() { + return this.warehouseId; + } + + deleteShipping(startsWith) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + shippingMethod.getShippingZones().then(resp => { + if (resp.body.data.shippingZones) { + const shippingZone = resp.body.data.shippingZones.edges; + shippingZone.forEach(element => { + if (element.node.name.includes(startsWith)) { + shippingMethod.deleteShippingZone(element.node.id); + } + }); + } + }); + warehouse.getWarehouses(100, startsWith).then(resp => { + const warehouses = resp.body.data.warehouses.edges; + warehouses.forEach(warehouseElement => { + if (warehouseElement.node.name.includes(startsWith)) { + warehouse.deleteWarehouse(warehouseElement.node.id); + } + }); + }); + } +} +export default ShippingUtils; From db4fc1d8c594eb76d338bc93d4e367d9c8eec9d4 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 3 Feb 2021 09:22:04 +0100 Subject: [PATCH 05/56] Fix failing login in Firefox browser (#970) The bug was introduced in OAuth2 implementation (1645e2fd), where the tokenAuthLoading flag from AuthProvider was not used and it did not blocked UI in loading state. --- src/auth/views/Login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/views/Login.tsx b/src/auth/views/Login.tsx index 2f7b9e294..59f7b1878 100644 --- a/src/auth/views/Login.tsx +++ b/src/auth/views/Login.tsx @@ -86,7 +86,7 @@ const LoginView: React.FC = ({ params }) => { externalAuthentications={ externalAuthentications?.shop?.availableExternalAuthentications } - loading={externalAuthenticationsLoading} + loading={externalAuthenticationsLoading || tokenAuthLoading} onExternalAuthentication={handleRequestExternalAuthentication} onPasswordRecovery={() => navigate(passwordResetUrl)} onSubmit={handleSubmit} From cb528a7744b807cbf6aaa4787de7fb305a78b444 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 4 Feb 2021 12:15:27 +0100 Subject: [PATCH 06/56] tests for publish products --- cypress/apiRequests/Attribute.js | 49 +++++ cypress/apiRequests/Category.js | 43 ++++ cypress/apiRequests/Channels.js | 53 +++++ cypress/apiRequests/Product.js | 192 ++++++++++++++++++ cypress/apiRequests/ShopInfo.js | 13 ++ cypress/elements/catalog/product-selectors.js | 14 +- .../frontend-elements/search-selectors.js | 5 + cypress/fixtures/addresses.json | 5 + cypress/integration/products.js | 47 ----- cypress/integration/products/products.js | 109 ++++++++++ cypress/integration/products/publish.js | 141 +++++++++++++ cypress/steps/frontendSteps/searchSteps.js | 11 + cypress/support/index.js | 35 ++++ cypress/support/user/index.js | 20 ++ cypress/url/url-list.js | 6 + cypress/utils/channelsUtils.js | 38 ++++ cypress/utils/productsUtils.js | 153 ++++++++++++++ 17 files changed, 886 insertions(+), 48 deletions(-) create mode 100644 cypress/apiRequests/Attribute.js create mode 100644 cypress/apiRequests/Category.js create mode 100644 cypress/apiRequests/Channels.js create mode 100644 cypress/apiRequests/Product.js create mode 100644 cypress/apiRequests/ShopInfo.js create mode 100644 cypress/elements/frontend-elements/search-selectors.js create mode 100644 cypress/fixtures/addresses.json delete mode 100644 cypress/integration/products.js create mode 100644 cypress/integration/products/products.js create mode 100644 cypress/integration/products/publish.js create mode 100644 cypress/steps/frontendSteps/searchSteps.js create mode 100644 cypress/url/url-list.js create mode 100644 cypress/utils/channelsUtils.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..4ba3467f2 --- /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..0d76c7b3b --- /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 new file mode 100644 index 000000000..2daa39a9e --- /dev/null +++ b/cypress/apiRequests/Channels.js @@ -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; diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js new file mode 100644 index 000000000..e7e0c11d7 --- /dev/null +++ b/cypress/apiRequests/Product.js @@ -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; diff --git a/cypress/apiRequests/ShopInfo.js b/cypress/apiRequests/ShopInfo.js new file mode 100644 index 000000000..17d5f8a9e --- /dev/null +++ b/cypress/apiRequests/ShopInfo.js @@ -0,0 +1,13 @@ +class ShopInfo { + getShopInfo() { + const query = `query{ + shop{ + domain{ + url + } + } + }`; + return cy.sendRequestWithQuery(query); + } +} +export default ShopInfo; diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index de68973b0..b61b38a11 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -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]" }; diff --git a/cypress/elements/frontend-elements/search-selectors.js b/cypress/elements/frontend-elements/search-selectors.js new file mode 100644 index 000000000..712a91ce9 --- /dev/null +++ b/cypress/elements/frontend-elements/search-selectors.js @@ -0,0 +1,5 @@ +export const SEARCH_SELECTORS = { + searchButton: "[data-test='menuSearchOverlayLink']", + searchInputField: "[placeholder='search']", + productItem: ".search__products__item" +}; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json new file mode 100644 index 000000000..5a346904f --- /dev/null +++ b/cypress/fixtures/addresses.json @@ -0,0 +1,5 @@ +{ + "plAddress":{ + "currency": "PLN" + } +} \ No newline at end of file diff --git a/cypress/integration/products.js b/cypress/integration/products.js deleted file mode 100644 index 1d8e38ee8..000000000 --- a/cypress/integration/products.js +++ /dev/null @@ -1,47 +0,0 @@ -import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors"; -import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; - -// -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"); - }); -}); diff --git a/cypress/integration/products/products.js b/cypress/integration/products/products.js new file mode 100644 index 000000000..9cb780cda --- /dev/null +++ b/cypress/integration/products/products.js @@ -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"; + +// +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); + }); + }); + }); +}); diff --git a/cypress/integration/products/publish.js b/cypress/integration/products/publish.js new file mode 100644 index 000000000..ebd7588fc --- /dev/null +++ b/cypress/integration/products/publish.js @@ -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"; + +// +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); + }); + }); + }); + }); + }); +}); diff --git a/cypress/steps/frontendSteps/searchSteps.js b/cypress/steps/frontendSteps/searchSteps.js new file mode 100644 index 000000000..9c42e54fa --- /dev/null +++ b/cypress/steps/frontendSteps/searchSteps.js @@ -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; diff --git a/cypress/support/index.js b/cypress/support/index.js index 531b8aab9..c09fd0ded 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -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}`); +}); diff --git a/cypress/support/user/index.js b/cypress/support/user/index.js index 9edf99e27..1cdf56cf9 100644 --- a/cypress/support/user/index.js +++ b/cypress/support/user/index.js @@ -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); + }); +}); diff --git a/cypress/url/url-list.js b/cypress/url/url-list.js new file mode 100644 index 000000000..aa047845c --- /dev/null +++ b/cypress/url/url-list.js @@ -0,0 +1,6 @@ +export const URL_LIST = { + dashbord: "/", + channels: "/channels/", + products: "/products/", + orders: "/orders/" +}; diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js new file mode 100644 index 000000000..a536fb4f3 --- /dev/null +++ b/cypress/utils/channelsUtils.js @@ -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; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js new file mode 100644 index 000000000..eda29a95b --- /dev/null +++ b/cypress/utils/productsUtils.js @@ -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; From 584e66b9f9a70c6c2a92ed090592c92a49fc6da0 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 4 Feb 2021 13:26:04 +0100 Subject: [PATCH 07/56] one test for available for purchase --- cypress/apiRequests/ShippingMethod.js | 84 +++++++++++++++ cypress/apiRequests/Warehouse.js | 55 ++++++++++ .../frontend-elements/cart-selectors.js | 3 + .../product-details-selectors.js | 3 + .../products/availableForPurchase.js | 101 ++++++++++++++++++ cypress/utils/channelsUtils.js | 13 ++- cypress/utils/shippingUtils.js | 71 ++++++++++++ 7 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 cypress/apiRequests/ShippingMethod.js create mode 100644 cypress/apiRequests/Warehouse.js create mode 100644 cypress/elements/frontend-elements/cart-selectors.js create mode 100644 cypress/elements/frontend-elements/product-details-selectors.js create mode 100644 cypress/integration/products/availableForPurchase.js create mode 100644 cypress/utils/shippingUtils.js diff --git a/cypress/apiRequests/ShippingMethod.js b/cypress/apiRequests/ShippingMethod.js new file mode 100644 index 000000000..25c067a43 --- /dev/null +++ b/cypress/apiRequests/ShippingMethod.js @@ -0,0 +1,84 @@ +class ShippingMethod { + createShippingRate(name, shippingZone) { + const mutation = ` + mutation{ + shippingPriceCreate(input:{ + name: "${name}" + shippingZone: "${shippingZone}" + type: PRICE + }){ + shippingMethod{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + createShippingZone(name, country) { + const mutation = ` + mutation{ + shippingZoneCreate(input:{ + name: "${name}" + countries: "${country}" + }){ + shippingZone{ + id + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + addChannelToShippingMethod(shippingRateId, channelId, price) { + const mutation = ` + mutation{ + shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ + addChannels: { + channelId:"${channelId}" + price: ${price} + } + }){ + shippingMethod{ + id + } + shippingErrors{ + code + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + deleteShippingZone(shippingZoneId) { + const mutation = `mutation{ + shippingZoneDelete(id:"${shippingZoneId}"){ + shippingErrors{ + message + } + } + } + `; + return cy.sendRequestWithQuery(mutation); + } + + getShippingZones() { + const query = `query{ + shippingZones(first:100){ + edges{ + node{ + name + id + } + } + } + } + `; + return cy.sendRequestWithQuery(query); + } +} +export default ShippingMethod; diff --git a/cypress/apiRequests/Warehouse.js b/cypress/apiRequests/Warehouse.js new file mode 100644 index 000000000..80df7f6f6 --- /dev/null +++ b/cypress/apiRequests/Warehouse.js @@ -0,0 +1,55 @@ +class Warehouse { + createWarehouse(name, shippingZone, address, slug = name) { + const mutation = `mutation{ + createWarehouse(input:{ + name:"${name}" + slug:"${slug}" + shippingZones:"${shippingZone}" + address:{ + streetAddress1: "${address.streetAddress1}" + streetAddress2: "${address.streetAddress2}" + city: "${address.city}" + postalCode: "${address.postalCode}" + country: ${address.country} + phone: "${address.phone}" + } + }){ + warehouseErrors{ + field + message + } + warehouse{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } + getWarehouses(first, search) { + const query = `query{ + warehouses(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy.sendRequestWithQuery(query); + } + deleteWarehouse(warehouseId) { + const mutation = `mutation{ + deleteWarehouse(id:"${warehouseId}"){ + warehouseErrors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); + } +} +export default Warehouse; diff --git a/cypress/elements/frontend-elements/cart-selectors.js b/cypress/elements/frontend-elements/cart-selectors.js new file mode 100644 index 000000000..0209eba5c --- /dev/null +++ b/cypress/elements/frontend-elements/cart-selectors.js @@ -0,0 +1,3 @@ +export const CART_SELECTORS = { + productInCart: "[data-test='cartRow']" +}; diff --git a/cypress/elements/frontend-elements/product-details-selectors.js b/cypress/elements/frontend-elements/product-details-selectors.js new file mode 100644 index 000000000..9cda9b9f6 --- /dev/null +++ b/cypress/elements/frontend-elements/product-details-selectors.js @@ -0,0 +1,3 @@ +export const PRODUCTS_DETAILS_SELECTORS = { + addToCartButton: "[data-test='addProductToCartButton']" +}; diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js new file mode 100644 index 000000000..1aa2c3685 --- /dev/null +++ b/cypress/integration/products/availableForPurchase.js @@ -0,0 +1,101 @@ +import faker from "faker"; + +import ShopInfo from "../../apiRequests/ShopInfo"; +import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors"; +import { CART_SELECTORS } from "../../elements/frontend-elements/cart-selectors"; +import { PRODUCTS_DETAILS_SELECTORS } from "../../elements/frontend-elements/product-details-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"; +import ShippingUtils from "../../utils/shippingUtils"; + +// +describe("Products", () => { + const shippingUtils = new ShippingUtils(); + 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; + let defaultChannel; + let warehouseId; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + shippingUtils.deleteShipping(startsWith); + productsUtils.deleteProducts(startsWith); + + channelsUtils.findDefaultChannel().then(() => { + defaultChannel = channelsUtils.getDefaultChannel(); + cy.fixture("addresses").then(json => { + shippingUtils + .createShipping(defaultChannel, name, json.plAddress, 10) + .then(() => { + warehouseId = shippingUtils.getWarehouseId(); + }); + }); + }); + 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"); + }); + + it("should be possible to add to cart available for purchase product", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel, + true, + false, + warehouseId, + 10, + 10 + ) + .then(() => { + const productUrl = `${URL_LIST.products}${productsUtils.getCreatedProductId}`; + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Dostępne do zakupu") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(name); + cy.get(SEARCH_SELECTORS.productItem) + .contains(productName) + .click() + .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) + .click() + .get(CART_SELECTORS.productInCart) + .contains(productName); + }); + }); + }); +}); diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js index a536fb4f3..7e1e58973 100644 --- a/cypress/utils/channelsUtils.js +++ b/cypress/utils/channelsUtils.js @@ -3,6 +3,8 @@ import Channels from "../apiRequests/Channels"; class ChannelsUtils { channels = new Channels(); + defaultChannel; + deleteChannels(nameStartsWith) { this.channels.getChannels().then(resp => { const channelsArray = new Set(resp.body.data.channels); @@ -26,13 +28,18 @@ class ChannelsUtils { } }); } - getDefaultChannel() { + findDefaultChannel() { return this.channels.getChannels().then(resp => { const channelsArray = resp.body.data.channels; - return channelsArray.find(function(channelElement) { + return (this.defaultChannel = channelsArray.find(function( + channelElement + ) { return channelElement.slug === "default-channel"; - }); + })); }); } + getDefaultChannel() { + return this.defaultChannel; + } } export default ChannelsUtils; diff --git a/cypress/utils/shippingUtils.js b/cypress/utils/shippingUtils.js new file mode 100644 index 000000000..5806ac82d --- /dev/null +++ b/cypress/utils/shippingUtils.js @@ -0,0 +1,71 @@ +import ShippingMethod from "../apiRequests/ShippingMethod"; +import Warehouse from "../apiRequests/Warehouse"; +class ShippingUtils { + shippingMethodId; + shippingZoneId; + warehouseId; + + createShipping(channelId, name, address, price) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + return shippingMethod + .createShippingZone(name, address.country) + .then(shippingZoneResp => { + this.shippingZoneId = + shippingZoneResp.body.data.shippingZoneCreate.shippingZone.id; + return warehouse + .createWarehouse(name, this.shippingZoneId, address) + .then(createWarehouseResp => { + this.warehouseId = + createWarehouseResp.body.data.createWarehouse.warehouse.id; + return shippingMethod + .createShippingRate(name, this.shippingZoneId) + .then(rateResp => { + this.shippingMethodId = + rateResp.body.data.shippingPriceCreate.shippingMethod.id; + return shippingMethod.addChannelToShippingMethod( + this.shippingMethodId, + channelId, + price + ); + }); + }); + }); + } + + getShippingMethodId() { + return this.shippingMethodId; + } + + getShippingZoneId() { + return this.shippingZoneId; + } + + getWarehouseId() { + return this.warehouseId; + } + + deleteShipping(startsWith) { + const shippingMethod = new ShippingMethod(); + const warehouse = new Warehouse(); + shippingMethod.getShippingZones().then(resp => { + if (resp.body.data.shippingZones) { + const shippingZone = resp.body.data.shippingZones.edges; + shippingZone.forEach(element => { + if (element.node.name.includes(startsWith)) { + shippingMethod.deleteShippingZone(element.node.id); + } + }); + } + }); + warehouse.getWarehouses(100, startsWith).then(resp => { + const warehouses = resp.body.data.warehouses.edges; + warehouses.forEach(warehouseElement => { + if (warehouseElement.node.name.includes(startsWith)) { + warehouse.deleteWarehouse(warehouseElement.node.id); + } + }); + }); + } +} +export default ShippingUtils; From 2a9e4204200ba9b5e651893f9fbf23ee6b636429 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Mon, 8 Feb 2021 10:23:12 +0100 Subject: [PATCH 08/56] all tests for products --- cypress/apiRequests/Product.js | 5 +- cypress/elements/catalog/product-selectors.js | 3 +- cypress/fixtures/addresses.json | 10 +- .../products/availableForPurchase.js | 54 ++++++- cypress/integration/products/publish.js | 23 +-- .../integration/products/showInListings.js | 144 ++++++++++++++++++ cypress/utils/channelsUtils.js | 7 +- cypress/utils/productsUtils.js | 4 +- 8 files changed, 224 insertions(+), 26 deletions(-) create mode 100644 cypress/integration/products/showInListings.js diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index e7e0c11d7..035c6f083 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -27,7 +27,8 @@ class Product { productId, channelId, isPublished, - isAvailableForPurchase + isAvailableForPurchase, + visibleInListings ) { const mutation = `mutation{ productChannelListingUpdate(id:"${productId}", @@ -36,7 +37,7 @@ class Product { channelId:"${channelId}" isPublished:${isPublished} isAvailableForPurchase:${isAvailableForPurchase} - visibleInListings:true + visibleInListings:${visibleInListings} } }){ product{ diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index b61b38a11..a4d1b6437 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -23,5 +23,6 @@ export const PRODUCTS_SELECTORS = { channelAvailabilityList: "ul[role='menu']", goBackButton: "[data-test-id='app-header-back-button']", assignedChannels: "[data-test='channel-availability-item']", - publishedRadioButton: "[role=radiogroup]" + publishedRadioButton: "[role=radiogroup]", + visibleInListingsButton: "[class*='MuiFormControlLabel']" }; diff --git a/cypress/fixtures/addresses.json b/cypress/fixtures/addresses.json index 5a346904f..08fad0466 100644 --- a/cypress/fixtures/addresses.json +++ b/cypress/fixtures/addresses.json @@ -1,5 +1,13 @@ { - "plAddress":{ + "plAddress": { + "companyName": "Test3", + "streetAddress1": "Smolna", + "streetAddress2": "13/1", + "city": "Wrocław", + "postalCode": "53-346", + "country": "PL", + "countryArea": "Dolny Śląsk", + "phone": "123456787", "currency": "PLN" } } \ No newline at end of file diff --git a/cypress/integration/products/availableForPurchase.js b/cypress/integration/products/availableForPurchase.js index 1aa2c3685..18a73cd29 100644 --- a/cypress/integration/products/availableForPurchase.js +++ b/cypress/integration/products/availableForPurchase.js @@ -33,8 +33,8 @@ describe("Products", () => { shippingUtils.deleteShipping(startsWith); productsUtils.deleteProducts(startsWith); - channelsUtils.findDefaultChannel().then(() => { - defaultChannel = channelsUtils.getDefaultChannel(); + channelsUtils.getDefaultChannel().then(channel => { + defaultChannel = channel; cy.fixture("addresses").then(json => { shippingUtils .createShipping(defaultChannel, name, json.plAddress, 10) @@ -66,15 +66,18 @@ describe("Products", () => { productTypeId, attributeId, categoryId, - defaultChannel, + defaultChannel.id, true, false, + true, warehouseId, 10, 10 ) .then(() => { - const productUrl = `${URL_LIST.products}${productsUtils.getCreatedProductId}`; + const productUrl = `${ + URL_LIST.products + }${productsUtils.getCreatedProductId()}`; cy.visit(productUrl) .get(PRODUCTS_SELECTORS.assignedChannels) .click() @@ -87,7 +90,7 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); + searchSteps.searchFor(productName); cy.get(SEARCH_SELECTORS.productItem) .contains(productName) .click() @@ -98,4 +101,45 @@ describe("Products", () => { }); }); }); + it("shouldn't be possible to add to cart not available for purchase product", () => { + const productName = `${startsWith}${faker.random.number()}`; + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + true, + true, + warehouseId, + 10, + 10 + ) + .then(() => { + const productUrl = `${ + URL_LIST.products + }${productsUtils.getCreatedProductId()}`; + cy.visit(productUrl) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.publishedRadioButton) + .contains("Niedostępne do zakupu") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem) + .contains(productName) + .click() + .get(PRODUCTS_DETAILS_SELECTORS.addToCartButton) + .should("be.disabled"); + }); + }); + }); }); diff --git a/cypress/integration/products/publish.js b/cypress/integration/products/publish.js index ebd7588fc..aa274dee4 100644 --- a/cypress/integration/products/publish.js +++ b/cypress/integration/products/publish.js @@ -39,7 +39,7 @@ describe("Products", () => { .its("body.data.shop.domain.url") .as("shopUrl"); }); - xit("should display on frontend only published products", () => { + it("should display on frontend only published products", () => { const productName = `${startsWith}${faker.random.number()}`; channelsUtils.getDefaultChannel().then(defaultChannel => { productsUtils @@ -50,7 +50,8 @@ describe("Products", () => { categoryId, defaultChannel.id, false, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -65,8 +66,8 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); - cy.get(SEARCH_SELECTORS.productItem).contains(name); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); }); }); }); @@ -82,7 +83,8 @@ describe("Products", () => { categoryId, defaultChannel.id, true, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -97,13 +99,13 @@ describe("Products", () => { .get("@shopUrl") .then(shopUrl => { cy.visit(shopUrl); - searchSteps.searchFor(name); + searchSteps.searchFor(productName); cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); }); }); }); }); - xit("should display not published product for staff member", () => { + it("should display not published product for staff member", () => { const productName = `${startsWith}${faker.random.number()}`; channelsUtils.getDefaultChannel().then(defaultChannel => { productsUtils @@ -114,7 +116,8 @@ describe("Products", () => { categoryId, defaultChannel.id, true, - false + false, + true ) .then(() => { cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) @@ -131,8 +134,8 @@ describe("Products", () => { cy.visit(shopUrl) .loginInShop() .then(() => { - searchSteps.searchFor(name); - cy.get(SEARCH_SELECTORS.productItem).contains(name); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); }); }); }); diff --git a/cypress/integration/products/showInListings.js b/cypress/integration/products/showInListings.js new file mode 100644 index 000000000..a4a67fe72 --- /dev/null +++ b/cypress/integration/products/showInListings.js @@ -0,0 +1,144 @@ +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"; + +// +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"); + }); + it("should display on frontend only visible in listings products", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + false + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); + }); + }); + }); + }); + it("shouldn't display not visible in listing product for unlogged user", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl); + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).should("not.exist"); + }); + }); + }); + }); + it("should display not visible in listing product for staff member", () => { + const productName = `${startsWith}${faker.random.number()}`; + channelsUtils.getDefaultChannel().then(defaultChannel => { + productsUtils + .createProductInChannel( + productName, + productTypeId, + attributeId, + categoryId, + defaultChannel.id, + true, + false, + true + ) + .then(() => { + cy.visit(`${URL_LIST.products}${productsUtils.getCreatedProductId()}`) + .get(PRODUCTS_SELECTORS.assignedChannels) + .click() + .get(PRODUCTS_SELECTORS.visibleInListingsButton) + .contains("Wyświetlaj na liście produktów") + .click() + .get(PRODUCTS_SELECTORS.saveBtn) + .click() + .waitForGraph("ProductChannelListingUpdate") + .get("@shopUrl") + .then(shopUrl => { + cy.visit(shopUrl) + .loginInShop() + .then(() => { + searchSteps.searchFor(productName); + cy.get(SEARCH_SELECTORS.productItem).contains(productName); + }); + }); + }); + }); + }); +}); diff --git a/cypress/utils/channelsUtils.js b/cypress/utils/channelsUtils.js index 7e1e58973..dbb053c6e 100644 --- a/cypress/utils/channelsUtils.js +++ b/cypress/utils/channelsUtils.js @@ -3,8 +3,6 @@ import Channels from "../apiRequests/Channels"; class ChannelsUtils { channels = new Channels(); - defaultChannel; - deleteChannels(nameStartsWith) { this.channels.getChannels().then(resp => { const channelsArray = new Set(resp.body.data.channels); @@ -28,7 +26,7 @@ class ChannelsUtils { } }); } - findDefaultChannel() { + getDefaultChannel() { return this.channels.getChannels().then(resp => { const channelsArray = resp.body.data.channels; return (this.defaultChannel = channelsArray.find(function( @@ -38,8 +36,5 @@ class ChannelsUtils { })); }); } - getDefaultChannel() { - return this.defaultChannel; - } } export default ChannelsUtils; diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index eda29a95b..914e51aaa 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -42,6 +42,7 @@ class ProductsUtils { channelId, isPublished, isAvailableForPurchase, + visibleInListings, warehouseId, quantityInWarehouse, price @@ -57,7 +58,8 @@ class ProductsUtils { this.createdProductId, channelId, isPublished, - isAvailableForPurchase + isAvailableForPurchase, + visibleInListings ) .then(() => product From b36160edb1c7f9e8ce87cbf121d096c8a2c08124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20G=C4=99bala?= <5421321+maarcingebala@users.noreply.github.com> Date: Wed, 10 Feb 2021 09:50:12 +0100 Subject: [PATCH 09/56] Update PR template (#975) --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7bb5cbabf..56f936bee 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,6 +21,8 @@ greatly reduce the amount of work needed to review your work. --> 1. [ ] Data-test are added for new elements. 1. [ ] Type definitions are up to date. 1. [ ] Changes are mentioned in the changelog. +1. [ ] The changes are tested in different browsers (Chrome, Firefox, Safari). +1. [ ] The changes are tested in light and dark mode. ### Test environment config From 27e5d912e403a981420abbb9726ad80e3fb7dbdc Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Thu, 11 Feb 2021 13:20:00 +0100 Subject: [PATCH 10/56] tests for dashboard --- cypress/apiRequests/Channels.js | 4 +- cypress/apiRequests/Checkout.js | 2 +- .../elements/dashboard/dashboard-selectors.js | 17 +++--- cypress/integration/dashboard.js | 55 +++++++++++-------- cypress/support/softAsserations/index.js | 11 ++++ cypress/utils/ordersUtils.js | 4 +- cypress/utils/productsUtils.js | 2 +- src/components/AppLayout/AppChannelSelect.tsx | 1 + .../SingleSelectField/SingleSelectField.tsx | 5 +- .../HomeActivityCard/HomeActivityCard.tsx | 5 +- .../HomeAnalyticsCard/HomeAnalyticsCard.tsx | 5 +- .../HomeNotificationTable.tsx | 6 +- src/home/components/HomePage/HomePage.tsx | 8 ++- .../HomeProductListCard.tsx | 5 +- 14 files changed, 81 insertions(+), 49 deletions(-) diff --git a/cypress/apiRequests/Channels.js b/cypress/apiRequests/Channels.js index 9afa7cc6b..197a709bc 100644 --- a/cypress/apiRequests/Channels.js +++ b/cypress/apiRequests/Channels.js @@ -55,10 +55,10 @@ class Channels { }); } - deleteChannel(channelId, targetChennelId) { + deleteChannel(channelId, targetChannelId) { const deleteChannelMutation = `mutation{ channelDelete(id: "${channelId}", input:{ - targetChannel: "${targetChennelId}" + targetChannel: "${targetChannelId}" }){ channel{ name diff --git a/cypress/apiRequests/Checkout.js b/cypress/apiRequests/Checkout.js index ca63560f4..0b611f883 100644 --- a/cypress/apiRequests/Checkout.js +++ b/cypress/apiRequests/Checkout.js @@ -50,7 +50,7 @@ class Checkout { }`; return cy.sendRequestWithQuery(mutation); } - compliteCheckout(checkoutId) { + completeCheckout(checkoutId) { const mutation = `mutation{ checkoutComplete(checkoutId:"${checkoutId}"){ order{ diff --git a/cypress/elements/dashboard/dashboard-selectors.js b/cypress/elements/dashboard/dashboard-selectors.js index 5c71fe47e..ad81a2ad8 100644 --- a/cypress/elements/dashboard/dashboard-selectors.js +++ b/cypress/elements/dashboard/dashboard-selectors.js @@ -1,11 +1,10 @@ export const DASHBOARD_SELECTORS = { - sales: "div:nth-child(1) > [class*='HomeAnalyticsCard-cardContent']", - orders: "div:nth-child(2) > [class*='HomeAnalyticsCard-cardContent']", - activity: "[class*='Grid-root'] > div:nth-child(2) > [class*='MuiPaper']", - topProducts: - "[class*='Grid-root'] > div:nth-child(1) > [class*='MuiPaper']:nth-child(4)", - ordersReadyToFulfill: "[class*='HomeNotificationTable'] > tr:nth-child(1)", - paymentsWaitingForCapture: - "[class*='HomeNotificationTable'] > tr:nth-child(2)", - productsOutOfStock: "[class*='HomeNotificationTable'] > tr:nth-child(3)" + sales: "[data-test-id='sales-analytics']", + orders: "[data-test-id='orders-analytics']", + activity: "[data-test-id='activity-card']", + topProducts: "[data-test-id='top-products']", + ordersReadyToFulfill: "[data-test-id='orders-to-fulfill']", + paymentsWaitingForCapture: "[data-test-id='orders-to-capture']", + productsOutOfStock: "[data-test-id='products-out-of-stock']", + dataAreLoading: "[class*='Skeleton-skeleton']" }; diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js index 4251afa72..471634092 100644 --- a/cypress/integration/dashboard.js +++ b/cypress/integration/dashboard.js @@ -30,15 +30,15 @@ describe("User authorization", () => { cy.clearSessionData().loginUserViaRequest(); }); - xit("should all elements be visible on the dashboard", () => { - cy.visit("/"); - softAssertVisibility(DASHBOARD_SELECTORS.sales); - softAssertVisibility(DASHBOARD_SELECTORS.orders); - softAssertVisibility(DASHBOARD_SELECTORS.activity); - softAssertVisibility(DASHBOARD_SELECTORS.topProducts); - softAssertVisibility(DASHBOARD_SELECTORS.ordersReadyToFulfill); - softAssertVisibility(DASHBOARD_SELECTORS.paymentsWaitingForCapture); - softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); + it("should all elements be visible on the dashboard", () => { + cy.visit("/") + .softAssertVisibility(DASHBOARD_SELECTORS.sales) + .softAssertVisibility(DASHBOARD_SELECTORS.orders) + .softAssertVisibility(DASHBOARD_SELECTORS.activity) + .softAssertVisibility(DASHBOARD_SELECTORS.topProducts) + .softAssertVisibility(DASHBOARD_SELECTORS.ordersReadyToFulfill) + .softAssertVisibility(DASHBOARD_SELECTORS.paymentsWaitingForCapture) + .softAssertVisibility(DASHBOARD_SELECTORS.productsOutOfStock); }); it("should correct amount of orders be displayed", () => { @@ -47,6 +47,9 @@ describe("User authorization", () => { const randomNameProductOutOfStock = `${startsWith}${faker.random.number()}`; const shippingPrice = 12; const productPrice = 22; + let sales = productPrice * 2 + shippingPrice; + + // Create channel, customer, product - everything needed to create order cy.fixture("addresses").then(json => { channels .createChannel(true, randomName, randomName, json.plAddress.currency) @@ -87,12 +90,16 @@ describe("User authorization", () => { ) .then(() => { const variantsList = productsUtils.getCreatedVariants(); - ordersUtils.createReadyToFullfillOrder( + + // Create order ready to fulfill + ordersUtils.createReadyToFulfillOrder( customerId, shippingId, channelId, variantsList ); + + // Create order waiting for capture ordersUtils.createWaitingForCaptureOrder( channelSlug, customerEmail, @@ -100,6 +107,8 @@ describe("User authorization", () => { shippingId ); }); + + // Create product out of stock productsUtils.createProductInChannel( randomNameProductOutOfStock, channelId, @@ -120,18 +129,18 @@ describe("User authorization", () => { .click() .get(HEADER_SELECTORS.channelSelectList) .contains(randomName) - .click(); + .click() + .get(DASHBOARD_SELECTORS.dataAreLoading) + .should("not.exist"); + const regex = /^1\D+/; + sales = sales.toFixed(2).replace(".", ","); + cy.softAssertMatch(DASHBOARD_SELECTORS.ordersReadyToFulfill, regex) + .softAssertMatch(DASHBOARD_SELECTORS.paymentsWaitingForCapture, regex) + .softAssertMatch(DASHBOARD_SELECTORS.productsOutOfStock, regex) + .softAssertMatch( + DASHBOARD_SELECTORS.sales, + new RegExp(`\\D+${sales}\\D+`) + ) + .softAssertMatch(DASHBOARD_SELECTORS.orders, /\D+2\D*/); }); - - function softAssertVisibility(selector) { - cy.get(selector).then(element => chai.softExpect(element).to.be.visible); - } - - function softAssertMatch(selector, regexp) { - cy.get(selector) - .invoke("text") - .then(text => - chai.softExpect(assert.match(text, regexp, "regexp matches")) - ); - } }); diff --git a/cypress/support/softAsserations/index.js b/cypress/support/softAsserations/index.js index 07dc3dcff..559ae921f 100644 --- a/cypress/support/softAsserations/index.js +++ b/cypress/support/softAsserations/index.js @@ -76,3 +76,14 @@ afterEach(() => { errors = []; isSoftAssertion = false; }); + +Cypress.Commands.add("softAssertMatch", (selector, regexp) => { + cy.get(selector) + .invoke("text") + .then(text => + chai.softExpect(assert.match(text, regexp, "regexp matches")) + ); +}); +Cypress.Commands.add("softAssertVisibility", selector => { + cy.get(selector).then(element => chai.softExpect(element).to.be.visible); +}); diff --git a/cypress/utils/ordersUtils.js b/cypress/utils/ordersUtils.js index dad9ebc29..e1416cbc7 100644 --- a/cypress/utils/ordersUtils.js +++ b/cypress/utils/ordersUtils.js @@ -19,11 +19,11 @@ class OrdersUtils { .then(() => checkout .addPayment(checkoutId, "mirumee.payments.dummy", "not-charged") - .then(() => checkout.compliteCheckout(checkoutId)) + .then(() => checkout.completeCheckout(checkoutId)) ); }); } - createReadyToFullfillOrder( + createReadyToFulfillOrder( customerId, shippingMethodId, channelId, diff --git a/cypress/utils/productsUtils.js b/cypress/utils/productsUtils.js index d02efde08..af6601b19 100644 --- a/cypress/utils/productsUtils.js +++ b/cypress/utils/productsUtils.js @@ -121,4 +121,4 @@ class ProductsUtils { }); } } -export default productsUtils; +export default ProductsUtils; diff --git a/src/components/AppLayout/AppChannelSelect.tsx b/src/components/AppLayout/AppChannelSelect.tsx index c9385288a..944443585 100644 --- a/src/components/AppLayout/AppChannelSelect.tsx +++ b/src/components/AppLayout/AppChannelSelect.tsx @@ -38,6 +38,7 @@ const AppChannelSelect: React.FC = ({ return (
= props => { hint, selectProps, placeholder, - InputProps + InputProps, + testId } = props; const classes = useStyles(props); @@ -84,6 +86,7 @@ export const SingleSelectField: React.FC = props => { {label} - - - - - Changed quantity in checkout - - -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
- -
-
- -
-
- -
+
+ +
-
- -
-
- -
-
- -
-
- -
@@ -24413,6 +24217,50 @@ exports[`Storyshots Views / Apps / Webhooks / Create webhook loading 1`] = `
+
+ +
-
- -
-
-
- -
-
- -
-
-
@@ -25686,7 +25366,7 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details default 1`] = ` - Customer created + Checkout updated
@@ -25704,7 +25384,7 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details default 1`] = ` - Fulfillment created + Customer created @@ -25809,6 +25489,48 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details default 1`] = ` + + Fulfillment created + + + +
+
-
- -
-
- -
-
- -
+
+ +
-
- -
-
- -
-
- -
+
+ +
@@ -28402,7 +27970,7 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details loading 1`] = ` class="PrivateSwitchBase-input-id" data-indeterminate="false" disabled="" - name="CUSTOMER_CREATED" + name="INVOICE_REQUESTED" type="checkbox" /> - Customer created + Invoice requested @@ -28446,7 +28014,7 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details loading 1`] = ` class="PrivateSwitchBase-input-id" data-indeterminate="false" disabled="" - name="CUSTOMER_UPDATED" + name="INVOICE_SENT" type="checkbox" /> - Customer updated + Invoice sent @@ -28738,176 +28306,6 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details loading 1`] = ` -
- -
-
- -
-
- -
-
- -
@@ -29691,6 +29091,48 @@ exports[`Storyshots Views / Apps / Webhooks / Webhook details unnamed 1`] = `
+
+ +
-
- -
-
- -
-
- -
), - value: ZipCodeInclusion.Exclude + value: PostalCodeRuleInclusionTypeEnum.EXCLUDE }, { - disabled: true, label: (
- + = ({
), - value: ZipCodeInclusion.Include + value: PostalCodeRuleInclusionTypeEnum.INCLUDE } ]} - name="includeZipCodes" - value={data.includeZipCodes} - onChange={onZipCodeInclusionChange} + name="includePostalCodes" + value={getInlcusionType()} + onChange={onInclusionRadioChange} /> @@ -145,61 +163,50 @@ const ShippingZoneZipCodes: React.FC = ({ - {zipCodes === undefined || - (zipCodes.length > 0 && ( - - - - {zipCodes === undefined ? ( - - ) : ( - - - - )} - - - setExpanded(!expanded)}> - - - - - - ))} + + + + {postalCodes === undefined ? ( + + ) : ( + + + + )} + + + setExpanded(!expanded)}> + + + + + {expanded && ( {renderCollection( - zipCodes, - zipCodeRange => ( - + postalCodes, + postalCodeRange => ( + - {zipCodeRange?.start ? ( - zipCodeRange?.end ? ( - `${zipCodeRange.start} - ${zipCodeRange.end}` - ) : ( - zipCodeRange.start - ) - ) : ( - - )} + {getPostalCodeRangeLabel(postalCodeRange)} onZipCodeDelete(zipCodeRange.id)} - data-test="delete-zip-code" - data-test-id={zipCodeRange?.id} + onClick={() => onPostalCodeDelete(postalCodeRange)} + data-test="delete-postal-code" + data-test-id={postalCodeRange?.id} > @@ -223,8 +230,5 @@ const ShippingZoneZipCodes: React.FC = ({ ); }; -ShippingZoneZipCodes.displayName = "ShippingZoneZipCodes"; -ShippingZoneZipCodes.defaultProps = { - initialExpanded: true -}; -export default ShippingZoneZipCodes; +ShippingZonePostalCodes.displayName = "ShippingZonePostalCodes"; +export default ShippingZonePostalCodes; diff --git a/src/shipping/components/ShippingZonePostalCodes/index.ts b/src/shipping/components/ShippingZonePostalCodes/index.ts new file mode 100644 index 000000000..7a8caf0cb --- /dev/null +++ b/src/shipping/components/ShippingZonePostalCodes/index.ts @@ -0,0 +1,2 @@ +export * from "./ShippingZonePostalCodes"; +export { default } from "./ShippingZonePostalCodes"; diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx index 8652232f1..8ffc5459d 100644 --- a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx +++ b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx @@ -1,5 +1,8 @@ import Decorator from "@saleor/storybook//Decorator"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import { storiesOf } from "@storybook/react"; import React from "react"; @@ -45,27 +48,30 @@ const props: ShippingZoneRatesCreatePageProps = { onBack: () => undefined, onChannelsChange: () => undefined, onDelete: () => undefined, + onPostalCodeAssign: () => undefined, + onPostalCodeInclusionChange: () => undefined, + onPostalCodeUnassign: () => undefined, onSubmit: () => undefined, - onZipCodeAssign: () => undefined, - onZipCodeUnassign: () => undefined, openChannelsModal: () => undefined, - saveButtonBarState: "default", - shippingChannels: defaultChannels, - variant: ShippingMethodTypeEnum.PRICE, - zipCodes: [ + postalCodes: [ { - __typename: "ShippingMethodZipCodeRule", + __typename: "ShippingMethodPostalCodeRule", end: "51-200", id: "1", + inclusionType: PostalCodeRuleInclusionTypeEnum.EXCLUDE, start: "51-220" }, { - __typename: "ShippingMethodZipCodeRule", + __typename: "ShippingMethodPostalCodeRule", end: "31-101", id: "1", + inclusionType: PostalCodeRuleInclusionTypeEnum.EXCLUDE, start: "44-205" } - ] + ], + saveButtonBarState: "default", + shippingChannels: defaultChannels, + variant: ShippingMethodTypeEnum.PRICE }; storiesOf("Shipping / ShippingZoneRatesCreatePage page", module) diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx index 67cd1615b..2b660be97 100644 --- a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx +++ b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx @@ -10,24 +10,24 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; -import { ShippingMethodFragment_zipCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; +import { ShippingMethodFragment_postalCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; import { validatePrice } from "@saleor/products/utils/validation"; import OrderValue from "@saleor/shipping/components/OrderValue"; import OrderWeight from "@saleor/shipping/components/OrderWeight"; import PricingCard from "@saleor/shipping/components/PricingCard"; import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo"; import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import ShippingZoneZipCodes, { - ZipCodeInclusion -} from "../ShippingZoneZipCodes"; +import ShippingZonePostalCodes from "../ShippingZonePostalCodes"; export interface FormData { channelListings: ChannelShippingData[]; - includeZipCodes: ZipCodeInclusion; name: string; noLimits: boolean; minValue: string; @@ -42,15 +42,18 @@ export interface ShippingZoneRatesCreatePageProps { shippingChannels: ChannelShippingData[]; disabled: boolean; hasChannelChanged?: boolean; - zipCodes?: ShippingMethodFragment_zipCodeRules[]; + postalCodes?: ShippingMethodFragment_postalCodeRules[]; channelErrors: ShippingChannelsErrorFragment[]; errors: ShippingErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; onDelete?: () => void; onSubmit: (data: FormData) => void; - onZipCodeAssign: () => void; - onZipCodeUnassign: (id: string) => void; + onPostalCodeInclusionChange: ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => void; + onPostalCodeAssign: () => void; + onPostalCodeUnassign: (code: any) => void; onChannelsChange: (data: ChannelShippingData[]) => void; openChannelsModal: () => void; variant: ShippingMethodTypeEnum; @@ -66,19 +69,19 @@ export const ShippingZoneRatesCreatePage: React.FC { const intl = useIntl(); const isPriceVariant = variant === ShippingMethodTypeEnum.PRICE; const initialForm: FormData = { channelListings: shippingChannels, - includeZipCodes: ZipCodeInclusion.Include, maxDays: "", maxValue: "", minDays: "", @@ -88,6 +91,12 @@ export const ShippingZoneRatesCreatePage: React.FC { + onPostalCodeInclusionChange(inclusion); + }; + return (
{({ change, data, hasChanged, submit, triggerChange }) => { @@ -154,13 +163,12 @@ export const ShippingZoneRatesCreatePage: React.FC - undefined} - onZipCodeRangeAdd={onZipCodeAssign} - zipCodes={zipCodes} + onPostalCodeDelete={onPostalCodeUnassign} + onPostalCodeInclusionChange={postalCodeInclusionChange} + onPostalCodeRangeAdd={onPostalCodeAssign} + postalCodes={postalCodes} />
diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx index a56ea4b85..538cce234 100644 --- a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx @@ -48,12 +48,13 @@ const props: ShippingZoneRatesPageProps = { onChannelsChange: () => undefined, onDelete: () => undefined, onNextPage: () => undefined, + onPostalCodeAssign: () => undefined, + onPostalCodeInclusionChange: () => undefined, + onPostalCodeUnassign: () => undefined, onPreviousPage: () => undefined, onProductAssign: () => undefined, onProductUnassign: () => undefined, onSubmit: () => undefined, - onZipCodeAssign: () => undefined, - onZipCodeUnassign: () => undefined, openChannelsModal: () => undefined, rate: shippingZone.shippingMethods[0], saveButtonBarState: "default", diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx index c360c65b1..3b0691f9e 100644 --- a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx @@ -12,6 +12,7 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; +import { ShippingMethodFragment_postalCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; import { validatePrice } from "@saleor/products/utils/validation"; import OrderValue from "@saleor/shipping/components/OrderValue"; import OrderWeight from "@saleor/shipping/components/OrderWeight"; @@ -21,19 +22,19 @@ import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo"; import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone"; import { ListActions, ListProps } from "@saleor/types"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; import { FormattedMessage } from "react-intl"; -import ShippingZoneZipCodes, { - ZipCodeInclusion -} from "../ShippingZoneZipCodes"; +import ShippingZonePostalCodes from "../ShippingZonePostalCodes"; export interface FormData extends MetadataFormData { channelListings: ChannelShippingData[]; - includeZipCodes: ZipCodeInclusion; name: string; noLimits: boolean; minValue: string; @@ -50,6 +51,7 @@ export interface ShippingZoneRatesPageProps shippingChannels: ChannelShippingData[]; disabled: boolean; hasChannelChanged?: boolean; + havePostalCodesChanged?: boolean; rate: ShippingZone_shippingZone_shippingMethods; channelErrors: ShippingChannelsErrorFragment[]; errors: ShippingErrorFragment[]; @@ -57,8 +59,11 @@ export interface ShippingZoneRatesPageProps onBack: () => void; onDelete?: () => void; onSubmit: (data: FormData) => void; - onZipCodeAssign: () => void; - onZipCodeUnassign: (id: string) => void; + onPostalCodeInclusionChange: ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => void; + onPostalCodeAssign: () => void; + onPostalCodeUnassign: (code: ShippingMethodFragment_postalCodeRules) => void; onChannelsChange: (data: ChannelShippingData[]) => void; openChannelsModal: () => void; onProductAssign: () => void; @@ -73,12 +78,14 @@ export const ShippingZoneRatesPage: React.FC = ({ disabled, errors, hasChannelChanged, + havePostalCodesChanged, onBack, onDelete, onSubmit, + onPostalCodeInclusionChange, onChannelsChange, - onZipCodeAssign, - onZipCodeUnassign, + onPostalCodeAssign, + onPostalCodeUnassign, onProductAssign, onProductUnassign, openChannelsModal, @@ -90,7 +97,6 @@ export const ShippingZoneRatesPage: React.FC = ({ const isPriceVariant = variant === ShippingMethodTypeEnum.PRICE; const initialForm: FormData = { channelListings: shippingChannels, - includeZipCodes: ZipCodeInclusion.Exclude, maxDays: rate?.maximumDeliveryDays?.toString() || "", maxValue: rate?.maximumOrderWeight?.value.toString() || "", metadata: rate?.metadata.map(mapMetadataItemToInput), @@ -102,6 +108,14 @@ export const ShippingZoneRatesPage: React.FC = ({ type: rate?.type || null }; + const postalCodesInclusionType = rate?.postalCodeRules[0]?.inclusionType; + + const postalCodeInclusionChange = ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => { + onPostalCodeInclusionChange(inclusion); + }; + const { makeChangeHandler: makeMetadataChangeHandler } = useMetadataChangeTrigger(); @@ -119,6 +133,8 @@ export const ShippingZoneRatesPage: React.FC = ({ ); const changeMetadata = makeMetadataChangeHandler(change); + const formIsUnchanged = + !hasChanged && !hasChannelChanged && !havePostalCodesChanged; return ( @@ -162,13 +178,13 @@ export const ShippingZoneRatesPage: React.FC = ({ errors={channelErrors} /> - undefined} - onZipCodeRangeAdd={onZipCodeAssign} - zipCodes={rate?.zipCodeRules} + onPostalCodeDelete={onPostalCodeUnassign} + onPostalCodeInclusionChange={postalCodeInclusionChange} + onPostalCodeRangeAdd={onPostalCodeAssign} + postalCodes={rate?.postalCodeRules} + initialInclusionType={postalCodesInclusionType} /> = ({
+ rules + .filter(code => !code.id || code.id === "0") + .map( + code => + ({ + end: code.end, + start: code.start + } as ShippingPostalCodeRulesCreateInputRange) + ); + export function getCreateShippingPriceRateVariables( data: ShippingZoneRatesPageFormData, - id: string + id: string, + addPostalCodeRules: ShippingMethodFragment_postalCodeRules[], + inclusionType: PostalCodeRuleInclusionTypeEnum ): CreateShippingRateVariables { const parsedMinDays = parseInt(data.minDays, 10); const parsedMaxDays = parseInt(data.maxDays, 10); + const postalCodeRules = getPostalCodeRulesToAdd(addPostalCodeRules); return { input: { + addPostalCodeRules: postalCodeRules, + inclusionType, maximumDeliveryDays: parsedMaxDays, minimumDeliveryDays: parsedMinDays, name: data.name, @@ -62,15 +83,20 @@ export function getCreateShippingPriceRateVariables( export function getCreateShippingWeightRateVariables( data: ShippingZoneRatesPageFormData, - id: string + id: string, + addPostalCodeRules: ShippingMethodFragment_postalCodeRules[], + inclusionType: PostalCodeRuleInclusionTypeEnum ): CreateShippingRateVariables { const parsedMinValue = parseFloat(data.minValue); const parsedMaxValue = parseFloat(data.maxValue); const parsedMinDays = parseInt(data.minDays, 10); const parsedMaxDays = parseInt(data.maxDays, 10); const isWeightSet = !data.noLimits; + const postalCodeRules = getPostalCodeRulesToAdd(addPostalCodeRules); return { input: { + addPostalCodeRules: postalCodeRules, + inclusionType, maximumDeliveryDays: parsedMaxDays, maximumOrderWeight: isWeightSet ? parsedMaxValue : null, minimumDeliveryDays: parsedMinDays, @@ -85,13 +111,21 @@ export function getCreateShippingWeightRateVariables( export function getUpdateShippingPriceRateVariables( data: ShippingZoneRatesPageFormData, id: string, - rateId: string + rateId: string, + addPostalCodeRules: ShippingMethodFragment_postalCodeRules[], + deletePostalCodeRules: string[] ): UpdateShippingRateVariables { const parsedMinDays = parseInt(data.minDays, 10); const parsedMaxDays = parseInt(data.maxDays, 10); + const postalCodeRules = getPostalCodeRulesToAdd(addPostalCodeRules); return { id: rateId, input: { + addPostalCodeRules: postalCodeRules, + deletePostalCodeRules, + inclusionType: + addPostalCodeRules[0]?.inclusionType || + PostalCodeRuleInclusionTypeEnum.EXCLUDE, maximumDeliveryDays: parsedMaxDays, minimumDeliveryDays: parsedMinDays, name: data.name, @@ -104,16 +138,24 @@ export function getUpdateShippingPriceRateVariables( export function getUpdateShippingWeightRateVariables( data: ShippingZoneRatesPageFormData, id: string, - rateId: string + rateId: string, + addPostalCodeRules: ShippingMethodFragment_postalCodeRules[], + deletePostalCodeRules: string[] ): UpdateShippingRateVariables { const parsedMinValue = parseFloat(data.minValue); const parsedMaxValue = parseFloat(data.maxValue); const parsedMinDays = parseInt(data.minDays, 10); const parsedMaxDays = parseInt(data.maxDays, 10); const isWeightSet = !data.noLimits; + const postalCodeRules = getPostalCodeRulesToAdd(addPostalCodeRules); return { id: rateId, input: { + addPostalCodeRules: postalCodeRules, + deletePostalCodeRules, + inclusionType: + addPostalCodeRules[0]?.inclusionType || + PostalCodeRuleInclusionTypeEnum.EXCLUDE, maximumDeliveryDays: parsedMaxDays, maximumOrderWeight: isWeightSet ? parsedMaxValue : null, minimumDeliveryDays: parsedMinDays, @@ -156,7 +198,8 @@ export function getShippingMethodChannelVariables( export function useShippingRateCreator( shippingZoneId: string, type: ShippingMethodTypeEnum, - zipCodes: ShippingMethodFragment_zipCodeRules[] + postalCodes: ShippingMethodFragment_postalCodeRules[], + inclusionType: PostalCodeRuleInclusionTypeEnum ) { const intl = useIntl(); const notify = useNotifier(); @@ -165,10 +208,6 @@ export function useShippingRateCreator( createBaseShippingRate, createBaseShippingRateOpts ] = useShippingRateCreate({}); - const [ - assignZipCodeRules, - assignZipCodeRulesOpts - ] = useShippingMethodZipCodeRangeAssign({}); const [ updateShippingMethodChannelListing, updateShippingMethodChannelListingOpts @@ -186,7 +225,7 @@ export function useShippingRateCreator( const createShippingRate = async (data: ShippingZoneRatesPageFormData) => { const response = await createBaseShippingRate({ - variables: getVariables(data, shippingZoneId) + variables: getVariables(data, shippingZoneId, postalCodes, inclusionType) }); const createErrors = response.data.shippingPriceCreate.errors; @@ -200,17 +239,6 @@ export function useShippingRateCreator( data.noLimits, data.channelListings ) - }), - assignZipCodeRules({ - variables: { - id: rateId, - input: { - zipCodeRules: zipCodes.map(zipCodeRule => ({ - end: zipCodeRule.end || null, - start: zipCodeRule.start - })) - } - } }) ]); @@ -236,16 +264,12 @@ export function useShippingRateCreator( const called = createBaseShippingRateOpts.called || - updateShippingMethodChannelListingOpts.called || - assignZipCodeRulesOpts.called; + updateShippingMethodChannelListingOpts.called; const loading = createBaseShippingRateOpts.loading || - updateShippingMethodChannelListingOpts.loading || - assignZipCodeRulesOpts.loading; + updateShippingMethodChannelListingOpts.loading; const errors = [ - ...(createBaseShippingRateOpts.data?.shippingPriceCreate.errors || []), - ...(assignZipCodeRulesOpts.data?.shippingMethodZipCodeRulesCreate.errors || - []) + ...(createBaseShippingRateOpts.data?.shippingPriceCreate.errors || []) ]; const channelErrors = updateShippingMethodChannelListingOpts.data diff --git a/src/shipping/mutations.ts b/src/shipping/mutations.ts index 8874db557..b69d9bb8c 100644 --- a/src/shipping/mutations.ts +++ b/src/shipping/mutations.ts @@ -4,7 +4,6 @@ import { } from "@saleor/fragments/errors"; import { shippingMethodFragment, - shippingMethodWithZipCodesFragment, shippingZoneDetailsFragment } from "@saleor/fragments/shipping"; import { countryFragment } from "@saleor/fragments/taxes"; @@ -39,14 +38,6 @@ import { ShippingMethodChannelListingUpdate, ShippingMethodChannelListingUpdateVariables } from "./types/ShippingMethodChannelListingUpdate"; -import { - ShippingMethodZipCodeRangeAssign, - ShippingMethodZipCodeRangeAssignVariables -} from "./types/ShippingMethodZipCodeRangeAssign"; -import { - ShippingMethodZipCodeRangeUnassign, - ShippingMethodZipCodeRangeUnassignVariables -} from "./types/ShippingMethodZipCodeRangeUnassign"; import { ShippingPriceExcludeProduct, ShippingPriceExcludeProductVariables @@ -263,49 +254,6 @@ export const useShippingMethodChannelListingUpdate = makeMutation< ShippingMethodChannelListingUpdateVariables >(shippingMethodChannelListingUpdate); -export const shippingMethodZipCodeRangeAssign = gql` - ${shippingChannelsErrorFragment} - ${shippingMethodWithZipCodesFragment} - mutation ShippingMethodZipCodeRangeAssign( - $id: ID! - $input: ShippingZipCodeRulesCreateInput! - ) { - shippingMethodZipCodeRulesCreate(shippingMethodId: $id, input: $input) { - errors: shippingErrors { - ...ShippingChannelsErrorFragment - } - shippingMethod { - ...ShippingMethodWithZipCodesFragment - } - } - } -`; - -export const useShippingMethodZipCodeRangeAssign = makeMutation< - ShippingMethodZipCodeRangeAssign, - ShippingMethodZipCodeRangeAssignVariables ->(shippingMethodZipCodeRangeAssign); - -export const shippingMethodZipCodeRulesDelete = gql` - ${shippingChannelsErrorFragment} - ${shippingMethodWithZipCodesFragment} - mutation ShippingMethodZipCodeRangeUnassign($id: ID!) { - shippingMethodZipCodeRulesDelete(id: $id) { - errors: shippingErrors { - ...ShippingChannelsErrorFragment - } - shippingMethod { - ...ShippingMethodWithZipCodesFragment - } - } - } -`; - -export const useShippingMethodZipCodeRangeUnassign = makeMutation< - ShippingMethodZipCodeRangeUnassign, - ShippingMethodZipCodeRangeUnassignVariables ->(shippingMethodZipCodeRulesDelete); - export const shippingPriceExcludeProducts = gql` ${shippingErrorFragment} mutation ShippingPriceExcludeProduct( diff --git a/src/shipping/types/CreateShippingRate.ts b/src/shipping/types/CreateShippingRate.ts index c98d9827b..b14849f1c 100644 --- a/src/shipping/types/CreateShippingRate.ts +++ b/src/shipping/types/CreateShippingRate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ShippingPriceInput, ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; +import { ShippingPriceInput, ShippingErrorCode, PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CreateShippingRate @@ -32,9 +32,10 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_countries { country: string; } -export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -100,7 +101,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMet export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - zipCodeRules: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_zipCodeRules | null)[] | null; + postalCodeRules: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_postalCodeRules | null)[] | null; metadata: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_metadata | null)[]; privateMetadata: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_privateMetadata | null)[]; minimumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderWeight | null; @@ -131,9 +132,10 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone { warehouses: (CreateShippingRate_shippingPriceCreate_shippingZone_warehouses | null)[] | null; } -export interface CreateShippingRate_shippingPriceCreate_shippingMethod_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -199,7 +201,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelLi export interface CreateShippingRate_shippingPriceCreate_shippingMethod { __typename: "ShippingMethod"; id: string; - zipCodeRules: (CreateShippingRate_shippingPriceCreate_shippingMethod_zipCodeRules | null)[] | null; + postalCodeRules: (CreateShippingRate_shippingPriceCreate_shippingMethod_postalCodeRules | null)[] | null; metadata: (CreateShippingRate_shippingPriceCreate_shippingMethod_metadata | null)[]; privateMetadata: (CreateShippingRate_shippingPriceCreate_shippingMethod_privateMetadata | null)[]; minimumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingMethod_minimumOrderWeight | null; diff --git a/src/shipping/types/DeleteShippingRate.ts b/src/shipping/types/DeleteShippingRate.ts index 8d63eea92..d71b61787 100644 --- a/src/shipping/types/DeleteShippingRate.ts +++ b/src/shipping/types/DeleteShippingRate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; +import { ShippingErrorCode, PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: DeleteShippingRate @@ -32,9 +32,10 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_countries { country: string; } -export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -100,7 +101,7 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMet export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - zipCodeRules: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_zipCodeRules | null)[] | null; + postalCodeRules: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_postalCodeRules | null)[] | null; metadata: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_metadata | null)[]; privateMetadata: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_privateMetadata | null)[]; minimumOrderWeight: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderWeight | null; diff --git a/src/shipping/types/ShippingMethodChannelListingUpdate.ts b/src/shipping/types/ShippingMethodChannelListingUpdate.ts index 1eae5797c..dc8122772 100644 --- a/src/shipping/types/ShippingMethodChannelListingUpdate.ts +++ b/src/shipping/types/ShippingMethodChannelListingUpdate.ts @@ -2,15 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ShippingMethodChannelListingInput, WeightUnitsEnum, ShippingMethodTypeEnum, ShippingErrorCode } from "./../../types/globalTypes"; +import { ShippingMethodChannelListingInput, PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum, ShippingErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ShippingMethodChannelListingUpdate // ==================================================== -export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -76,7 +77,7 @@ export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListing export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod { __typename: "ShippingMethod"; id: string; - zipCodeRules: (ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_zipCodeRules | null)[] | null; + postalCodeRules: (ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_postalCodeRules | null)[] | null; metadata: (ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_metadata | null)[]; privateMetadata: (ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_privateMetadata | null)[]; minimumOrderWeight: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_minimumOrderWeight | null; diff --git a/src/shipping/types/ShippingMethodZipCodeRangeAssign.ts b/src/shipping/types/ShippingMethodZipCodeRangeAssign.ts deleted file mode 100644 index e10053f47..000000000 --- a/src/shipping/types/ShippingMethodZipCodeRangeAssign.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// This file was automatically generated and should not be edited. - -import { ShippingZipCodeRulesCreateInput, ShippingErrorCode } from "./../../types/globalTypes"; - -// ==================================================== -// GraphQL mutation operation: ShippingMethodZipCodeRangeAssign -// ==================================================== - -export interface ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_errors { - __typename: "ShippingError"; - code: ShippingErrorCode; - field: string | null; - channels: string[] | null; -} - -export interface ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_shippingMethod_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; - id: string; - start: string | null; - end: string | null; -} - -export interface ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_shippingMethod { - __typename: "ShippingMethod"; - id: string; - zipCodeRules: (ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_shippingMethod_zipCodeRules | null)[] | null; -} - -export interface ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate { - __typename: "ShippingZipCodeRulesCreate"; - errors: ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_errors[]; - shippingMethod: ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate_shippingMethod | null; -} - -export interface ShippingMethodZipCodeRangeAssign { - shippingMethodZipCodeRulesCreate: ShippingMethodZipCodeRangeAssign_shippingMethodZipCodeRulesCreate | null; -} - -export interface ShippingMethodZipCodeRangeAssignVariables { - id: string; - input: ShippingZipCodeRulesCreateInput; -} diff --git a/src/shipping/types/ShippingMethodZipCodeRangeUnassign.ts b/src/shipping/types/ShippingMethodZipCodeRangeUnassign.ts deleted file mode 100644 index 23fd0ca4a..000000000 --- a/src/shipping/types/ShippingMethodZipCodeRangeUnassign.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// This file was automatically generated and should not be edited. - -import { ShippingErrorCode } from "./../../types/globalTypes"; - -// ==================================================== -// GraphQL mutation operation: ShippingMethodZipCodeRangeUnassign -// ==================================================== - -export interface ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_errors { - __typename: "ShippingError"; - code: ShippingErrorCode; - field: string | null; - channels: string[] | null; -} - -export interface ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_shippingMethod_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; - id: string; - start: string | null; - end: string | null; -} - -export interface ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_shippingMethod { - __typename: "ShippingMethod"; - id: string; - zipCodeRules: (ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_shippingMethod_zipCodeRules | null)[] | null; -} - -export interface ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete { - __typename: "ShippingZipCodeRulesDelete"; - errors: ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_errors[]; - shippingMethod: ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete_shippingMethod | null; -} - -export interface ShippingMethodZipCodeRangeUnassign { - shippingMethodZipCodeRulesDelete: ShippingMethodZipCodeRangeUnassign_shippingMethodZipCodeRulesDelete | null; -} - -export interface ShippingMethodZipCodeRangeUnassignVariables { - id: string; -} diff --git a/src/shipping/types/ShippingZone.ts b/src/shipping/types/ShippingZone.ts index c717e840a..7e8f24f83 100644 --- a/src/shipping/types/ShippingZone.ts +++ b/src/shipping/types/ShippingZone.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; +import { PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ShippingZone @@ -26,9 +26,10 @@ export interface ShippingZone_shippingZone_countries { country: string; } -export interface ShippingZone_shippingZone_shippingMethods_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface ShippingZone_shippingZone_shippingMethods_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -125,7 +126,7 @@ export interface ShippingZone_shippingZone_shippingMethods_excludedProducts { export interface ShippingZone_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - zipCodeRules: (ShippingZone_shippingZone_shippingMethods_zipCodeRules | null)[] | null; + postalCodeRules: (ShippingZone_shippingZone_shippingMethods_postalCodeRules | null)[] | null; metadata: (ShippingZone_shippingZone_shippingMethods_metadata | null)[]; privateMetadata: (ShippingZone_shippingZone_shippingMethods_privateMetadata | null)[]; minimumOrderWeight: ShippingZone_shippingZone_shippingMethods_minimumOrderWeight | null; diff --git a/src/shipping/types/UpdateShippingRate.ts b/src/shipping/types/UpdateShippingRate.ts index ac938c79c..6e30ff3f0 100644 --- a/src/shipping/types/UpdateShippingRate.ts +++ b/src/shipping/types/UpdateShippingRate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ShippingPriceInput, ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; +import { ShippingPriceInput, ShippingErrorCode, PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: UpdateShippingRate @@ -14,9 +14,10 @@ export interface UpdateShippingRate_shippingPriceUpdate_errors { field: string | null; } -export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_zipCodeRules { - __typename: "ShippingMethodZipCodeRule"; +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_postalCodeRules { + __typename: "ShippingMethodPostalCodeRule"; id: string; + inclusionType: PostalCodeRuleInclusionTypeEnum | null; start: string | null; end: string | null; } @@ -82,7 +83,7 @@ export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelLi export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod { __typename: "ShippingMethod"; id: string; - zipCodeRules: (UpdateShippingRate_shippingPriceUpdate_shippingMethod_zipCodeRules | null)[] | null; + postalCodeRules: (UpdateShippingRate_shippingPriceUpdate_shippingMethod_postalCodeRules | null)[] | null; metadata: (UpdateShippingRate_shippingPriceUpdate_shippingMethod_metadata | null)[]; privateMetadata: (UpdateShippingRate_shippingPriceUpdate_shippingMethod_privateMetadata | null)[]; minimumOrderWeight: UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderWeight | null; diff --git a/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx index 7df7c265b..3d98020cc 100644 --- a/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx +++ b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx @@ -2,13 +2,11 @@ import { useChannelsList } from "@saleor/channels/queries"; import { createSortedShippingChannels } from "@saleor/channels/utils"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; -import { ShippingMethodFragment_zipCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import { sectionNames } from "@saleor/intl"; -import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; +import ShippingZonePostalCodeRangeDialog from "@saleor/shipping/components/ShippingZonePostalCodeRangeDialog"; import ShippingZoneRatesCreatePage from "@saleor/shipping/components/ShippingZoneRatesCreatePage"; -import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import { useShippingRateCreator } from "@saleor/shipping/handlers"; import { shippingPriceRatesUrl, @@ -16,10 +14,13 @@ import { ShippingRateCreateUrlQueryParams, shippingZoneUrl } from "@saleor/shipping/urls"; +import filterPostalCodes from "@saleor/shipping/views/utils"; import { MinMax } from "@saleor/types"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; -import { remove } from "@saleor/utils/lists"; import React from "react"; import { useIntl } from "react-intl"; @@ -35,9 +36,10 @@ export const PriceRatesCreate: React.FC = ({ const navigate = useNavigator(); const intl = useIntl(); - const [zipCodes, setZipCodes] = React.useState< - ShippingMethodFragment_zipCodeRules[] - >([]); + const [postalCodes, setPostalCodes] = React.useState([]); + const [radioInclusionType, setRadioInclusionType] = React.useState( + PostalCodeRuleInclusionTypeEnum.EXCLUDE + ); const { data: channelsData, loading: channelsLoading } = useChannelsList({}); @@ -66,30 +68,35 @@ export const PriceRatesCreate: React.FC = ({ createShippingRate, errors, status - } = useShippingRateCreator(id, ShippingMethodTypeEnum.PRICE, zipCodes); + } = useShippingRateCreator( + id, + ShippingMethodTypeEnum.PRICE, + postalCodes, + radioInclusionType + ); const handleBack = () => navigate(shippingZoneUrl(id)); - const handleZipCodeRangeAdd = (data: MinMax) => { - setZipCodes(zipCodes => [ - ...zipCodes, + const handlePostalCodeRangeAdd = (data: MinMax) => { + setPostalCodes(postalCodes => [ + ...postalCodes, { - __typename: "ShippingMethodZipCodeRule", end: data.max, - id: zipCodes.length.toString(), start: data.min } ]); closeModal(); }; - const handleZipCodeRangeDelete = (id: string) => { - setZipCodes(zipCodes => - remove( - zipCodes.find(zipCode => zipCode.id === id), - zipCodes, - (a, b) => a.id === b.id - ) - ); + + const onPostalCodeInclusionChange = ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => { + setRadioInclusionType(inclusion); + setPostalCodes([]); + }; + + const onPostalCodeUnassign = code => { + setPostalCodes(filterPostalCodes(postalCodes, code)); closeModal(); }; @@ -123,29 +130,20 @@ export const PriceRatesCreate: React.FC = ({ onBack={handleBack} errors={errors} channelErrors={channelErrors} - zipCodes={zipCodes} + postalCodes={postalCodes} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} - onZipCodeAssign={() => openModal("add-range")} - onZipCodeUnassign={id => - openModal("remove-range", { - id - }) - } + onPostalCodeAssign={() => openModal("add-range")} + onPostalCodeUnassign={onPostalCodeUnassign} + onPostalCodeInclusionChange={onPostalCodeInclusionChange} variant={ShippingMethodTypeEnum.PRICE} /> - - handleZipCodeRangeDelete(params.id)} - open={params.action === "remove-range"} - /> ); }; diff --git a/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx index 010cbb8bd..a6c9c3eaa 100644 --- a/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx +++ b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx @@ -20,11 +20,10 @@ import { commonMessages } from "@saleor/intl"; import useProductSearch from "@saleor/searches/useProductSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; import ShippingMethodProductsAddDialog from "@saleor/shipping/components/ShippingMethodProductsAddDialog"; -import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; +import ShippingZonePostalCodeRangeDialog from "@saleor/shipping/components/ShippingZonePostalCodeRangeDialog"; import ShippingZoneRatesPage, { FormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; -import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import UnassignDialog from "@saleor/shipping/components/UnassignDialog"; import { getShippingMethodChannelVariables, @@ -32,8 +31,6 @@ import { } from "@saleor/shipping/handlers"; import { useShippingMethodChannelListingUpdate, - useShippingMethodZipCodeRangeAssign, - useShippingMethodZipCodeRangeUnassign, useShippingPriceExcludeProduct, useShippingPriceRemoveProductsFromExclude, useShippingRateDelete, @@ -46,7 +43,12 @@ import { ShippingRateUrlQueryParams, shippingZoneUrl } from "@saleor/shipping/urls"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import filterPostalCodes from "@saleor/shipping/views/utils"; +import { MinMax } from "@saleor/types"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -110,43 +112,6 @@ export const PriceRatesUpdate: React.FC = ({ updateShippingMethodChannelListingOpts ] = useShippingMethodChannelListingUpdate({}); - const [ - assignZipCodeRange, - assignZipCodeRangeOpts - ] = useShippingMethodZipCodeRangeAssign({ - onCompleted: data => { - if (data.shippingMethodZipCodeRulesCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } else { - notify({ - status: "error", - text: intl.formatMessage({ - defaultMessage: "Cannot add specified postal codes range.", - description: "postal code range add error text" - }) - }); - } - } - }); - - const [ - unassignZipCodeRange, - unassignZipCodeRangeOpts - ] = useShippingMethodZipCodeRangeUnassign({ - onCompleted: data => { - if (data.shippingMethodZipCodeRulesDelete.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - } - } - }); - const [ unassignProduct, unassignProductOpts @@ -209,10 +174,40 @@ export const PriceRatesUpdate: React.FC = ({ const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + const [codesToDelete, setCodesToDelete] = React.useState([]); + const [havePostalCodesChanged, setHavePostalCodesChanged] = React.useState( + false + ); + const [originalCodes, setOriginalCodes] = React.useState([]); + const [inclusionType, setInclusionType] = React.useState( + rate?.postalCodeRules[0]?.inclusionType + ); + + const onPostalCodeInclusionChange = ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => { + setInclusionType(inclusion); + setCodesToDelete( + rate.postalCodeRules + .filter(code => code.id !== undefined) + .map(code => code.id) + ); + setHavePostalCodesChanged(true); + rate.postalCodeRules = []; + }; + const updateData = async (formData: FormData): Promise => { const response = await updateShippingRate({ - variables: getUpdateShippingPriceRateVariables(formData, id, rateId) + variables: getUpdateShippingPriceRateVariables( + formData, + id, + rateId, + rate.postalCodeRules, + codesToDelete + ) }); + setCodesToDelete([]); + setHavePostalCodesChanged(false); const errors = response.data.shippingPriceUpdate.errors; if (errors.length === 0) { handleSuccess(); @@ -248,6 +243,41 @@ export const PriceRatesUpdate: React.FC = ({ reset(); }; + const onPostalCodeAssign = (rule: MinMax) => { + if (!originalCodes.length) { + setOriginalCodes([...rate.postalCodeRules]); + } + if ( + rate.postalCodeRules.filter( + item => item.start === rule.min && item.end === rule.max + ).length > 0 + ) { + closeModal(); + return; + } + const newCode = { + __typename: undefined, + end: rule.max, + id: undefined, + inclusionType, + start: rule.min + }; + rate.postalCodeRules.push(newCode); + closeModal(); + }; + + const onPostalCodeUnassign = code => { + if (code.id !== undefined) { + setCodesToDelete([...codesToDelete, code.id]); + rate.postalCodeRules = rate.postalCodeRules.filter( + rule => rule.id !== code.id + ); + } else { + rate.postalCodeRules = filterPostalCodes(rate.postalCodeRules, code); + } + setHavePostalCodesChanged(true); + }; + const handleBack = () => navigate(shippingZoneUrl(id)); return ( @@ -314,6 +344,7 @@ export const PriceRatesUpdate: React.FC = ({ assignProductOpts?.status === "loading" } hasChannelChanged={shippingChannels?.length !== currentChannels?.length} + havePostalCodesChanged={havePostalCodesChanged} saveButtonBarState={updateShippingRateOpts.status} onDelete={() => openModal("remove")} onSubmit={handleSubmit} @@ -344,45 +375,19 @@ export const PriceRatesUpdate: React.FC = ({ /> } - onZipCodeAssign={() => openModal("add-range")} - onZipCodeUnassign={id => - openModal("remove-range", { - id - }) - } + onPostalCodeInclusionChange={onPostalCodeInclusionChange} + onPostalCodeAssign={() => openModal("add-range")} + onPostalCodeUnassign={onPostalCodeUnassign} /> - - assignZipCodeRange({ - variables: { - id: rateId, - input: { - zipCodeRules: [ - { - end: data.max || null, - start: data.min - } - ] - } - } - }) - } + onSubmit={code => { + onPostalCodeAssign(code); + setHavePostalCodesChanged(true); + }} open={params.action === "add-range"} /> - - unassignZipCodeRange({ - variables: { - id: params.id - } - }) - } - open={params.action === "remove-range"} - /> ); }; diff --git a/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx b/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx index da233ae65..eccc1e15d 100644 --- a/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx +++ b/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx @@ -5,13 +5,11 @@ import { } from "@saleor/channels/utils"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; -import { ShippingMethodFragment_zipCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import { sectionNames } from "@saleor/intl"; -import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; +import ShippingZonePostalCodeRangeDialog from "@saleor/shipping/components/ShippingZonePostalCodeRangeDialog"; import ShippingZoneRatesCreatePage from "@saleor/shipping/components/ShippingZoneRatesCreatePage"; -import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import { useShippingRateCreator } from "@saleor/shipping/handlers"; import { ShippingRateCreateUrlDialog, @@ -19,10 +17,13 @@ import { shippingWeightRatesUrl, shippingZoneUrl } from "@saleor/shipping/urls"; +import filterPostalCodes from "@saleor/shipping/views/utils"; import { MinMax } from "@saleor/types"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; -import { remove } from "@saleor/utils/lists"; import React from "react"; import { useIntl } from "react-intl"; @@ -38,9 +39,10 @@ export const WeightRatesCreate: React.FC = ({ const navigate = useNavigator(); const intl = useIntl(); - const [zipCodes, setZipCodes] = React.useState< - ShippingMethodFragment_zipCodeRules[] - >([]); + const [postalCodes, setPostalCodes] = React.useState([]); + const [radioInclusionType, setRadioInclusionType] = React.useState( + PostalCodeRuleInclusionTypeEnum.EXCLUDE + ); const { data: channelsData, loading: channelsLoading } = useChannelsList({}); @@ -70,30 +72,38 @@ export const WeightRatesCreate: React.FC = ({ createShippingRate, errors, status - } = useShippingRateCreator(id, ShippingMethodTypeEnum.WEIGHT, zipCodes); + } = useShippingRateCreator( + id, + ShippingMethodTypeEnum.WEIGHT, + postalCodes, + radioInclusionType + ); const handleBack = () => navigate(shippingZoneUrl(id)); - const handleZipCodeRangeAdd = (data: MinMax) => { - setZipCodes(zipCodes => [ - ...zipCodes, + const handlePostalCodeRangeAdd = (data: MinMax) => { + setPostalCodes(postalCodes => [ + ...postalCodes, { - __typename: "ShippingMethodZipCodeRule", + __typename: "ShippingMethodPostalCodeRule", end: data.max, - id: zipCodes.length.toString(), + id: postalCodes.length.toString(), + inclusionType: postalCodes?.[0]?.inclusionType, start: data.min } ]); closeModal(); }; - const handleZipCodeRangeDelete = (id: string) => { - setZipCodes(zipCodes => - remove( - zipCodes.find(zipCode => zipCode.id === id), - zipCodes, - (a, b) => a.id === b.id - ) - ); + + const onPostalCodeInclusionChange = ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => { + setRadioInclusionType(inclusion); + setPostalCodes([]); + }; + + const onPostalCodeUnassign = code => { + setPostalCodes(filterPostalCodes(postalCodes, code)); closeModal(); }; @@ -126,29 +136,20 @@ export const WeightRatesCreate: React.FC = ({ onBack={handleBack} errors={errors} channelErrors={channelErrors} - zipCodes={zipCodes} + postalCodes={postalCodes} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} - onZipCodeAssign={() => openModal("add-range")} - onZipCodeUnassign={id => - openModal("remove-range", { - id - }) - } + onPostalCodeAssign={() => openModal("add-range")} + onPostalCodeUnassign={onPostalCodeUnassign} + onPostalCodeInclusionChange={onPostalCodeInclusionChange} variant={ShippingMethodTypeEnum.WEIGHT} /> - - handleZipCodeRangeDelete(params.id)} - open={params.action === "remove-range"} - /> ); }; diff --git a/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx index 53193e688..aa7984c31 100644 --- a/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx +++ b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx @@ -20,11 +20,10 @@ import { commonMessages } from "@saleor/intl"; import useProductSearch from "@saleor/searches/useProductSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; import ShippingMethodProductsAddDialog from "@saleor/shipping/components/ShippingMethodProductsAddDialog"; -import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; +import ShippingZonePostalCodeRangeDialog from "@saleor/shipping/components/ShippingZonePostalCodeRangeDialog"; import ShippingZoneRatesPage, { FormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; -import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import UnassignDialog from "@saleor/shipping/components/UnassignDialog"; import { getShippingMethodChannelVariables, @@ -32,8 +31,6 @@ import { } from "@saleor/shipping/handlers"; import { useShippingMethodChannelListingUpdate, - useShippingMethodZipCodeRangeAssign, - useShippingMethodZipCodeRangeUnassign, useShippingPriceExcludeProduct, useShippingPriceRemoveProductsFromExclude, useShippingRateDelete, @@ -46,7 +43,12 @@ import { shippingWeightRatesEditUrl, shippingZoneUrl } from "@saleor/shipping/urls"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import filterPostalCodes from "@saleor/shipping/views/utils"; +import { MinMax } from "@saleor/types"; +import { + PostalCodeRuleInclusionTypeEnum, + ShippingMethodTypeEnum +} from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -84,6 +86,51 @@ export const WeightRatesUpdate: React.FC = ({ ShippingRateUrlQueryParams >(navigate, params => shippingWeightRatesEditUrl(id, rateId, params), params); + const [codesToDelete, setCodesToDelete] = React.useState([]); + const [havePostalCodesChanged, setHavePostalCodesChanged] = React.useState( + false + ); + const [originalCodes, setOriginalCodes] = React.useState([]); + const [inclusionType, setInclusionType] = React.useState( + PostalCodeRuleInclusionTypeEnum.EXCLUDE + ); + + const onPostalCodeInclusionChange = ( + inclusion: PostalCodeRuleInclusionTypeEnum + ) => { + setInclusionType(inclusion); + setCodesToDelete( + rate.postalCodeRules + .filter(code => code.id !== undefined) + .map(code => code.id) + ); + setHavePostalCodesChanged(true); + rate.postalCodeRules = []; + }; + + const onPostalCodeAssign = (rule: MinMax) => { + if (!originalCodes.length) { + setOriginalCodes([...rate.postalCodeRules]); + } + if ( + rate.postalCodeRules.filter( + item => item.start === rule.min && item.end === rule.max + ).length > 0 + ) { + closeModal(); + return; + } + const newCode = { + __typename: undefined, + end: rule.max, + id: undefined, + inclusionType, + start: rule.min + }; + rate.postalCodeRules.push(newCode); + closeModal(); + }; + const { loadMore, search: productsSearch, @@ -171,50 +218,21 @@ export const WeightRatesUpdate: React.FC = ({ } }); - const [ - assignZipCodeRange, - assignZipCodeRangeOpts - ] = useShippingMethodZipCodeRangeAssign({ - onCompleted: data => { - if (data.shippingMethodZipCodeRulesCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } else { - notify({ - status: "error", - text: intl.formatMessage({ - defaultMessage: "Cannot add specified postal codes range.", - description: "postal code range add error text" - }) - }); - } - } - }); - const [ - unassignZipCodeRange, - unassignZipCodeRangeOpts - ] = useShippingMethodZipCodeRangeUnassign({ - onCompleted: data => { - if (data.shippingMethodZipCodeRulesDelete.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - } - }); - const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const updateData = async (data: FormData) => { const response = await updateShippingRate({ - variables: getUpdateShippingWeightRateVariables(data, id, rateId) + variables: getUpdateShippingWeightRateVariables( + data, + id, + rateId, + rate.postalCodeRules, + codesToDelete + ) }); + setCodesToDelete([]); + setHavePostalCodesChanged(false); const errors = response.data.shippingPriceUpdate.errors; if (errors.length === 0) { handleSuccess(); @@ -231,6 +249,18 @@ export const WeightRatesUpdate: React.FC = ({ return errors; }; + const onPostalCodeUnassign = code => { + if (code.id !== undefined) { + setCodesToDelete([...codesToDelete, code.id]); + rate.postalCodeRules = rate.postalCodeRules.filter( + rule => rule.id !== code.id + ); + } else { + rate.postalCodeRules = filterPostalCodes(rate.postalCodeRules, code); + } + setHavePostalCodesChanged(true); + }; + const handleSubmit = createMetadataUpdateHandler( rate, updateData, @@ -316,6 +346,7 @@ export const WeightRatesUpdate: React.FC = ({ assignProductOpts?.status === "loading" } hasChannelChanged={shippingChannels?.length !== currentChannels?.length} + havePostalCodesChanged={havePostalCodesChanged} saveButtonBarState={updateShippingRateOpts.status} onDelete={() => openModal("remove")} onSubmit={handleSubmit} @@ -346,45 +377,19 @@ export const WeightRatesUpdate: React.FC = ({ /> } - onZipCodeAssign={() => openModal("add-range")} - onZipCodeUnassign={id => - openModal("remove-range", { - id - }) - } + onPostalCodeInclusionChange={onPostalCodeInclusionChange} + onPostalCodeAssign={() => openModal("add-range")} + onPostalCodeUnassign={onPostalCodeUnassign} /> - - assignZipCodeRange({ - variables: { - id: rateId, - input: { - zipCodeRules: [ - { - end: data.max || null, - start: data.min - } - ] - } - } - }) - } + onSubmit={code => { + onPostalCodeAssign(code); + setHavePostalCodesChanged(true); + }} open={params.action === "add-range"} /> - - unassignZipCodeRange({ - variables: { - id: params.id - } - }) - } - open={params.action === "remove-range"} - /> ); }; diff --git a/src/shipping/views/utils.tsx b/src/shipping/views/utils.tsx new file mode 100644 index 000000000..077c4dcb6 --- /dev/null +++ b/src/shipping/views/utils.tsx @@ -0,0 +1,7 @@ +const filterPostalCodes = (postalCodes, codeToFilterOut) => + postalCodes.filter( + rule => + rule.start !== codeToFilterOut.start && rule.end !== codeToFilterOut.end + ); + +export default filterPostalCodes; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index f0dac3fd8..5166159e0 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -17131,7 +17131,7 @@ exports[`Storyshots Shipping / ShippingZoneRatesCreatePage page create price 1`] > + + + @@ -208915,7 +208951,7 @@ exports[`Storyshots Views / Shipping / Shipping rate loading 1`] = ` >