diff --git a/cypress/elements/discounts/vouchers.js b/cypress/elements/discounts/vouchers.js index 78ed01084..11d0e3e40 100644 --- a/cypress/elements/discounts/vouchers.js +++ b/cypress/elements/discounts/vouchers.js @@ -6,5 +6,17 @@ export const VOUCHERS_SELECTORS = { "[name='discountType'][value='VALUE_PERCENTAGE']", fixedDiscountRadioButton: "[name='discountType'][value='VALUE_FIXED']", shippingDiscountRadioButton: "[name='discountType'][value='SHIPPING']", - discountValueInputs: "[name='value']" + discountValueInputs: "[name='value']", + limits: { + usageLimitCheckbox: '[data-test-id="has-usage-limit"]', + usageLimitTextField: '[data-test-id="usage-limit"]', + applyOncePerCustomerCheckbox: '[data-test-id="apply-once-per-customer"]', + onlyForStaffCheckbox: '[data-test-id="only-for-staff"]' + }, + requirements: { + minOrderValueCheckbox: '[name="requirementsPicker"][value="ORDER"]', + minAmountOfItemsCheckbox: '[name="requirementsPicker"][value="ITEM"]', + minCheckoutItemsQuantityInput: '[name="minCheckoutItemsQuantity"]', + minOrderValueInput: '[name="minSpent"]' + } }; diff --git a/cypress/integration/configuration/plugins/adyen.js b/cypress/integration/configuration/plugins/adyen.js index 394adf79e..f261fde75 100644 --- a/cypress/integration/configuration/plugins/adyen.js +++ b/cypress/integration/configuration/plugins/adyen.js @@ -164,8 +164,8 @@ filterTests({ definedTags: ["stagedOnly"] }, () => { simpleCard.brand = paymentCards.cards.simpleCard.brand; simpleCard.encryptedSecurityCode = paymentCards.encryptedSecurityCodes.unknown; - completeCheckout(checkout.id, simpleCard).then(({ checkoutErrors }) => { - expect(checkoutErrors).to.have.length(1); + completeCheckout(checkout.id, simpleCard).then(({ errors }) => { + expect(errors).to.have.length(1); }); }); @@ -174,8 +174,8 @@ filterTests({ definedTags: ["stagedOnly"] }, () => { errorCard.encryptedCardNumber = paymentCards.cards.errorCard.encryptedCardNumber; errorCard.brand = paymentCards.cards.errorCard.brand; - completeCheckout(checkout.id, errorCard).then(({ checkoutErrors }) => { - expect(checkoutErrors).to.have.length(1); + completeCheckout(checkout.id, errorCard).then(({ errors }) => { + expect(errors).to.have.length(1); }); }); @@ -184,8 +184,8 @@ filterTests({ definedTags: ["stagedOnly"] }, () => { closeAccount.encryptedCardNumber = paymentCards.cards.closeAccount.encryptedCardNumber; closeAccount.brand = paymentCards.cards.closeAccount.brand; - completeCheckout(checkout.id, closeAccount).then(({ checkoutErrors }) => { - expect(checkoutErrors).to.have.length(1); + completeCheckout(checkout.id, closeAccount).then(({ errors }) => { + expect(errors).to.have.length(1); }); }); }); diff --git a/cypress/integration/discounts/vouchers/createVouchers.js b/cypress/integration/discounts/vouchers/createVouchers.js new file mode 100644 index 000000000..a2d95e8f6 --- /dev/null +++ b/cypress/integration/discounts/vouchers/createVouchers.js @@ -0,0 +1,356 @@ +// / +// / + +import faker from "faker"; + +import { urlList } from "../../../fixtures/urlList"; +import { createChannel } from "../../../support/api/requests/Channels"; +import { completeCheckout } from "../../../support/api/requests/Checkout"; +import * as channelsUtils from "../../../support/api/utils/channelsUtils"; +import { deleteVouchersStartsWith } from "../../../support/api/utils/discounts/vouchersUtils"; +import { + addPayment, + createCheckoutWithVoucher +} from "../../../support/api/utils/ordersUtils"; +import * as productsUtils from "../../../support/api/utils/products/productsUtils"; +import filterTests from "../../../support/filterTests"; +import { + createVoucher, + discountOptions, + loginAndCreateCheckoutForVoucherWithDiscount +} from "../../../support/pages/discounts/vouchersPage"; + +filterTests({ definedTags: ["all"] }, () => { + describe("As an admin I want to create voucher", () => { + const startsWith = "CyVou-"; + const productPrice = 100; + const shippingPrice = 100; + + let defaultChannel; + let createdChannel; + let shippingMethod; + let variants; + let address; + let dataForCheckout; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + channelsUtils.deleteChannelsStartsWith(startsWith); + deleteVouchersStartsWith(startsWith); + const name = `${startsWith}${faker.datatype.number()}`; + productsUtils + .createProductWithShipping({ name, productPrice, shippingPrice }) + .then( + ({ + variantsList: variantsResp, + defaultChannel: channel, + shippingMethod: shippingMethodResp, + address: addressResp + }) => { + variants = variantsResp; + defaultChannel = channel; + shippingMethod = shippingMethodResp; + address = addressResp; + createChannel({ name }); + } + ) + .then(channel => { + createdChannel = channel; + dataForCheckout = { + channelSlug: defaultChannel.slug, + variantsList: variants, + address, + shippingMethodName: shippingMethod.name, + auth: "token" + }; + }); + }); + + it("should be able to create fixed price voucher. TC: SALEOR_1901", () => { + const voucherValue = 50; + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const expectedAmount = productPrice + shippingPrice - voucherValue; + let checkout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.FIXED, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout + }) + .then(({ addPromoCodeResp, checkout: checkoutResp }) => { + expect(addPromoCodeResp.checkout.totalPrice.gross.amount).to.be.eq( + expectedAmount + ); + dataForCheckout.voucherCode = voucherCode; + checkout = checkoutResp; + addPayment(checkout.id); + }) + .then(() => { + completeCheckout(checkout.id); + }) + .then(({ order }) => { + expect(order.id).to.be.ok; + }); + }); + + it("should be able to create percentage voucher. TC: SALEOR_1902", () => { + const voucherValue = 50; + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const expectedAmount = + (productPrice * voucherValue) / 100 + shippingPrice; + let checkout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout + }) + .then(({ addPromoCodeResp, checkout: checkoutResp }) => { + expect(addPromoCodeResp.checkout.totalPrice.gross.amount).to.be.eq( + expectedAmount + ); + dataForCheckout.voucherCode = voucherCode; + checkout = checkoutResp; + addPayment(checkout.id); + }) + .then(() => { + completeCheckout(checkout.id); + }) + .then(({ order }) => { + expect(order.id).to.be.ok; + }); + }); + + it("should be able to create free shipping voucher. TC: SALEOR_1903", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const expectedAmount = productPrice; + let checkout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.SHIPPING, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout + }) + .then(({ addPromoCodeResp, checkout: checkoutResp }) => { + expect(addPromoCodeResp.checkout.totalPrice.gross.amount).to.be.eq( + expectedAmount + ); + dataForCheckout.voucherCode = voucherCode; + checkout = checkoutResp; + addPayment(checkout.id); + }) + .then(() => { + completeCheckout(checkout.id); + }) + .then(({ order }) => { + expect(order.id).to.be.ok; + }); + }); + + it("should be able to create voucher not available for selected channel. TC: SALEOR_1904", () => { + const randomName = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + + cy.clearSessionData() + .loginUserViaRequest() + .visit(urlList.vouchers); + cy.softExpectSkeletonIsVisible(); + createVoucher({ + voucherCode: randomName, + voucherValue, + discountOption: discountOptions.PERCENTAGE, + channelName: createdChannel.name + }); + dataForCheckout.voucherCode = randomName; + createCheckoutWithVoucher(dataForCheckout).then( + ({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + expect(errorField).to.be.eq("promoCode"); + } + ); + }); + + it("should be able to create voucher with limited number of times discount can be used in total. TC: SALEOR_1907", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + const usageLimit = 1; + let firstCheckout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout, + usageLimit + }) + .then(({ checkout, addPromoCodeResp }) => { + expect(addPromoCodeResp.errors).to.be.empty; + firstCheckout = checkout; + dataForCheckout.voucherCode = voucherCode; + addPayment(firstCheckout.id); + }) + .then(() => { + completeCheckout(firstCheckout.id); + }) + .then(() => { + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + expect(errorField, "error in promo code should occur").to.be.eq( + "promoCode" + ); + }); + }); + + it("should be able to create voucher with limit to one use per customer. TC: SALEOR_1908", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + dataForCheckout.auth = "token"; + let firstCheckout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout, + applyOnePerCustomer: true + }) + .then(({ checkout, addPromoCodeResp }) => { + expect(addPromoCodeResp.errors).to.be.empty; + + dataForCheckout.voucherCode = voucherCode; + firstCheckout = checkout; + addPayment(firstCheckout.id); + }) + .then(() => { + completeCheckout(firstCheckout.id); + }) + .then(() => { + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + expect(errorField, "error in promo code should occur").to.be.eq( + "promoCode" + ); + + // Create new checkout as other not logged in customer - voucher should be available for other customer + + cy.clearSessionData(); + dataForCheckout.email = "newUser@example.com"; + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors; + expect(errorField, "No errors when adding promo code").to.be.empty; + }); + }); + + it("should be able to create voucher with limit to staff only. TC: SALEOR_1909", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + dataForCheckout.auth = "auth"; + let firstCheckout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout, + onlyStaff: true + }) + .then(({ checkout, addPromoCodeResp }) => { + expect(addPromoCodeResp.errors).to.be.empty; + dataForCheckout.voucherCode = voucherCode; + firstCheckout = checkout; + addPayment(firstCheckout.id); + }) + .then(() => { + completeCheckout(firstCheckout.id); + }) + .then(() => { + dataForCheckout.auth = "token"; + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + expect(errorField, "error in promo code should occur").to.be.eq( + "promoCode" + ); + }); + }); + + it("should be able to create voucher with minimum value of order. TC: SALEOR_1910", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + const minOrderValue = productPrice * 1.5; + dataForCheckout.productQuantity = 1; + let checkout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout, + minOrderValue + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + dataForCheckout.voucherCode = voucherCode; + + expect(errorField, "error in promo code should occur").to.be.eq( + "promoCode" + ); + dataForCheckout.productQuantity = 2; + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ checkout: checkoutResp, addPromoCodeResp }) => { + checkout = checkoutResp; + const errorField = addPromoCodeResp.errors; + expect(errorField, "No errors when adding promo code").to.be.empty; + }); + }); + + it("should create voucher with min product quantity. TC: SALEOR_1911", () => { + const voucherCode = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + const minAmountOfItems = 2; + dataForCheckout.productQuantity = 1; + let checkout; + + loginAndCreateCheckoutForVoucherWithDiscount({ + discount: discountOptions.PERCENTAGE, + voucherValue, + voucherCode, + channelName: defaultChannel.name, + dataForCheckout, + minAmountOfItems + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + dataForCheckout.voucherCode = voucherCode; + + expect(errorField, "error in promo code should occur").to.be.eq( + "promoCode" + ); + dataForCheckout.productQuantity = 2; + createCheckoutWithVoucher(dataForCheckout); + }) + .then(({ checkout: checkoutResp, addPromoCodeResp }) => { + checkout = checkoutResp; + const errorField = addPromoCodeResp.errors; + expect(errorField, "No errors when adding promo code").to.be.empty; + }); + }); + }); +}); diff --git a/cypress/integration/discounts/vouchers/vouchers.js b/cypress/integration/discounts/vouchers/vouchers.js new file mode 100644 index 000000000..18bfd52d0 --- /dev/null +++ b/cypress/integration/discounts/vouchers/vouchers.js @@ -0,0 +1,101 @@ +// / +// / + +import faker from "faker"; + +import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { voucherDetailsUrl } from "../../../fixtures/urlList"; +import { createChannel } from "../../../support/api/requests/Channels"; +import * as channelsUtils from "../../../support/api/utils/channelsUtils"; +import { + createVoucherInChannel, + deleteVouchersStartsWith +} from "../../../support/api/utils/discounts/vouchersUtils"; +import { createCheckoutWithVoucher } from "../../../support/api/utils/ordersUtils"; +import * as productsUtils from "../../../support/api/utils/products/productsUtils"; +import filterTests from "../../../support/filterTests"; + +filterTests({ definedTags: ["all"] }, () => { + describe("Vouchers discounts", () => { + const startsWith = "CyVou-"; + const productPrice = 100; + const shippingPrice = 100; + + let defaultChannel; + let createdChannel; + let shippingMethod; + let variants; + let product; + let address; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + channelsUtils.deleteChannelsStartsWith(startsWith); + deleteVouchersStartsWith(startsWith); + const name = `${startsWith}${faker.datatype.number()}`; + productsUtils + .createProductWithShipping({ name, productPrice, shippingPrice }) + .then( + ({ + variantsList: variantsResp, + defaultChannel: channel, + shippingMethod: shippingMethodResp, + address: addressResp, + product: productResp + }) => { + variants = variantsResp; + defaultChannel = channel; + shippingMethod = shippingMethodResp; + address = addressResp; + product = productResp; + createChannel({ name }); + } + ) + .then(channel => { + createdChannel = channel; + }); + }); + + it("should delete voucher", () => { + const name = `${startsWith}${faker.datatype.number()}`; + const voucherValue = 50; + + let voucher; + + cy.clearSessionData().loginUserViaRequest(); + createVoucherInChannel({ + name, + productId: product.id, + channelId: defaultChannel.id, + value: voucherValue + }) + .then(voucherResp => { + voucher = voucherResp; + expect(voucher.id).to.be.ok; + cy.visit(voucherDetailsUrl(voucher.id)) + .addAliasToGraphRequest("VoucherDelete") + .get(BUTTON_SELECTORS.deleteButton) + .click() + .get(BUTTON_SELECTORS.submit) + .click() + .wait("@VoucherDelete"); + createCheckoutForCreatedVoucher(voucher.code); + }) + .then(({ addPromoCodeResp }) => { + const errorField = addPromoCodeResp.errors[0].field; + expect(errorField).to.be.eq("promoCode"); + }); + }); + + function createCheckoutForCreatedVoucher(voucherCode) { + return createCheckoutWithVoucher({ + channelSlug: defaultChannel.slug, + variantsList: variants, + address, + shippingMethodName: shippingMethod.name, + voucherCode, + auth: "token" + }); + } + }); +}); diff --git a/cypress/support/api/requests/Checkout.js b/cypress/support/api/requests/Checkout.js index af0f4397e..598a77f7a 100644 --- a/cypress/support/api/requests/Checkout.js +++ b/cypress/support/api/requests/Checkout.js @@ -155,7 +155,7 @@ export function completeCheckout(checkoutId, paymentData) { } confirmationNeeded confirmationData - checkoutErrors{ + errors{ field message } @@ -169,7 +169,7 @@ export function addVoucher(checkoutId, voucherCode) { checkoutAddPromoCode(checkoutId:"${checkoutId}", promoCode:"${voucherCode}" ){ - checkoutErrors{ + errors{ field message } @@ -191,7 +191,7 @@ export function checkoutVariantsUpdate(checkoutId, variantsList) { const mutation = `mutation{ checkoutLinesUpdate(checkoutId:"${checkoutId}", lines: [${lines.join()}]){ - checkoutErrors{ + errors{ field message } @@ -203,7 +203,7 @@ export function checkoutVariantsUpdate(checkoutId, variantsList) { export function checkoutShippingMethodUpdate(checkoutId, shippingMethodId) { const mutation = `mutation{ checkoutShippingMethodUpdate(checkoutId:"${checkoutId}" shippingMethodId:"${shippingMethodId}"){ - checkoutErrors{ + errors{ field message } @@ -220,7 +220,7 @@ export function checkoutShippingAddressUpdate(checkoutId, address) { checkoutShippingAddressUpdate(checkoutId:"${checkoutId}", ${shippingAddress} ){ - checkoutErrors{ + errors{ field message } diff --git a/cypress/support/api/utils/ordersUtils.js b/cypress/support/api/utils/ordersUtils.js index 9618c6921..7531387d3 100644 --- a/cypress/support/api/utils/ordersUtils.js +++ b/cypress/support/api/utils/ordersUtils.js @@ -66,6 +66,7 @@ export function createCheckoutWithVoucher({ channelSlug, email = "email@example.com", variantsList, + productQuantity = 1, address, shippingMethodName, voucherCode, @@ -75,6 +76,7 @@ export function createCheckoutWithVoucher({ return checkoutRequest .createCheckout({ channelSlug, + productQuantity, email, variantsList, address, diff --git a/cypress/support/pages/discounts/vouchersPage.js b/cypress/support/pages/discounts/vouchersPage.js index 8afb1e4f4..b834d0353 100644 --- a/cypress/support/pages/discounts/vouchersPage.js +++ b/cypress/support/pages/discounts/vouchersPage.js @@ -1,5 +1,8 @@ import { VOUCHERS_SELECTORS } from "../../../elements/discounts/vouchers"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { urlList } from "../../../fixtures/urlList"; +import { ONE_PERMISSION_USERS } from "../../../fixtures/users"; +import { createCheckoutWithVoucher } from "../../api/utils/ordersUtils"; import { selectChannelInDetailsPages } from "../channelsPage"; export const discountOptions = { @@ -12,7 +15,12 @@ export function createVoucher({ voucherCode, voucherValue, discountOption, - channelName + channelName, + usageLimit, + applyOnePerCustomer, + onlyStaff, + minOrderValue, + minAmountOfItems }) { cy.get(VOUCHERS_SELECTORS.createVoucherButton).click(); selectChannelInDetailsPages(channelName); @@ -23,7 +31,61 @@ export function createVoucher({ if (discountOption !== discountOptions.SHIPPING) { cy.get(VOUCHERS_SELECTORS.discountValueInputs).type(voucherValue); } + if (usageLimit) { + cy.get(VOUCHERS_SELECTORS.limits.usageLimitCheckbox) + .click() + .type(usageLimit); + } + if (applyOnePerCustomer) { + cy.get(VOUCHERS_SELECTORS.limits.applyOncePerCustomerCheckbox).click(); + } + if (onlyStaff) { + cy.get(VOUCHERS_SELECTORS.limits.onlyForStaffCheckbox).click(); + } + if (minOrderValue) { + cy.get(VOUCHERS_SELECTORS.requirements.minOrderValueCheckbox) + .click() + .get(VOUCHERS_SELECTORS.requirements.minOrderValueInput) + .type(minOrderValue); + } + if (minAmountOfItems) { + cy.get(VOUCHERS_SELECTORS.requirements.minAmountOfItemsCheckbox) + .click() + .get(VOUCHERS_SELECTORS.requirements.minCheckoutItemsQuantityInput) + .type(minAmountOfItems); + } cy.get(BUTTON_SELECTORS.confirm) .click() .confirmationMessageShouldDisappear(); } + +export function loginAndCreateCheckoutForVoucherWithDiscount({ + discount, + voucherValue, + voucherCode, + channelName, + dataForCheckout, + usageLimit, + applyOnePerCustomer, + onlyStaff, + minOrderValue, + minAmountOfItems +}) { + cy.clearSessionData() + .loginUserViaRequest("auth", ONE_PERMISSION_USERS.discount) + .visit(urlList.vouchers); + cy.softExpectSkeletonIsVisible(); + createVoucher({ + voucherCode, + voucherValue, + discountOption: discount, + channelName, + usageLimit, + applyOnePerCustomer, + onlyStaff, + minOrderValue, + minAmountOfItems + }); + dataForCheckout.voucherCode = voucherCode; + return createCheckoutWithVoucher(dataForCheckout); +} diff --git a/src/components/ControlledCheckbox.tsx b/src/components/ControlledCheckbox.tsx index 9e40c468a..dca08c152 100644 --- a/src/components/ControlledCheckbox.tsx +++ b/src/components/ControlledCheckbox.tsx @@ -9,6 +9,7 @@ export interface ControlledCheckboxProps { indeterminate?: boolean; disabled?: boolean; checkedIcon?: React.ReactNode; + testId?: string; onChange(event: any); } @@ -20,12 +21,14 @@ export const ControlledCheckbox: React.FC = ({ onChange, checkedIcon, indeterminate, + testId, ...props }) => ( ))}