diff --git a/.changeset/lemon-beds-raise.md b/.changeset/lemon-beds-raise.md new file mode 100644 index 000000000..9edaf5e28 --- /dev/null +++ b/.changeset/lemon-beds-raise.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": minor +--- + +Add order exipration TTL field to channel settings diff --git a/cypress/e2e/configuration/channels/channels.js b/cypress/e2e/configuration/channels/channels.js index 4e663e924..8d226fcd7 100644 --- a/cypress/e2e/configuration/channels/channels.js +++ b/cypress/e2e/configuration/channels/channels.js @@ -8,6 +8,7 @@ import { CHANNELS_SELECTORS } from "../../../elements/channels/channels-selector import { SELECT_CHANNELS_TO_ASSIGN } from "../../../elements/channels/select-channels-to-assign"; import { HEADER_SELECTORS } from "../../../elements/header/header-selectors"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { MESSAGES } from "../../../fixtures/"; import { productDetailsUrl, urlList } from "../../../fixtures/urlList"; import { ONE_PERMISSION_USERS } from "../../../fixtures/users"; import { createChannel } from "../../../support/api/requests/Channels"; @@ -17,7 +18,11 @@ import { getShippingZone, } from "../../../support/api/requests/ShippingMethod"; import { createWarehouse as createWarehouseViaApi } from "../../../support/api/requests/Warehouse"; -import { createChannelByView } from "../../../support/pages/channelsPage"; +import { + createChannelByView, + setChannelRequiredFields, + typeExpirationDays, +} from "../../../support/pages/channelsPage"; describe("Channels", () => { const channelStartsWith = `CyChannels`; @@ -91,6 +96,65 @@ describe("Channels", () => { }); }, ); + it( + "should create new channel with expired orders functionality set. TC: SALEOR_0713", + { tags: ["@channel", "@allEnv", "@stable"] }, + () => { + const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; + const orderExpiresAfter = 120; + cy.addAliasToGraphRequest("Channels"); + cy.addAliasToGraphRequest("ChannelCreate"); + cy.visit(urlList.channels); + cy.waitForRequestAndCheckIfNoErrors("@Channels"); + setChannelRequiredFields({ name: randomChannel, currency }); + typeExpirationDays(orderExpiresAfter); + cy.clickConfirmButton(); + cy.waitForRequestAndCheckIfNoErrors("@ChannelCreate").then( + channelCreate => { + expect( + channelCreate.response.body.data.channelCreate.channel + .orderSettings, + ).to.have.property("deleteExpiredOrdersAfter", orderExpiresAfter); + cy.get(CHANNELS_SELECTORS.orderExpirationInput) + .invoke("val") + .should("contain", orderExpiresAfter.toString()); + }, + ); + }, + ); + it( + "should not be able to create new channel with expired orders functionality with values outside boundary conditions. TC: SALEOR_0714", + { tags: ["@channel", "@allEnv"] }, + () => { + const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; + const underBoundaryConditions = 0; + const overBoundaryConditions = 121; + cy.addAliasToGraphRequest("Channels"); + cy.addAliasToGraphRequest("ChannelCreate"); + cy.visit(urlList.channels); + cy.waitForRequestAndCheckIfNoErrors("@Channels"); + setChannelRequiredFields({ name: randomChannel, currency }); + typeExpirationDays(underBoundaryConditions); + + cy.clickConfirmButton(); + cy.wait("@ChannelCreate").then(createChannelResponse => { + cy.log(createChannelResponse); + expect( + createChannelResponse.response.body.data.channelCreate.errors.length, + ).to.eq(1); + cy.confirmationErrorMessageShouldAppear(); + }); + typeExpirationDays(overBoundaryConditions); + cy.clickConfirmButton(); + cy.wait("@ChannelCreate").then(createChannelResponse => { + cy.log(createChannelResponse); + expect( + createChannelResponse.response.body.data.channelCreate.errors.length, + ).to.eq(1); + cy.confirmationErrorMessageShouldAppear(); + }); + }, + ); it( "should create channel with shippingZone and warehouse TC: SALEOR_0712", { tags: ["@channel", "@allEnv"] }, @@ -101,7 +165,6 @@ describe("Channels", () => { const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; cy.addAliasToGraphRequest("Channels"); cy.visit(urlList.channels); - cy.expectSkeletonIsVisible(); cy.wait("@Channels"); createChannelByView({ name: randomChannel, @@ -120,7 +183,7 @@ describe("Channels", () => { ); it( - "should validate slug name. TC: SALEOR_0703", + "should validate that creating channels with same slug name as other is not possible. TC: SALEOR_0703", { tags: ["@channel", "@allEnv", "@stable"] }, () => { const randomChannel = `${channelStartsWith} ${faker.datatype.number()}`; @@ -133,8 +196,10 @@ describe("Channels", () => { cy.visit(urlList.channels); cy.expectSkeletonIsVisible(); createChannelByView({ name: randomChannel, currency }); - cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should( - "be.visible", + cy.confirmationErrorMessageShouldAppear(); + cy.get(ADD_CHANNEL_FORM_SELECTORS.generalInformationSection).should( + "contain.text", + MESSAGES.slugMustBeUnique, ); }, ); diff --git a/cypress/elements/channels/add-channel-form-selectors.js b/cypress/elements/channels/add-channel-form-selectors.js index d25f8b2af..233b5cd0a 100644 --- a/cypress/elements/channels/add-channel-form-selectors.js +++ b/cypress/elements/channels/add-channel-form-selectors.js @@ -15,4 +15,5 @@ export const ADD_CHANNEL_FORM_SELECTORS = { warehouseAutocompleteSelect: "[data-test-id='warehouse-auto-complete-select']", countryAutocompleteInput: '[data-test-id="country-select-input"]', + generalInformationSection: '[data-test-id="general-information"]', }; diff --git a/cypress/elements/channels/channels-selectors.js b/cypress/elements/channels/channels-selectors.js index a055a125d..c249fdf18 100644 --- a/cypress/elements/channels/channels-selectors.js +++ b/cypress/elements/channels/channels-selectors.js @@ -1,5 +1,6 @@ export const CHANNELS_SELECTORS = { createChannelButton: "[data-test-id='add-channel']", channelsTable: "[class='MuiTableBody-root']", - channelName: "[data-test-id='name']" + channelName: "[data-test-id='name']", + orderExpirationInput: "[data-test-id='delete-expired-order-input']", }; diff --git a/cypress/fixtures/messages.js b/cypress/fixtures/messages.js index 2bc3b3f0f..f3c886412 100644 --- a/cypress/fixtures/messages.js +++ b/cypress/fixtures/messages.js @@ -2,4 +2,5 @@ export const MESSAGES = { noProductFound: "No products found", confirmProductsDeletion: "Are you sure you want to delete 2 products?", invalidEmailAddress: "Enter a valid email address.", + slugMustBeUnique: "Slug must be unique", }; diff --git a/cypress/support/pages/channelsPage.js b/cypress/support/pages/channelsPage.js index 768cc54c0..ae168c455 100644 --- a/cypress/support/pages/channelsPage.js +++ b/cypress/support/pages/channelsPage.js @@ -16,13 +16,32 @@ export function createChannelByView({ defaultCountry = "Poland", warehouse, }) { - cy.addAliasToGraphRequest("Channel") - .get(CHANNELS_SELECTORS.createChannelButton) - .click() - .get(ADD_CHANNEL_FORM_SELECTORS.channelName) - .type(name) + setChannelRequiredFields({ + name, + currency, + slug, + shippingZone, + defaultCountry, + warehouse, + }); + cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click(); +} +export function setChannelRequiredFields({ + name, + currency, + slug = name, + shippingZone, + defaultCountry = "Poland", + warehouse, +}) { + cy.addAliasToGraphRequest("Channel"); + clickCreateChannelButton(); + cy.get(ADD_CHANNEL_FORM_SELECTORS.channelName) + .click({ force: true }) + .type(name, { force: true }) .get(ADD_CHANNEL_FORM_SELECTORS.slug) - .type(slug) + .click({ force: true }) + .type(slug, { force: true }) .get(ADD_CHANNEL_FORM_SELECTORS.currency) .click(); cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency); @@ -43,7 +62,6 @@ export function createChannelByView({ if (warehouse) { addWarehouse(warehouse); } - cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click(); } export function addShippingZone(shippingZone) { @@ -57,6 +75,16 @@ export function addShippingZone(shippingZone) { shippingZone, ); } +export function typeExpirationDays(expirationDays) { + cy.get(CHANNELS_SELECTORS.orderExpirationInput) + .click({ force: true }) + .clear() + .type(expirationDays); +} + +export function clickCreateChannelButton() { + return cy.get(CHANNELS_SELECTORS.createChannelButton).click(); +} export function addWarehouse(warehouse) { cy.get(BUTTON_SELECTORS.expandIcon) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 7bc126071..e35ce582e 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -4411,6 +4411,10 @@ "context": "page header, edit view", "string": "Edit grant refund" }, + "U+79k0": { + "context": "order expiration card description", + "string": "The time in days after expired orders will be deleted. Allowed range between 1 and 120." + }, "U1eJIw": { "context": "order history message", "string": "Products were added to an order" @@ -5269,10 +5273,6 @@ "context": "header", "string": "Password" }, - "ZhaXLU": { - "context": "button", - "string": "Copy" - }, "ZhqH8J": { "context": "button", "string": "Copy headers" @@ -6659,6 +6659,10 @@ "kTr2o8": { "string": "Attribute name" }, + "kVKTwC": { + "context": "order expiration card title", + "string": "Order expiration" + }, "kVOslW": { "context": "reason for discount label", "string": "Reason for discount" diff --git a/src/channels/components/ChannelForm/ChannelForm.tsx b/src/channels/components/ChannelForm/ChannelForm.tsx index 45db8d0c6..1b61e4d0d 100644 --- a/src/channels/components/ChannelForm/ChannelForm.tsx +++ b/src/channels/components/ChannelForm/ChannelForm.tsx @@ -3,8 +3,7 @@ import { ChannelShippingZones, ChannelWarehouses, } from "@dashboard/channels/pages/ChannelDetailsPage/types"; -import CardSpacer from "@dashboard/components/CardSpacer"; -import CardTitle from "@dashboard/components/CardTitle"; +import { DashboardCard } from "@dashboard/components/Card"; import ControlledSwitch from "@dashboard/components/ControlledSwitch"; import FormSpacer from "@dashboard/components/FormSpacer"; import Link from "@dashboard/components/Link"; @@ -23,18 +22,11 @@ import { ChangeEvent, FormChange } from "@dashboard/hooks/useForm"; import { commonMessages } from "@dashboard/intl"; import { getFormErrors } from "@dashboard/utils/errors"; import getChannelsErrorMessage from "@dashboard/utils/errors/channels"; -import { - Card, - CardContent, - InputAdornment, - TextField, - Typography, -} from "@material-ui/core"; -import { Box } from "@saleor/macaw-ui/next"; +import { Box, Button, CopyIcon, Input, Text } from "@saleor/macaw-ui/next"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { useStyles } from "../styles"; +import { messages } from "./messages"; import { ExtendedFormHelperTextProps } from "./types"; export interface FormData extends StockSettingsInput { @@ -49,6 +41,7 @@ export interface FormData extends StockSettingsInput { warehousesToDisplay: ChannelWarehouses; defaultCountry: CountryCode; markAsPaidStrategy: MarkAsPaidStrategyEnum; + deleteExpiredOrdersAfter: number; } export interface ChannelFormProps { @@ -79,91 +72,62 @@ export const ChannelForm: React.FC = ({ onMarkAsPaidStrategyChange, }) => { const intl = useIntl(); - const [copied, copy] = useClipboard(); + const [, copy] = useClipboard(); const formErrors = getFormErrors( - ["name", "slug", "currencyCode", "defaultCountry"], + [ + "name", + "slug", + "currencyCode", + "defaultCountry", + "deleteExpiredOrdersAfter", + ], errors, ); - const classes = useStyles(); return ( <> - - - - + + {intl.formatMessage(commonMessages.generalInformations)} + + + - copy(data.slug)} - > - {copied ? ( - - ) : ( - - )} - - ), - }} + endAdornment={ +