Merge branch 'master' of github.com:mirumee/saleor-dashboard into feature/unconfirmed-order-line-manipulation

This commit is contained in:
Tomasz Szymanski 2021-03-16 11:11:47 +01:00
commit 17e01508a6
201 changed files with 15061 additions and 32038 deletions

View file

@ -21,8 +21,15 @@
"default": "array-simple"
}
],
"@typescript-eslint/ban-types": "error",
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/ban-types": [
"error",
{
"extendDefaults": true,
"types": {
"{}": false
}
}
],
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/explicit-member-accessibility": "off",
@ -128,7 +135,7 @@
"no-multiple-empty-lines": "off",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-redeclare": "error",
"no-redeclare": "off",
"no-return-await": "error",
"no-sequences": "error",
"no-shadow": [
@ -155,7 +162,7 @@
"radix": "error",
"simple-import-sort/sort": ["error"],
"sort-imports": "off", // imports are handled by simple-import-sort/sort
"sort-keys": "warn",
"sort-keys": "off",
"space-before-function-paren": "off",
"spaced-comment": "error",
"use-isnan": "error",

View file

@ -19,9 +19,12 @@ All notable, unreleased changes to this project will be documented in this file.
- Drop descriptionJson and contentJson fields - #950 by @jwm0
- Add error tracking with Sentry adapter - #956 by @jwm0
- Add OAuth2 login with OpenID support - #963 by @orzechdev
- Add order and order line discounts and modal - #978 by @mmarkusik
- Fix no channels crash - #984 by @dominik-zeglen
- Update webhooks - #982 by @piotrgrundas
- Fix trigger form change when collections are being added to list of product collections - #987 by @gax97
- Add product variant webhooks - #1006 by @piotrgrundas
- Use default sort for search products list - #997 by @orzechdev
- Unconfirmed order manipulation - #967 by @tomaszszymanski129
# 2.11.1

View file

@ -1,51 +1,52 @@
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)
.then(resp => resp.body.data.attributes.edges);
}
deleteAttribute(attributeId) {
const mutation = `mutation{
attributeDelete(id:"${attributeId}"){
attributeErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function createAttribute(name, attributeValues = ["value"]) {
const values = attributeValues.map(element => `{name:"${element}"}`);
const mutation = `mutation{
attributeCreate(input:{
name:"${name}"
valueRequired:false
type:PRODUCT_TYPE
values: [${values}]
}){
attribute{
id
name
values{name}
}
attributeErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function getAttributes(first, search) {
const mutation = `query{
attributes(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(mutation)
.then(resp => resp.body.data.attributes.edges);
}
export function deleteAttribute(attributeId) {
const mutation = `mutation{
attributeDelete(id:"${attributeId}"){
attributeErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Attribute;

View file

@ -1,45 +1,42 @@
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)
.then(resp => resp.body.data.categories.edges);
}
deleteCategory(categoryId) {
const mutation = `mutation{
categoryDelete(id:"${categoryId}"){
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function createCategory(name, slug = name) {
const mutation = `mutation{
categoryCreate(input:{name:"${name}", slug: "${slug}"}){
productErrors{
field
message
}
category{
id
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function getCategories(first, search) {
const mutation = `query{
categories(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(mutation)
.then(resp => resp.body.data.categories.edges);
}
export function deleteCategory(categoryId) {
const mutation = `mutation{
categoryDelete(id:"${categoryId}"){
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Category;

View file

@ -1,53 +1,49 @@
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);
}
getChannels() {
const getChannelsInfoQuery = `query{
channels{
name
export function createChannel(isActive, name, slug, currencyCode) {
const createChannelMutation = `mutation{
channelCreate(input: {
isActive: ${isActive}
name: "${name}"
slug: "${slug}"
currencyCode: "${currencyCode}"
}){
channel{
id
isActive
name
slug
currencyCode
}
channelErrors{
code
message
}
}
`;
return cy.sendRequestWithQuery(getChannelsInfoQuery);
}
deleteChannel(channelId, targetChannelId) {
const deleteChannelMutation = `mutation{
channelDelete(id: "${channelId}", input:{
targetChannel: "${targetChannelId}"
}){
channel{
name
}
channelErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(deleteChannelMutation);
}
}`;
return cy.sendRequestWithQuery(createChannelMutation);
}
export function getChannels() {
const getChannelsInfoQuery = `query{
channels{
name
id
isActive
slug
currencyCode
}
}`;
return cy.sendRequestWithQuery(getChannelsInfoQuery);
}
export function deleteChannel(channelId, targetChannelId) {
const deleteChannelMutation = `mutation{
channelDelete(id: "${channelId}", input:{
targetChannel: "${targetChannelId}"
}){
channel{
name
}
channelErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(deleteChannelMutation);
}
export default Channels;

View file

@ -1,69 +1,71 @@
class Checkout {
createCheckout(channelSlug, email, productQuantity, variantsList) {
const lines = variantsList.map(
variant => `{quantity:${productQuantity}
export function createCheckout(
channelSlug,
email,
productQuantity,
variantsList
) {
const lines = variantsList.map(
variant => `{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);
}
completeCheckout(checkoutId) {
const mutation = `mutation{
checkoutComplete(checkoutId:"${checkoutId}"){
order{
id
}
confirmationNeeded
confirmationData
checkoutErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
);
const mutation = `mutation{
checkoutCreate(input:{
channel:"${channelSlug}"
email:"${email}"
lines: [${lines.join()}]
}){
checkoutErrors{
field
message
}
created
checkout{
id
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function addShippingMethod(checkoutId, shippingMethodId) {
const mutation = `mutation{
checkoutShippingMethodUpdate(checkoutId:"${checkoutId}",
shippingMethodId:"${shippingMethodId}"){
checkoutErrors{
message
field
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function addPayment(checkoutId, gateway, token) {
const mutation = `mutation{
checkoutPaymentCreate(checkoutId:"${checkoutId}",
input:{
gateway: "${gateway}"
token:"${token}"
}){
paymentErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function completeCheckout(checkoutId) {
const mutation = `mutation{
checkoutComplete(checkoutId:"${checkoutId}"){
order{
id
}
confirmationNeeded
confirmationData
checkoutErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Checkout;

View file

@ -0,0 +1,34 @@
export function getCollections(search) {
const filter = search
? `, filter:{
search:""
}`
: "";
const query = `query{
collections(first:100 ${filter}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.collections.edges);
}
export function deleteCollection(collectionId) {
const mutation = `mutation{
collectionDelete(id:"${collectionId}"){
collection{
id
}
collectionErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}

View file

@ -1,85 +1,80 @@
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
email
}
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);
}
});
export function 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}"
}
});
}
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);
}
defaultShippingAddress: {
companyName: "${address.companyName}"
streetAddress1: "${address.streetAddress1}"
streetAddress2: "${address.streetAddress2}"
city: "${address.city}"
postalCode: "${address.postalCode}"
country: ${address.country}
phone: "${address.phone}"
}
}){
user{
id
email
}
accountErrors{
code
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function deleteCustomersStartsWith(startsWith) {
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)) {
deleteCustomer(element.node.id);
}
});
}
});
}
export function deleteCustomer(customerId) {
const mutation = `mutation{
customerDelete(id:"${customerId}"){
accountErrors{
code
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function getCustomers(startsWith) {
const query = `query{
customers(first:100, filter: {
search: "${startsWith}"
}){
edges{
node{
id
email
}
}
}
}`;
return cy.sendRequestWithQuery(query);
}
export default Customer;

View file

@ -1,37 +1,34 @@
class HomePage {
getSalesForChannel(channelSlug, period) {
const query = `query{
ordersTotal(period: ${period}, channel:"${channelSlug}"){
gross{
amount
}
}
}`;
return cy.sendRequestWithQuery(query);
}
getOrdersForChannel(channelSlug, created) {
const query = `query{
orders(created: ${created}, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
getOrdersWithStatus(status, channelSlug) {
const query = `query{
orders(status: ${status}, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
getProductsOutOfStock(channelSlug) {
const query = `query{
products(stockAvailability: OUT_OF_STOCK, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
export function getSalesForChannel(channelSlug, period) {
const query = `query{
ordersTotal(period: ${period}, channel:"${channelSlug}"){
gross{
amount
}
}
}`;
return cy.sendRequestWithQuery(query);
}
export function getOrdersForChannel(channelSlug, created) {
const query = `query{
orders(created: ${created}, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
export function getOrdersWithStatus(status, channelSlug) {
const query = `query{
orders(status: ${status}, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
export function getProductsOutOfStock(channelSlug) {
const query = `query{
products(stockAvailability: OUT_OF_STOCK, channel:"${channelSlug}"){
totalCount
}
}`;
return cy.sendRequestWithQuery(query);
}
export default HomePage;

View file

@ -1,60 +1,55 @@
class Order {
markOrderAsPaid(orderId) {
const mutation = `mutation{
orderMarkAsPaid(id:"${orderId}"){
orderErrors{
message
}
}
}`;
return 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 function markOrderAsPaid(orderId) {
const mutation = `mutation{
orderMarkAsPaid(id:"${orderId}"){
orderErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function addProductToOrder(orderId, variantId, quantity = 1) {
const mutation = `mutation{
draftOrderLinesCreate(id:"${orderId}", input:{
quantity:${quantity}
variantId: "${variantId}"
}){
orderErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function createDraftOrder(customerId, shippingMethodId, channelId) {
const mutation = `mutation{
draftOrderCreate(input:{
user:"${customerId}"
shippingMethod:"${shippingMethodId}"
channel: "${channelId}"
}){
orderErrors{
message
}
order{
id
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function completeOrder(orderId) {
const mutation = `mutation{
draftOrderComplete(id:"${orderId}"){
order{
id
}
orderErrors{
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Order;

View file

@ -1,200 +1,196 @@
import Utils from "./utils/Utils";
import { getValueWithDefault } from "./utils/Utils";
class Product {
utils = new Utils();
getFirstProducts(first, search) {
const filter = search
? `, filter:{
export function getFirstProducts(first, search) {
const filter = search
? `, filter:{
search:"${search}"
}`
: "";
const query = `query{
products(first:${first}${filter}){
edges{
node{
id
name
variants{
id
}
}
}
}
`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.products.edges);
}
updateChannelInProduct({
productId,
channelId,
isPublished = true,
isAvailableForPurchase = true,
visibleInListings = true
}) {
const mutation = `mutation{
productChannelListingUpdate(id:"${productId}",
input:{
addChannels:{
channelId:"${channelId}"
isPublished:${isPublished}
isAvailableForPurchase:${isAvailableForPurchase}
visibleInListings:${visibleInListings}
}
}){
product{
id
name
}
}
}`;
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{
: "";
const query = `query{
products(first:${first}${filter}){
edges{
node{
id
}
productErrors{
field
message
name
variants{
id
}
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.products.edges);
}
createVariant(
productId,
sku,
warehouseId,
quantity,
export function updateChannelInProduct({
productId,
channelId,
isPublished = true,
isAvailableForPurchase = true,
visibleInListings = true
}) {
const mutation = `mutation{
productChannelListingUpdate(id:"${productId}",
input:{
addChannels:{
channelId:"${channelId}"
isPublished:${isPublished}
isAvailableForPurchase:${isAvailableForPurchase}
visibleInListings:${visibleInListings}
}
}){
product{
id
name
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function updateChannelPriceInVariant(variantId, channelId) {
const mutation = `mutation{
productVariantChannelListingUpdate(id: "${variantId}", input: {
channelId: "${channelId}"
price: 10
costPrice: 10
}){
productChannelListingErrors{
message
}
}
} `;
return cy.sendRequestWithQuery(mutation);
}
export function createProduct(attributeId, name, productType, category) {
const mutation = `mutation{
productCreate(input:{
attributes:[{
id:"${attributeId}"
}]
name:"${name}"
productType:"${productType}"
category:"${category}"
}){
product{
id
name
}
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function createVariant({
productId,
sku,
warehouseId,
quantity,
channelId,
price = 1,
costPrice = 1
}) {
const channelListings = getValueWithDefault(
channelId,
price = 1,
costPrice = 1
) {
const channelListings = this.utils.getValueWithDefault(
channelId,
`channelListings:{
`channelListings:{
channelId:"${channelId}"
price:"${price}"
costPrice:"${costPrice}"
}`
);
);
const stocks = this.utils.getValueWithDefault(
warehouseId,
`stocks:{
const stocks = getValueWithDefault(
warehouseId,
`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
}
}
const mutation = `mutation{
productVariantBulkCreate(product: "${productId}", variants: {
attributes: []
sku: "${sku}"
${channelListings}
${stocks}
}) {
productVariants{
id
name
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.productTypes.edges);
}
deleteProductType(productTypeId) {
const mutation = `mutation{
productTypeDelete(id:"${productTypeId}"){
productErrors{
field
message
}
bulkProductErrors{
field
message
}
}`;
return cy.sendRequestWithQuery(mutation);
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Product;
export function createTypeProduct(name, attributeId, slug = name) {
const mutation = `mutation{
productTypeCreate(input: {
name: "${name}"
slug: "${slug}"
isShippingRequired: true
productAttributes: "${attributeId}"
variantAttributes: "${attributeId}"
}){
productErrors{
field
message
}
productType{
id
}
}
} `;
return cy.sendRequestWithQuery(mutation);
}
export function deleteProduct(productId) {
const mutation = `mutation{
productDelete(id: "${productId}"){
productErrors{
field
message
}
}
} `;
return cy.sendRequestWithQuery(mutation);
}
export function getProductTypes(first, search) {
const query = `query{
productTypes(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.productTypes.edges);
}
export function deleteProductType(productTypeId) {
const mutation = `mutation{
productTypeDelete(id:"${productTypeId}"){
productErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}

View file

@ -0,0 +1,35 @@
import { getValueWithDefault } from "./utils/Utils";
export function getSales(first, searchQuery) {
const filter = getValueWithDefault(
searchQuery,
`, filter:{
search:"${searchQuery}"
}`
);
const query = `query{
sales(first:
${first} ${filter}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.sales.edges);
}
export function deleteSale(saleId) {
const mutation = `mutation{
saleDelete(id:"${saleId}"){
discountErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}

View file

@ -1,61 +1,54 @@
class ShippingMethod {
createShippingRate(name, shippingZone) {
const mutation = `
mutation{
shippingPriceCreate(input:{
name: "${name}"
shippingZone: "${shippingZone}"
type: PRICE
}){
shippingMethod{
id
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
export function 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);
}
export function 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);
}
export function 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{
export function deleteShippingZone(shippingZoneId) {
const mutation = `mutation{
shippingZoneDelete(id:"${shippingZoneId}"){
shippingErrors{
message
@ -63,11 +56,11 @@ class ShippingMethod {
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
return cy.sendRequestWithQuery(mutation);
}
getShippingZones() {
const query = `query{
export function getShippingZones() {
const query = `query{
shippingZones(first:100){
edges{
node{
@ -78,9 +71,7 @@ class ShippingMethod {
}
}
`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.shippingZones.edges);
}
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.shippingZones.edges);
}
export default ShippingMethod;

View file

@ -1,57 +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)
.then(resp => resp.body.data.warehouses.edges);
}
deleteWarehouse(warehouseId) {
const mutation = `mutation{
deleteWarehouse(id:"${warehouseId}"){
warehouseErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function 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
name
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export function getWarehouses(first, search) {
const query = `query{
warehouses(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.warehouses.edges);
}
export function deleteWarehouse(warehouseId) {
const mutation = `mutation{
deleteWarehouse(id:"${warehouseId}"){
warehouseErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
export default Warehouse;

View file

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

View file

@ -1,18 +1,37 @@
class ProductDetails {
getProductDetails(productId, channelId) {
const query = `fragment BasicProductFields on Product {
id
name
}
query ProductDetails{
product(id: "${productId}", channel: "${channelId}") {
...BasicProductFields
isAvailable
isAvailableForPurchase
availableForPurchase
}
}`;
return cy.sendRequestWithQuery(query, "token");
export function getProductDetails(productId, channelId) {
const query = `fragment BasicProductFields on Product {
id
name
}
fragment Price on TaxedMoney {
gross {
amount
currency
}
}
fragment ProductVariantFields on ProductVariant {
id
sku
name
pricing {
price {
...Price
}
}
}
query ProductDetails{
product(id: "${productId}", channel: "${channelId}") {
...BasicProductFields
variants {
...ProductVariantFields
}
isAvailable
isAvailableForPurchase
availableForPurchase
}
}`;
return cy.sendRequestWithQuery(query, "token");
}
export default ProductDetails;

View file

@ -1,20 +1,17 @@
class Search {
searchInShop(searchQuery) {
const query = `query SearchProducts {
products(channel: "default-channel", filter:{
search: "${searchQuery}"
}, first:10){
totalCount
edges{
node{
id
name
}
}
export function searchInShop(searchQuery) {
const query = `query SearchProducts {
products(channel: "default-channel", filter:{
search: "${searchQuery}"
},
first:10){
totalCount
edges{
node{
id
name
}
}`;
return cy.sendRequestWithQuery(query, "token");
}
}
}
}`;
return cy.sendRequestWithQuery(query, "token");
}
export default Search;

View file

@ -1,6 +1,2 @@
class Utils {
getValueWithDefault(condition, value, defaultValue = "") {
return condition ? value : defaultValue;
}
}
export default Utils;
export const getValueWithDefault = (condition, value, defaultValue = "") =>
condition ? value : defaultValue;

View file

@ -0,0 +1,4 @@
export const ASSIGN_PRODUCTS_SELECTORS = {
searchInput: "[name='query']",
tableRow: "[data-test-id='assign-product-table-row']"
};

View file

@ -0,0 +1,6 @@
export const COLLECTION_SELECTORS = {
createCollectionButton: "[data-test-id = 'create-collection']",
nameInput: "[name='name']",
saveButton: "[data-test='button-bar-confirm']",
addProductButton: "[data-test-id='add-product']"
};

View file

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

View file

@ -1,4 +1,3 @@
/* eslint-disable sort-keys */
export const PRODUCTS_SELECTORS = {
productsList: "[data-test-id][data-test='id']",
products: "[data-test='submenu-item-label'][data-test-id='products']",
@ -23,6 +22,8 @@ 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]",
addVariantsButton: "[data-test*='button-add-variant']",
publishedRadioButtons: "[name*='isPublished']",
availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
radioButtonsValueTrue: "[value='true']",

View file

@ -0,0 +1,15 @@
export const VARIANTS_SELECTORS = {
attributeCheckbox: "[name*='value:']",
valueContainer: "[data-test-id='value-container']",
nextButton: "[class*='MuiButton-containedPrimary']",
priceInput: "[name*='channel-price']",
costPriceInput: "[name*='channel-costPrice']",
warehouseCheckboxes: "[name*='warehouse:']",
skuInput: "input[class*='MuiInputBase'][type='text']",
attributeSelector: "[data-test='attribute-value']",
attributeOption: "[data-test-type='option']",
addWarehouseButton: "button[class*='MuiIconButton-colorPrimary']",
warehouseOption: "[role='menuitem']",
saveButton: "[data-test='button-bar-confirm']",
skuInputInAddVariant: "[name='sku']"
};

View file

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

View file

@ -0,0 +1,16 @@
export const MENAGE_CHANNEL_AVAILABILITY = {
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']",
assignedChannels: "[data-test='channel-availability-item']",
publishedRadioButtons: "[name*='isPublished']",
availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
radioButtonsValueTrue: "[value='true']",
radioButtonsValueFalse: "[value='false']",
visibleInListingsButton: "[name*='visibleInListings']",
allChannelsInput: "[name='allChannels']"
};

View file

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

View file

@ -0,0 +1,11 @@
export const SALES_SELECTORS = {
createSaleButton: "[data-test-id='create-sale']",
nameInput: "[name='name']",
percentageOption: "[value='PERCENTAGE']",
fixedOption: "[value='FIXED']",
discountValue: "[name='value']",
startDateInput: "[name='startDate']",
saveButton: "[data-test='button-bar-confirm']",
productsTab: "[data-test-id='products-tab']",
assignProducts: "[data-test-id='assign-products']"
};

View file

@ -1,3 +1,10 @@
export const DRAFT_ORDER_SELECTORS = {
addProducts: "[data-test-id='add-products-button']"
addProducts: "[data-test-id='add-products-button']",
salesChannel: "[data-test-id='sales-channel']",
editCustomerButton: "[data-test-id='edit-customer']",
selectCustomer: "[data-test-id='select-customer']",
selectCustomerOption: "[data-test-type='option']",
addShippingCarrierLink: "[data-test-id='add-shipping-carrier']",
finalizeButton: "[data-test='button-bar-confirm']",
pageHeader: "[data-test-id='page-header']"
};

View file

@ -0,0 +1,3 @@
export const DRAFT_ORDERS_LIST_SELECTORS = {
draftOrderRow: "[data-test-id='draft-order-table-row']"
};

View file

@ -1,4 +1,5 @@
export const ORDERS_SELECTORS = {
orders: "[data-test='submenu-item-label'][data-test-id='orders']",
createOrder: "[data-test-id='create-order-button']"
createOrder: "[data-test-id='create-order-button']",
orderRow: "[data-test-id='order-table-row']"
};

View file

@ -1,5 +1,5 @@
/* eslint-disable sort-keys */
export const BUTTON_SELECTORS = {
back: '[data-test="back"]',
submit: '[data-test="submit"]'
submit: '[data-test="submit"]',
checkbox: "[type='checkbox']"
};

View file

@ -0,0 +1,5 @@
export const SELECT_SHIPPING_METHOD_FORM = {
selectShippingMethod: "[id='mui-component-select-shippingMethod']",
shippingMethodOption: "[data-test='selectFieldOption']",
submitButton: "[type='submit']"
};

View file

@ -1,9 +1,9 @@
// <reference types="cypress" />
import faker from "faker";
import Channels from "../apiRequests/Channels";
import { createChannel } from "../apiRequests/Channels";
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/products/product-selectors";
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
@ -12,20 +12,17 @@ import { HEADER_SELECTORS } from "../elements/header/header-selectors";
import { DRAFT_ORDER_SELECTORS } from "../elements/orders/draft-order-selectors";
import { ORDERS_SELECTORS } from "../elements/orders/orders-selectors";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
import ChannelsSteps from "../steps/channelsSteps";
import { createChannelByView } from "../steps/channelsSteps";
import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils";
import { deleteChannelsStartsWith } from "../utils/channelsUtils";
describe("Channels", () => {
const channelStartsWith = "Cypress:";
const currency = "PLN";
const channels = new Channels();
const channelsUtils = new ChannelsUtils();
const channelsSteps = new ChannelsSteps();
before(() => {
cy.clearSessionData().loginUserViaRequest();
channelsUtils.deleteChannels(channelStartsWith);
deleteChannelsStartsWith(channelStartsWith);
});
beforeEach(() => {
@ -48,7 +45,7 @@ describe("Channels", () => {
cy.visit(urlList.channels);
cy.wait("@Channels");
cy.addAliasToGraphRequest("Channel");
channelsSteps.createChannelByView(randomChannel, currency);
createChannelByView(randomChannel, currency);
// New channel should be visible in channels list
cy.wait("@Channel")
.get(ADD_CHANNEL_FORM_SELECTORS.backToChannelsList)
@ -79,22 +76,18 @@ describe("Channels", () => {
it("should validate slug name", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency);
createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.channels);
channelsSteps.createChannelByView(randomChannel, currency);
createChannelByView(randomChannel, currency);
cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should(
"be.visible"
);
});
it("should validate currency", () => {
it("should validate duplicated currency", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
cy.visit(urlList.channels);
channelsSteps.createChannelByView(
randomChannel,
currency,
"notExistingCurrency"
);
createChannelByView(randomChannel, "notExistingCurrency");
cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyValidationMessage).should(
"be.visible"
);
@ -102,7 +95,7 @@ describe("Channels", () => {
it("should delete channel", () => {
const randomChannelToDelete = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(
createChannel(
false,
randomChannelToDelete,
randomChannelToDelete,
@ -111,8 +104,7 @@ describe("Channels", () => {
cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels);
cy.wait("@Channels");
cy.get(CHANNELS_SELECTORS.channelName)
.contains(randomChannelToDelete)
cy.contains(CHANNELS_SELECTORS.channelName, randomChannelToDelete)
.parentsUntil(CHANNELS_SELECTORS.channelsTable)
.find("button")
.click();
@ -127,7 +119,7 @@ describe("Channels", () => {
it("should not be possible to add products to order with inactive channel", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency);
createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.orders)
.get(ORDERS_SELECTORS.createOrder)
.click()

View file

@ -0,0 +1,171 @@
// <reference types="cypress" />
import faker from "faker";
import { updateChannelInProduct } from "../apiRequests/Product";
import { getCollection } from "../apiRequests/storeFront/Collections";
import { searchInShop } from "../apiRequests/storeFront/Search";
import {
assignProductsToCollection,
createCollection
} from "../steps/collectionsSteps";
import { urlList } from "../url/urlList";
import * as channelsUtils from "../utils/channelsUtils";
import { deleteCollectionsStartsWith } from "../utils/collectionsUtils";
import * as productsUtils from "../utils/productsUtils";
import { deleteShippingStartsWith } from "../utils/shippingUtils";
import {
isCollectionVisible,
isProductInCollectionVisible
} from "../utils/storeFront/collectionsUtils";
import { isProductVisibleInSearchResult } from "../utils/storeFront/storeFrontProductUtils";
describe("Collections", () => {
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let attribute;
let productType;
let category;
let product;
let defaultChannel;
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProductsStartsWith(startsWith);
deleteCollectionsStartsWith(startsWith);
deleteShippingStartsWith(startsWith);
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createTypeAttributeAndCategoryForProduct(name);
})
.then(
({
attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
attribute = attributeResp;
productType = productTypeResp;
category = categoryResp;
productsUtils.createProductInChannel({
name,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id
});
}
)
.then(({ product: productResp }) => (product = productResp));
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should not display hidden collections", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
cy.visit(urlList.collections);
let collection;
createCollection(collectionName, false, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
assignProductsToCollection(name);
})
.then(() => {
getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(false);
});
});
it("should display collections", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
let collection;
cy.visit(urlList.collections);
createCollection(collectionName, true, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
assignProductsToCollection(name);
getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(true);
});
});
it("should not display collection not set as available in channel", () => {
const collectionName = `${startsWith}${faker.random.number()}`;
let collection;
let channel;
channelsUtils
.createChannel({ name: collectionName })
.then(channelResp => {
channel = channelResp;
updateChannelInProduct(product.id, channel.id);
})
.then(() => {
cy.visit(urlList.collections);
createCollection(collectionName, true, channel);
})
.then(collectionResp => {
collection = collectionResp;
assignProductsToCollection(name);
getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isCollectionVisible(resp, collection.id);
expect(isVisible).to.equal(false);
});
});
it("should display products hidden in listing", () => {
// Products "hidden in listings" are not displayed in Category listings or search results,
// but are listed on Collections
const randomName = `${startsWith}${faker.random.number()}`;
let collection;
let createdProduct;
productsUtils
.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
visibleInListings: false
})
.then(({ product: productResp }) => (createdProduct = productResp));
cy.visit(urlList.collections);
createCollection(randomName, true, defaultChannel)
.then(collectionResp => {
collection = collectionResp;
assignProductsToCollection(randomName);
})
.then(() => {
getCollection(collection.id, defaultChannel.slug);
})
.then(resp => {
const isVisible = isProductInCollectionVisible(resp, createdProduct.id);
expect(isVisible).to.equal(true);
})
.then(() => {
searchInShop(createdProduct.name);
})
.then(resp => {
const isVisible = isProductVisibleInSearchResult(
resp,
createdProduct.name
);
expect(isVisible).to.equal(false);
});
});
});

View file

@ -0,0 +1,179 @@
// <reference types="cypress" />
import faker from "faker";
import { updateChannelInProduct } from "../../apiRequests/Product";
import {
assignProducts,
createSale,
discountOptions
} from "../../steps/salesSteps";
import { urlList } from "../../url/urlList";
import * as channelsUtils from "../../utils/channelsUtils";
import * as productsUtils from "../../utils/productsUtils";
import { deleteSalesStartsWith } from "../../utils/salesUtils";
import {
createShipping,
deleteShippingStartsWith
} from "../../utils/shippingUtils";
import { getProductPrice } from "../../utils/storeFront/storeFrontProductUtils";
describe("Sales discounts", () => {
const startsWith = "Cy-";
let productType;
let attribute;
let category;
let defaultChannel;
let warehouse;
before(() => {
cy.clearSessionData().loginUserViaRequest();
channelsUtils.deleteChannelsStartsWith(startsWith);
deleteSalesStartsWith(startsWith);
productsUtils.deleteProductsStartsWith(startsWith);
deleteShippingStartsWith(startsWith);
const name = `${startsWith}${faker.random.number()}`;
productsUtils
.createTypeAttributeAndCategoryForProduct(name)
.then(
({
productType: productTypeResp,
attribute: attributeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
channelsUtils.getDefaultChannel();
}
)
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
})
.then(addresses => {
createShipping({
channelId: defaultChannel.id,
name,
address: addresses.plAddress,
price: 100
});
})
.then(({ warehouse: warehouseResp }) => {
warehouse = warehouseResp;
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should create percentage discount", () => {
const saleName = `${startsWith}${faker.random.number()}`;
const discountValue = 50;
const productPrice = 100;
productsUtils
.createProductInChannel({
name: saleName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
})
.then(({ product: productResp }) => {
cy.visit(urlList.sales);
const product = productResp;
createSale({
saleName,
channelName: defaultChannel.name,
discountValue,
discountOption: discountOptions.PERCENTAGE
});
assignProducts(product.name);
getProductPrice(product.id, defaultChannel.slug);
})
.then(price => {
const expectedPrice = (productPrice * discountValue) / 100;
expect(expectedPrice).to.be.eq(price);
});
});
it("should create fixed price discount", () => {
const saleName = `${startsWith}${faker.random.number()}`;
const discountValue = 50;
const productPrice = 100;
productsUtils
.createProductInChannel({
name: saleName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
})
.then(({ product: productResp }) => {
cy.visit(urlList.sales);
const product = productResp;
createSale({
saleName,
channelName: defaultChannel.name,
discountValue,
discountOption: discountOptions.FIXED
});
assignProducts(product.name);
getProductPrice(product.id, defaultChannel.slug);
})
.then(price => {
const expectedPrice = productPrice - discountValue;
expect(expectedPrice).to.be.eq(price);
});
});
it("should not displayed discount not assign to channel", () => {
const saleName = `${startsWith}${faker.random.number()}`;
let channel;
let product;
const discountValue = 50;
const productPrice = 100;
channelsUtils
.createChannel({ name: saleName })
.then(channelResp => (channel = channelResp));
productsUtils
.createProductInChannel({
name: saleName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
})
.then(({ product: productResp }) => {
product = productResp;
updateChannelInProduct({
productId: product.id,
channelId: channel.id
});
})
.then(() => {
cy.visit(urlList.sales);
createSale({
saleName,
channelName: channel.name,
discountValue
});
assignProducts(product.name);
getProductPrice(product.id, defaultChannel.slug);
})
.then(price => expect(price).to.equal(productPrice));
});
});

View file

@ -1,29 +1,34 @@
import faker from "faker";
import Customer from "../apiRequests/Customer";
import {
createCustomer,
deleteCustomersStartsWith
} from "../apiRequests/Customer";
import { HOMEPAGE_SELECTORS } from "../elements/homePage/homePage-selectors";
import HomePageSteps from "../steps/homePageSteps";
import { changeChannel } from "../steps/homePageSteps";
import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils";
import HomePageUtils from "../utils/homePageUtils";
import OrdersUtils from "../utils/ordersUtils";
import ProductsUtils from "../utils/productsUtils";
import ShippingUtils from "../utils/shippingUtils";
import { getDefaultChannel } from "../utils/channelsUtils";
import * as homePageUtils from "../utils/homePageUtils";
import {
createReadyToFulfillOrder,
createWaitingForCaptureOrder
} from "../utils/ordersUtils";
import * as productsUtils from "../utils/productsUtils";
import * as shippingUtils from "../utils/shippingUtils";
// <reference types="cypress" />
describe("Homepage analytics", () => {
const startsWith = "Cy-";
const customer = new Customer();
const productsUtils = new ProductsUtils();
const shippingUtils = new ShippingUtils();
const ordersUtils = new OrdersUtils();
const channelsUtils = new ChannelsUtils();
const homePageUtils = new HomePageUtils();
const homePageSteps = new HomePageSteps();
let customerId;
let defaultChannel;
let createdVariants;
let productType;
let attribute;
let category;
let warehouse;
let shippingMethod;
const productPrice = 22;
const shippingPrice = 12;
const randomName = startsWith + faker.random.number();
@ -31,21 +36,18 @@ describe("Homepage analytics", () => {
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
customer.deleteCustomers(startsWith);
shippingUtils.deleteShipping(startsWith);
productsUtils.deleteProductsStartsWith(startsWith);
deleteCustomersStartsWith(startsWith);
shippingUtils.deleteShippingStartsWith(startsWith);
let addresses;
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
})
.then(addressesFixture => (addresses = addressesFixture))
.then(() =>
customer.createCustomer(randomEmail, randomName, addresses.plAddress)
)
.then(() => createCustomer(randomEmail, randomName, addresses.plAddress))
.then(resp => {
customerId = resp.body.data.customerCreate.user.id;
shippingUtils.createShipping({
@ -55,24 +57,36 @@ describe("Homepage analytics", () => {
price: shippingPrice
});
})
.then(() => {
productsUtils.createTypeAttributeAndCategoryForProduct(randomName);
})
.then(() => {
const warehouse = shippingUtils.getWarehouse();
const productType = productsUtils.getProductType();
const attribute = productsUtils.getAttribute();
const category = productsUtils.getCategory();
productsUtils.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
quantityInWarehouse: 20,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
});
.then(
({ warehouse: warehouseResp, shippingMethod: shippingMethodResp }) => {
warehouse = warehouseResp;
shippingMethod = shippingMethodResp;
productsUtils.createTypeAttributeAndCategoryForProduct(randomName);
}
)
.then(
({
productType: productTypeResp,
attribute: attributeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
productsUtils.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
quantityInWarehouse: 20,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
});
}
)
.then(({ variants: variantsResp }) => {
createdVariants = variantsResp;
});
});
@ -96,11 +110,11 @@ describe("Homepage analytics", () => {
.getOrdersReadyToFulfill(defaultChannel.slug)
.as("ordersReadyToFulfill");
ordersUtils.createReadyToFulfillOrder(
createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
shippingMethod.id,
defaultChannel.id,
productsUtils.getCreatedVariants()
createdVariants
);
cy.get("@ordersReadyToFulfill").then(ordersReadyToFulfillBefore => {
const allOrdersReadyToFulfill = ordersReadyToFulfillBefore + 1;
@ -109,7 +123,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrdersReadyToFulfill}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.ordersReadyToFulfill,
ordersReadyToFulfillRegexp
@ -120,13 +134,12 @@ describe("Homepage analytics", () => {
homePageUtils
.getOrdersReadyForCapture(defaultChannel.slug)
.as("ordersReadyForCapture");
const variantsList = productsUtils.getCreatedVariants();
ordersUtils.createWaitingForCaptureOrder(
createWaitingForCaptureOrder(
defaultChannel.slug,
randomEmail,
variantsList,
shippingUtils.getShippingMethod().id
createdVariants,
shippingMethod.id
);
cy.get("@ordersReadyForCapture").then(ordersReadyForCaptureBefore => {
@ -136,7 +149,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrdersReadyForCapture}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.ordersReadyForCapture,
ordersReadyForCaptureRegexp
@ -148,13 +161,8 @@ describe("Homepage analytics", () => {
.getProductsOutOfStock(defaultChannel.slug)
.as("productsOutOfStock");
const productOutOfStockRandomName = startsWith + faker.random.number();
const productsOutOfStockUtils = new ProductsUtils();
const warehouse = shippingUtils.getWarehouse();
const productType = productsUtils.getProductType();
const attribute = productsUtils.getAttribute();
const category = productsUtils.getCategory();
productsOutOfStockUtils.createProductInChannel({
productsUtils.createProductInChannel({
name: productOutOfStockRandomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
@ -172,7 +180,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allProductsOutOfStock}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.productsOutOfStock,
productsOutOfStockRegexp
@ -182,11 +190,11 @@ describe("Homepage analytics", () => {
it("should correct amount of sales be displayed", () => {
homePageUtils.getSalesAmount(defaultChannel.slug).as("salesAmount");
ordersUtils.createReadyToFulfillOrder(
createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
shippingMethod.id,
defaultChannel.id,
productsUtils.getCreatedVariants()
createdVariants
);
cy.get("@salesAmount").then(salesAmount => {
@ -205,7 +213,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${totalAmountWithSeparators}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.sales, salesAmountRegexp).should(
"be.visible"
);
@ -214,11 +222,11 @@ describe("Homepage analytics", () => {
it("should correct amount of orders be displayed", () => {
homePageUtils.getTodaysOrders(defaultChannel.slug).as("todaysOrders");
ordersUtils.createReadyToFulfillOrder(
createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
shippingMethod.id,
defaultChannel.id,
productsUtils.getCreatedVariants()
createdVariants
);
cy.get("@todaysOrders").then(ordersBefore => {
@ -228,7 +236,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrders}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.orders, ordersRegexp).should("be.visible");
});
});

View file

@ -0,0 +1,97 @@
// <reference types="cypress" />
import faker from "faker";
import { CHANNEL_FORM_SELECTORS } from "../../elements/channels/channel-form-selectors";
import { HEADER_SELECTORS } from "../../elements/header/header-selectors";
import { DRAFT_ORDER_SELECTORS } from "../../elements/orders/draft-order-selectors";
import { ORDERS_SELECTORS } from "../../elements/orders/orders-selectors";
import {
selectChannelInHeader,
selectChannelInPicker
} from "../../steps/channelsSteps";
import { urlList } from "../../url/urlList";
import * as channelsUtils from "../../utils/channelsUtils";
describe("Channels in draft orders", () => {
const startsWith = "Cy-";
const randomName = startsWith + faker.random.number();
let defaultChannel;
let otherChannel;
before(() => {
cy.clearSessionData().loginUserViaRequest();
channelsUtils.deleteChannelsStartsWith(startsWith);
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
channelsUtils.createChannel({ name: randomName });
})
.then(channelResp => {
otherChannel = channelResp;
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("Draft order channel should be taken from global channel picker", () => {
let channelName;
cy.visit(urlList.homePage);
cy.getTextFromElement(HEADER_SELECTORS.channelSelect).then(
channelInHeader => {
channelName = channelInHeader;
}
);
cy.visit(urlList.orders)
.get(ORDERS_SELECTORS.createOrder)
.click();
cy.getTextFromElement(CHANNEL_FORM_SELECTORS.channelSelect).then(
selectedChannelName => {
expect(channelName).to.contains(selectedChannelName);
}
);
cy.get(CHANNEL_FORM_SELECTORS.confirmButton).click();
cy.getTextFromElement(DRAFT_ORDER_SELECTORS.salesChannel).then(
channelNameInDraftOrder => {
expect(channelName).to.contains(channelNameInDraftOrder);
}
);
});
it("Draft order channel should be taken from global channel picker when changed", () => {
cy.visit(urlList.homePage);
selectChannelInHeader(otherChannel.name);
cy.visit(urlList.orders);
cy.get(ORDERS_SELECTORS.createOrder).click();
cy.getTextFromElement(CHANNEL_FORM_SELECTORS.channelSelect).then(
channelInSelect => {
expect(channelInSelect).to.be.eq(otherChannel.name);
}
);
cy.get(CHANNEL_FORM_SELECTORS.confirmButton).click();
cy.getTextFromElement(DRAFT_ORDER_SELECTORS.salesChannel).then(
channelInDraftOrder => {
expect(channelInDraftOrder).to.be.eq(otherChannel.name);
}
);
});
it("should create draft order with chosen channel", () => {
cy.visit(urlList.homePage);
selectChannelInHeader(defaultChannel.name);
cy.visit(urlList.orders);
cy.get(ORDERS_SELECTORS.createOrder).click();
cy.getTextFromElement(CHANNEL_FORM_SELECTORS.channelSelect).then(
channelInSelect => {
expect(channelInSelect).to.be.eq(defaultChannel.name);
}
);
selectChannelInPicker(otherChannel.name);
cy.getTextFromElement(DRAFT_ORDER_SELECTORS.salesChannel).then(
channelInDraftOrder => {
expect(channelInDraftOrder).to.be.eq(otherChannel.name);
}
);
});
});

View file

@ -0,0 +1,101 @@
// <reference types="cypress" />
import faker from "faker";
import {
createCustomer,
deleteCustomersStartsWith
} from "../../apiRequests/Customer";
import { DRAFT_ORDERS_LIST_SELECTORS } from "../../elements/orders/draft-orders-list-selectors";
import { ORDERS_SELECTORS } from "../../elements/orders/orders-selectors";
import { selectChannelInPicker } from "../../steps/channelsSteps";
import { finalizeDraftOrder } from "../../steps/draftOrderSteps";
import { urlList } from "../../url/urlList";
import { getDefaultChannel } from "../../utils/channelsUtils";
import * as productsUtils from "../../utils/productsUtils";
import {
createShipping,
deleteShippingStartsWith
} from "../../utils/shippingUtils";
describe("Draft orders", () => {
const startsWith = "Cy-";
const randomName = startsWith + faker.random.number();
let defaultChannel;
let warehouse;
before(() => {
cy.clearSessionData().loginUserViaRequest();
deleteCustomersStartsWith(startsWith);
deleteShippingStartsWith(startsWith);
productsUtils.deleteProductsStartsWith(startsWith);
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
})
.then(() => {
cy.fixture("addresses");
})
.then(addresses => {
createCustomer(
`${randomName}@example.com`,
randomName,
addresses.plAddress,
true
);
createShipping({
channelId: defaultChannel.id,
name: randomName,
address: addresses.plAddress
});
})
.then(({ warehouse: warehouseResp }) => {
warehouse = warehouseResp;
productsUtils.createTypeAttributeAndCategoryForProduct(randomName);
})
.then(
({
productType: productTypeResp,
attribute: attributeResp,
category: categoryResp
}) => {
productsUtils.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productTypeResp.id,
attributeId: attributeResp.id,
categoryId: categoryResp.id
});
}
);
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should move draft order to orders", () => {
cy.visit(urlList.orders)
.get(ORDERS_SELECTORS.createOrder)
.click();
selectChannelInPicker(defaultChannel.name);
finalizeDraftOrder(randomName).then(draftOrderNumber => {
cy.visit(urlList.orders);
cy.contains(ORDERS_SELECTORS.orderRow, draftOrderNumber).should(
$order => {
/* eslint-disable no-unused-expressions */
expect($order).to.be.visible;
}
);
cy.visit(urlList.draftOrders);
cy.contains(
DRAFT_ORDERS_LIST_SELECTORS.draftOrderRow,
draftOrderNumber
).should($draftOrder => {
expect($draftOrder).to.not.exist;
});
});
});
});

View file

@ -1,18 +1,15 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { getProductDetails } from "../../../apiRequests/storeFront/ProductDetails";
import { updateProductIsAvailableForPurchase } from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import ShippingUtils from "../../../utils/shippingUtils";
import { getDefaultChannel } from "../../../utils/channelsUtils";
import * as productsUtils from "../../../utils/productsUtils";
import * as shippingUtils from "../../../utils/shippingUtils";
import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Products available in listings", () => {
const shippingUtils = new ShippingUtils();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
@ -23,11 +20,10 @@ describe("Products available in listings", () => {
before(() => {
cy.clearSessionData().loginUserViaRequest();
shippingUtils.deleteShipping(startsWith);
productsUtils.deleteProperProducts(startsWith);
shippingUtils.deleteShippingStartsWith(startsWith);
productsUtils.deleteProductsStartsWith(startsWith);
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
@ -39,15 +35,23 @@ describe("Products available in listings", () => {
address: addressesFixture.plAddress
});
})
.then(() => {
warehouse = shippingUtils.getWarehouse();
.then(({ warehouse: warehouseResp }) => {
warehouse = warehouseResp;
});
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
productsUtils
.createTypeAttributeAndCategoryForProduct(name)
.then(
({
attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
}
);
});
beforeEach(() => {
@ -56,6 +60,8 @@ describe("Products available in listings", () => {
it("should update product to available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`;
let product;
productsUtils
.createProductInChannel({
name: productName,
@ -66,25 +72,22 @@ describe("Products available in listings", () => {
categoryId: category.id,
isAvailableForPurchase: false
})
.then(() => {
const productUrl = productDetailsUrl(
productsUtils.getCreatedProduct().id
);
productSteps.updateProductIsAvailableForPurchase(productUrl, true);
.then(({ product: productResp }) => {
product = productResp;
const productUrl = productDetailsUrl(product.id);
updateProductIsAvailableForPurchase(productUrl, true);
})
.then(() => {
isProductAvailableForPurchase(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisibleResp => {
expect(isVisibleResp).to.be.eq(true);
.then(resp => {
expect(isProductAvailableForPurchase(resp)).to.be.eq(true);
});
});
it("should update product to not available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`;
let product;
productsUtils
.createProductInChannel({
name: productName,
@ -94,21 +97,16 @@ describe("Products available in listings", () => {
attributeId: attribute.id,
categoryId: category.id
})
.then(() => {
const productUrl = productDetailsUrl(
productsUtils.getCreatedProduct().id
);
productSteps.updateProductIsAvailableForPurchase(productUrl, false);
.then(({ product: productResp }) => {
product = productResp;
const productUrl = productDetailsUrl(product.id);
updateProductIsAvailableForPurchase(productUrl, false);
})
.then(() => {
isProductAvailableForPurchase(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
getProductDetails(product.id, defaultChannel.slug);
})
.then(isProductVisible => {
expect(isProductVisible).to.be.eq(false);
.then(resp => {
expect(isProductAvailableForPurchase(resp)).to.be.eq(false);
});
});
});

View file

@ -1,17 +1,14 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { getProductDetails } from "../../../apiRequests/storeFront/ProductDetails";
import { updateProductPublish } from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import { getDefaultChannel } from "../../../utils/channelsUtils";
import * as productsUtils from "../../../utils/productsUtils";
import { isProductVisible } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Published products", () => {
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
@ -20,12 +17,20 @@ describe("Published products", () => {
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
productsUtils.deleteProductsStartsWith(startsWith);
productsUtils
.createTypeAttributeAndCategoryForProduct(name)
.then(
({
attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
}
);
});
beforeEach(() => {
@ -34,8 +39,7 @@ describe("Published products", () => {
it("should update product to published", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
@ -48,13 +52,14 @@ describe("Published products", () => {
isAvailableForPurchase: false
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
.then(({ product: productResp }) => {
const product = productResp;
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, true);
isProductVisible(product.id, defaultChannel.slug, productName);
updateProductPublish(productUrl, true);
getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(true);
});
});
@ -63,8 +68,7 @@ describe("Published products", () => {
let defaultChannel;
let product;
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
@ -75,20 +79,22 @@ describe("Published products", () => {
categoryId: category.id
});
})
.then(() => {
product = productsUtils.getCreatedProduct();
.then(({ product: productResp }) => {
product = productResp;
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, false);
isProductVisible(product.id, defaultChannel.slug, productName);
updateProductPublish(productUrl, false);
getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(false);
cy.loginInShop();
})
.then(() => {
isProductVisible(product.id, defaultChannel.slug, productName);
getProductDetails(product.id, defaultChannel.slug);
})
.then(isVisible => {
.then(resp => {
const isVisible = isProductVisible(resp, productName);
expect(isVisible).to.be.eq(true);
});
});

View file

@ -1,17 +1,14 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { searchInShop } from "../../../apiRequests/storeFront/Search";
import { updateProductVisibleInListings } from "../../../steps/products/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import { getDefaultChannel } from "../../../utils/channelsUtils";
import * as productsUtils from "../../../utils/productsUtils";
import { isProductVisibleInSearchResult } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Products displayed in listings", () => {
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
@ -20,12 +17,20 @@ describe("Products displayed in listings", () => {
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
productsUtils.deleteProductsStartsWith(startsWith);
productsUtils
.createTypeAttributeAndCategoryForProduct(name)
.then(
({
attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
}
);
});
beforeEach(() => {
@ -34,8 +39,7 @@ describe("Products displayed in listings", () => {
it("should update product to visible in listings", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
@ -48,21 +52,24 @@ describe("Products displayed in listings", () => {
isAvailableForPurchase: false
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
.then(({ product: productResp }) => {
const product = productResp;
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug);
updateProductVisibleInListings(productUrl);
searchInShop(productName);
})
.then(isProductVisible => {
.then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(true);
});
});
it("should update product to not visible in listings", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
@ -74,21 +81,28 @@ describe("Products displayed in listings", () => {
visibleInListings: true
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
.then(({ product: productResp }) => {
const product = productResp;
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug).then(
isProductVisible => {
expect(isProductVisible).to.be.eq(false);
}
);
updateProductVisibleInListings(productUrl);
searchInShop(productName).then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(false);
});
cy.loginInShop();
})
.then(() => {
isProductVisibleInSearchResult(productName, defaultChannel.slug);
searchInShop(productName);
})
.then(isProductVisible => {
.then(resp => {
const isProductVisible = isProductVisibleInSearchResult(
resp,
productName
);
expect(isProductVisible).to.be.eq(true);
});
});

View file

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

View file

@ -0,0 +1,179 @@
import faker from "faker";
import { createChannel } from "../../apiRequests/Channels";
import {
createProduct,
updateChannelInProduct
} from "../../apiRequests/Product";
import {
createFirstVariant,
createVariant
} from "../../steps/products/VariantsSteps";
import { urlList } from "../../url/urlList";
import {
deleteChannelsStartsWith,
getDefaultChannel
} from "../../utils/channelsUtils";
import * as productUtils from "../../utils/productsUtils";
import * as shippingUtils from "../../utils/shippingUtils";
import { getProductVariants } from "../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Creating variants", () => {
const startsWith = "Cy-";
const attributeValues = ["value1", "value2"];
let defaultChannel;
let warehouse;
let attribute;
let productType;
let category;
before(() => {
cy.clearSessionData().loginUserViaRequest();
shippingUtils.deleteShippingStartsWith(startsWith);
productUtils.deleteProductsStartsWith(startsWith);
deleteChannelsStartsWith(startsWith);
const name = `${startsWith}${faker.random.number()}`;
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
})
.then(fixtureAddresses =>
shippingUtils.createShipping({
channelId: defaultChannel.id,
name,
address: fixtureAddresses.plAddress
})
)
.then(({ warehouse: warehouseResp }) => (warehouse = warehouseResp));
productUtils
.createTypeAttributeAndCategoryForProduct(name, attributeValues)
.then(
({
attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
attribute = attributeResp;
productType = productTypeResp;
category = categoryResp;
}
);
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should create variant visible on frontend", () => {
const name = `${startsWith}${faker.random.number()}`;
const price = 10;
let createdProduct;
createProduct(attribute.id, name, productType.id, category.id)
.then(resp => {
createdProduct = resp.body.data.productCreate.product;
updateChannelInProduct({
productId: createdProduct.id,
channelId: defaultChannel.id
});
cy.visit(`${urlList.products}${createdProduct.id}`);
createFirstVariant({
sku: name,
warehouseId: warehouse.id,
price,
attribute: attributeValues[0]
});
getProductVariants(createdProduct.id, defaultChannel.slug);
})
.then(([variant]) => {
expect(variant).to.have.property("name", attributeValues[0]);
expect(variant).to.have.property("price", price);
});
});
it("should create several variants", () => {
const name = `${startsWith}${faker.random.number()}`;
const secondVariantSku = `${startsWith}${faker.random.number()}`;
const variants = [{ price: 7 }, { name: attributeValues[1], price: 16 }];
let createdProduct;
productUtils
.createProductInChannel({
name,
attributeId: attribute.id,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
categoryId: category.id,
price: variants[0].price
})
.then(({ product: productResp }) => {
createdProduct = productResp;
cy.visit(`${urlList.products}${createdProduct.id}`);
createVariant({
sku: secondVariantSku,
warehouseName: warehouse.name,
attributeName: variants[1].name,
price: variants[1].price
});
})
.then(() => getProductVariants(createdProduct.id, defaultChannel.slug))
.then(([firstVariant, secondVariant]) => {
expect(firstVariant).to.have.property("price", variants[0].price);
expect(secondVariant).to.have.property("name", variants[1].name);
expect(secondVariant).to.have.property("price", variants[1].price);
});
});
it("should create variant for many channels", () => {
const name = `${startsWith}${faker.random.number()}`;
const variantsPrice = 10;
let newChannel;
let createdProduct;
createChannel(true, name, name, "PLN")
.then(resp => {
newChannel = resp.body.data.channelCreate.channel;
productUtils.createProduct(
attribute.id,
name,
productType.id,
category.id
);
})
.then(productResp => {
createdProduct = productResp;
updateChannelInProduct({
productId: createdProduct.id,
channelId: defaultChannel.id
});
})
.then(() => {
updateChannelInProduct({
productId: createdProduct.id,
channelId: newChannel.id
});
})
.then(() => {
cy.visit(`${urlList.products}${createdProduct.id}`);
createFirstVariant({
sku: name,
warehouseId: warehouse.id,
price: variantsPrice,
attribute: attributeValues[0]
});
getProductVariants(createdProduct.id, defaultChannel.slug);
})
.then(([variant]) => {
expect(variant).to.have.property("name", attributeValues[0]);
expect(variant).to.have.property("price", variantsPrice);
getProductVariants(createdProduct.id, newChannel.slug);
})
.then(([variant]) => {
expect(variant).to.have.property("name", attributeValues[0]);
expect(variant).to.have.property("price", variantsPrice);
});
});
});

View file

@ -1,26 +1,38 @@
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
import { HEADER_SELECTORS } from "../elements/header/header-selectors";
class ChannelsSteps {
createChannelByView(name, currency, otherCurrency, slug = name) {
cy.get(CHANNELS_SELECTORS.createChannelButton)
.click()
.get(ADD_CHANNEL_FORM_SELECTORS.channelName)
.type(name)
.get(ADD_CHANNEL_FORM_SELECTORS.slug)
.type(slug)
.get(ADD_CHANNEL_FORM_SELECTORS.currency)
.click();
if (!otherCurrency) {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency);
cy.get(`[data-test-value=${currency}]`).click();
export function createChannelByView(name, currency, slug = name) {
cy.get(CHANNELS_SELECTORS.createChannelButton)
.click()
.get(ADD_CHANNEL_FORM_SELECTORS.channelName)
.type(name)
.get(ADD_CHANNEL_FORM_SELECTORS.slug)
.type(slug)
.get(ADD_CHANNEL_FORM_SELECTORS.currency)
.click();
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency);
cy.get("body").then($body => {
if ($body.find(currency).length) {
cy.contains(ADD_CHANNEL_FORM_SELECTORS.currencyOptions, currency).click();
} else {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency)
.type(otherCurrency)
.get(ADD_CHANNEL_FORM_SELECTORS.currencyAutocompleteDropdown)
.click();
cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyAutocompleteDropdown).click();
}
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
}
});
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
}
export function selectChannelInPicker(channelName) {
cy.get(CHANNEL_FORM_SELECTORS.channelSelect).click();
cy.contains(CHANNEL_FORM_SELECTORS.channelOption, channelName)
.click()
.get(CHANNEL_FORM_SELECTORS.confirmButton)
.click();
}
export function selectChannelInHeader(channelName) {
cy.get(HEADER_SELECTORS.channelSelect)
.click()
.get(HEADER_SELECTORS.channelSelectList)
.contains(channelName)
.click();
}
export default ChannelsSteps;

View file

@ -0,0 +1,47 @@
import { COLLECTION_SELECTORS } from "../elements/catalog/collection-selectors";
import { ASSIGN_PRODUCTS_SELECTORS } from "../elements/catalog/products/assign-products-selectors";
import { MENAGE_CHANNEL_AVAILABILITY_FORM } from "../elements/channels/menage-channel-availability-form";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
export function createCollection(collectionName, isPublished, channel) {
const publishedSelector = isPublished
? MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueTrue
: MENAGE_CHANNEL_AVAILABILITY_FORM.radioButtonsValueFalse;
cy.get(COLLECTION_SELECTORS.createCollectionButton)
.click()
.get(COLLECTION_SELECTORS.nameInput)
.type(collectionName)
.get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsMenageButton)
.click()
.get(MENAGE_CHANNEL_AVAILABILITY_FORM.allChannelsCheckbox)
.click();
cy.contains(MENAGE_CHANNEL_AVAILABILITY_FORM.channelRow, channel.name)
.find(MENAGE_CHANNEL_AVAILABILITY_FORM.channelCheckbox)
.click()
.get(BUTTON_SELECTORS.submit)
.click()
.get(MENAGE_CHANNEL_AVAILABILITY_FORM.channelsAvailabilityItem)
.click()
.get(
`${MENAGE_CHANNEL_AVAILABILITY_FORM.publishedCheckbox}${publishedSelector}`
)
.click();
cy.addAliasToGraphRequest("CreateCollection");
cy.get(COLLECTION_SELECTORS.saveButton).click();
return cy
.wait("@CreateCollection")
.its("response.body.data.collectionCreate.collection");
}
export function assignProductsToCollection(productName) {
cy.get(COLLECTION_SELECTORS.addProductButton)
.click()
.get(ASSIGN_PRODUCTS_SELECTORS.searchInput)
.type(productName);
cy.contains(ASSIGN_PRODUCTS_SELECTORS.tableRow, productName)
.find(ASSIGN_PRODUCTS_SELECTORS.checkbox)
.click();
cy.addAliasToGraphRequest("CollectionAssignProduct");
cy.get(ASSIGN_PRODUCTS_SELECTORS.submitButton).click();
cy.wait("@CollectionAssignProduct");
}

View file

@ -0,0 +1,39 @@
import { ASSIGN_PRODUCTS_SELECTORS } from "../elements/catalog/products/assign-products-selectors";
import { DRAFT_ORDER_SELECTORS } from "../elements/orders/draft-order-selectors";
import { SELECT_SHIPPING_METHOD_FORM } from "../elements/shipping/select-shipping-method-form";
export function finalizeDraftOrder(name) {
cy.get(DRAFT_ORDER_SELECTORS.addProducts)
.click()
.get(ASSIGN_PRODUCTS_SELECTORS.searchInput)
.type(name);
cy.contains(ASSIGN_PRODUCTS_SELECTORS.tableRow, name)
.find(ASSIGN_PRODUCTS_SELECTORS.checkbox)
.click()
.get(ASSIGN_PRODUCTS_SELECTORS.submitButton)
.click()
.get(DRAFT_ORDER_SELECTORS.editCustomerButton)
.click()
.get(DRAFT_ORDER_SELECTORS.selectCustomer)
.type(name);
cy.contains(DRAFT_ORDER_SELECTORS.selectCustomerOption, name)
.click()
.get(DRAFT_ORDER_SELECTORS.addShippingCarrierLink)
.click()
.get(SELECT_SHIPPING_METHOD_FORM.selectShippingMethod)
.click()
.get(SELECT_SHIPPING_METHOD_FORM.shippingMethodOption)
.first()
.click();
cy.addAliasToGraphRequest("OrderShippingMethodUpdate")
.get(SELECT_SHIPPING_METHOD_FORM.submitButton)
.click();
cy.wait("@OrderShippingMethodUpdate");
cy.getTextFromElement(DRAFT_ORDER_SELECTORS.pageHeader).as(
"draftOrderNumber"
);
cy.addAliasToGraphRequest("OrderDraftFinalize");
cy.get(DRAFT_ORDER_SELECTORS.finalizeButton).click();
cy.wait("@OrderDraftFinalize");
return cy.get("@draftOrderNumber");
}

View file

@ -1,12 +1,9 @@
import { HEADER_SELECTORS } from "../elements/header/header-selectors";
class HomePageSteps {
changeChannel(channelName) {
cy.get(HEADER_SELECTORS.channelSelect).click();
cy.addAliasToGraphRequest("Home");
cy.get(HEADER_SELECTORS.channelSelectList)
.contains(channelName)
.click();
cy.wait("@Home");
}
export function changeChannel(channelName) {
cy.get(HEADER_SELECTORS.channelSelect).click();
cy.addAliasToGraphRequest("Home");
cy.get(HEADER_SELECTORS.channelSelectList)
.contains(channelName)
.click();
cy.wait("@Home");
}
export default HomePageSteps;

View file

@ -1,37 +0,0 @@
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
class ProductSteps {
valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue;
valueFalse = PRODUCTS_SELECTORS.radioButtonsValueFalse;
updateProductIsAvailableForPurchase(productUrl, isAvailableForPurchase) {
const isAvailableForPurchaseSelector = isAvailableForPurchase
? this.valueTrue
: this.valueFalse;
const availableForPurchaseSelector = `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`;
this.updateProductMenageInChannel(productUrl, availableForPurchaseSelector);
}
updateProductPublish(productUrl, isPublished) {
const isPublishedSelector = isPublished ? this.valueTrue : this.valueFalse;
const publishedSelector = `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}`;
this.updateProductMenageInChannel(productUrl, publishedSelector);
}
updateProductVisibleInListings(productUrl) {
this.updateProductMenageInChannel(
productUrl,
PRODUCTS_SELECTORS.visibleInListingsButton
);
}
updateProductMenageInChannel(productUrl, menageSelector) {
cy.visit(productUrl)
.get(PRODUCTS_SELECTORS.assignedChannels)
.click()
.get(menageSelector)
.click();
cy.addAliasToGraphRequest("ProductChannelListingUpdate");
cy.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.wait("@ProductChannelListingUpdate");
}
}
export default ProductSteps;

View file

@ -0,0 +1,52 @@
import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
import { VARIANTS_SELECTORS } from "../../elements/catalog/variants-selectors";
export function createFirstVariant({ sku, warehouseId, price, attribute }) {
cy.get(PRODUCTS_SELECTORS.addVariantsButton).click();
cy.get(VARIANTS_SELECTORS.valueContainer)
.contains(attribute)
.find(VARIANTS_SELECTORS.attributeCheckbox)
.click()
.get(VARIANTS_SELECTORS.nextButton)
.click()
.get(VARIANTS_SELECTORS.priceInput)
.each($priceInput => {
cy.wrap($priceInput).type(price);
});
cy.get(`[name*='${warehouseId}']`)
.click()
.get(VARIANTS_SELECTORS.nextButton)
.click()
.get(VARIANTS_SELECTORS.skuInput)
.type(sku);
cy.addAliasToGraphRequest("ProductVariantBulkCreate");
cy.get(VARIANTS_SELECTORS.nextButton).click();
cy.wait("@ProductVariantBulkCreate");
}
export function createVariant({
sku,
warehouseName,
attributeName,
price,
costPrice = price
}) {
cy.get(PRODUCTS_SELECTORS.addVariantsButton)
.click()
.get(VARIANTS_SELECTORS.attributeSelector)
.click()
.get(VARIANTS_SELECTORS.attributeOption)
.contains(attributeName)
.click()
.get(VARIANTS_SELECTORS.priceInput)
.type(price)
.get(VARIANTS_SELECTORS.costPriceInput)
.type(costPrice)
.get(VARIANTS_SELECTORS.skuInputInAddVariant)
.type(sku)
.get(VARIANTS_SELECTORS.addWarehouseButton)
.click();
cy.contains(VARIANTS_SELECTORS.warehouseOption, warehouseName).click();
cy.addAliasToGraphRequest("ProductVariantDetails");
cy.get(VARIANTS_SELECTORS.saveButton).click();
cy.wait("@ProductVariantDetails");
}

View file

@ -0,0 +1,37 @@
import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors";
const valueTrue = PRODUCTS_SELECTORS.radioButtonsValueTrue;
const valueFalse = PRODUCTS_SELECTORS.radioButtonsValueFalse;
export function updateProductIsAvailableForPurchase(
productUrl,
isAvailableForPurchase
) {
const isAvailableForPurchaseSelector = isAvailableForPurchase
? valueTrue
: valueFalse;
const availableForPurchaseSelector = `${PRODUCTS_SELECTORS.availableForPurchaseRadioButtons}${isAvailableForPurchaseSelector}`;
updateProductMenageInChannel(productUrl, availableForPurchaseSelector);
}
export function updateProductPublish(productUrl, isPublished) {
const isPublishedSelector = isPublished ? valueTrue : valueFalse;
const publishedSelector = `${PRODUCTS_SELECTORS.publishedRadioButtons}${isPublishedSelector}`;
updateProductMenageInChannel(productUrl, publishedSelector);
}
export function updateProductVisibleInListings(productUrl) {
updateProductMenageInChannel(
productUrl,
PRODUCTS_SELECTORS.visibleInListingsButton
);
}
function updateProductMenageInChannel(productUrl, menageSelector) {
cy.visit(productUrl)
.get(PRODUCTS_SELECTORS.assignedChannels)
.click()
.get(menageSelector)
.click();
cy.addAliasToGraphRequest("ProductChannelListingUpdate");
cy.get(PRODUCTS_SELECTORS.saveBtn)
.click()
.wait("@ProductChannelListingUpdate");
}

View file

@ -0,0 +1,57 @@
import { ASSIGN_PRODUCTS_SELECTORS } from "../elements/catalog/assign-products";
import { MENAGE_CHANNEL_AVAILABILITY } from "../elements/channels/menage-channel-availability";
import { SALES_SELECTORS } from "../elements/discounts/sales";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
import { formatDate } from "../support/formatDate";
export const discountOptions = {
PERCENTAGE: SALES_SELECTORS.percentageOption,
FIXED: SALES_SELECTORS.fixedOption
};
export function createSale({
saleName,
channelName,
discountValue = 10,
discountOption = discountOptions.PERCENTAGE
}) {
const todaysDate = formatDate(new Date());
cy.get(SALES_SELECTORS.createSaleButton)
.click()
.get(SALES_SELECTORS.nameInput)
.type(saleName)
.get(discountOption)
.click()
.get(MENAGE_CHANNEL_AVAILABILITY.availableManageButton)
.click()
.get(MENAGE_CHANNEL_AVAILABILITY.allChannelsInput)
.click()
.get(MENAGE_CHANNEL_AVAILABILITY.channelsAvailabilityForm)
.contains(channelName)
.click()
.get(BUTTON_SELECTORS.submit)
.click()
.get(SALES_SELECTORS.discountValue)
.type(discountValue)
.get(SALES_SELECTORS.startDateInput)
.type(todaysDate);
cy.addAliasToGraphRequest("SaleCreate");
cy.get(SALES_SELECTORS.saveButton).click();
cy.wait("@SaleCreate");
}
export function assignProducts(productName) {
cy.get(SALES_SELECTORS.productsTab)
.click()
.get(SALES_SELECTORS.assignProducts)
.click()
.get(ASSIGN_PRODUCTS_SELECTORS.searchInput)
.type(productName);
cy.contains(ASSIGN_PRODUCTS_SELECTORS.tableRow, productName)
.find(BUTTON_SELECTORS.checkbox)
.click();
cy.addAliasToGraphRequest("SaleCataloguesAdd");
cy.get(BUTTON_SELECTORS.submit).click();
cy.wait("@SaleCataloguesAdd");
}

View file

@ -7,7 +7,7 @@ Cypress.Commands.add(
}
);
Cypress.Commands.add(
"deleteProperElements",
"deleteElementsStartsWith",
(deleteFunction, getFunction, startsWith, name) => {
getFunction(100, startsWith).then(elements => {
elements.forEach(element => {

View file

@ -0,0 +1,3 @@
Cypress.Commands.add("getTextFromElement", element =>
cy.get(element).invoke("text")
);

View file

@ -0,0 +1,12 @@
export function formatDate(date) {
const day = getPeriodValue(date, { day: "2-digit" });
const month = getPeriodValue(date, { month: "2-digit" });
const year = getPeriodValue(date, { year: "numeric" });
return new Array(year, month, day).join("-");
}
function getPeriodValue(date, option) {
const formatter = new Intl.DateTimeFormat("en-us", option);
return formatter.format(date);
}

View file

@ -1,6 +1,7 @@
import "./user";
import "./softAssertions";
import "./deleteElement/index.js";
import "./elements/index";
import { urlList } from "../url/urlList";

View file

@ -2,9 +2,12 @@ export const urlList = {
apiUri: Cypress.env("API_URI"),
channels: "channels/",
configuration: "configuration/",
draftOrders: "orders/drafts/",
homePage: "/",
orders: "orders/",
products: "products/",
warehouses: "warehouses/"
warehouses: "warehouses/",
sales: "discounts/sales/",
collections: "collections/"
};
export const productDetailsUrl = productId => `${urlList.products}${productId}`;

View file

@ -1,41 +1,44 @@
import Channels from "../apiRequests/Channels";
import * as 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) {
return;
}
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);
}
export function deleteChannelsStartsWith(nameStartsWith) {
channels.getChannels().then(resp => {
const channelsArray = new Set(resp.body.data.channels);
if (!channelsArray) {
return;
}
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]) {
channels.deleteChannel(element.id, targetChannels[0].id);
channelsArray.delete(element);
}
});
}
});
}
getDefaultChannel() {
return this.channels.getChannels().then(resp => {
const channelsArray = resp.body.data.channels;
return (this.defaultChannel = channelsArray.find(function(
channelElement
) {
return channelElement.slug === "default-channel";
}));
});
}
});
}
export function getDefaultChannel() {
return channels.getChannels().then(resp => {
const channelsArray = resp.body.data.channels;
return channelsArray.find(function(channelElement) {
return channelElement.slug === "default-channel";
});
});
}
export function createChannel({
isActive = true,
name,
slug = name,
currencyCode = "PLN"
}) {
return channels
.createChannel(isActive, name, slug, currencyCode)
.its("body.data.channelCreate.channel");
}
export default ChannelsUtils;

View file

@ -0,0 +1,10 @@
import { deleteCollection, getCollections } from "../apiRequests/Collections";
export function deleteCollectionsStartsWith(startsWith) {
cy.deleteElementsStartsWith(
deleteCollection,
getCollections,
startsWith,
"collection"
);
}

View file

@ -1,30 +1,27 @@
import HomePage from "../apiRequests/HomePage";
class HomePageUtils {
homePage = new HomePage();
getOrdersReadyToFulfill(channelSlug) {
return this.homePage
.getOrdersWithStatus("READY_TO_FULFILL", channelSlug)
.then(resp => resp.body.data.orders.totalCount);
}
getOrdersReadyForCapture(channelSlug) {
return this.homePage
.getOrdersWithStatus("READY_TO_CAPTURE", channelSlug)
.then(resp => resp.body.data.orders.totalCount);
}
getProductsOutOfStock(channelSlug) {
return this.homePage
.getProductsOutOfStock(channelSlug)
.then(resp => resp.body.data.products.totalCount);
}
getSalesAmount(channelSlug) {
return this.homePage
.getSalesForChannel(channelSlug, "TODAY")
.then(resp => resp.body.data.ordersTotal.gross.amount);
}
getTodaysOrders(channelSlug) {
return this.homePage
.getOrdersForChannel(channelSlug, "TODAY")
.then(resp => resp.body.data.orders.totalCount);
}
import * as homePage from "../apiRequests/HomePage";
export function getOrdersReadyToFulfill(channelSlug) {
return homePage
.getOrdersWithStatus("READY_TO_FULFILL", channelSlug)
.its("body.data.orders.totalCount");
}
export function getOrdersReadyForCapture(channelSlug) {
return homePage
.getOrdersWithStatus("READY_TO_CAPTURE", channelSlug)
.its("body.data.orders.totalCount");
}
export function getProductsOutOfStock(channelSlug) {
return homePage
.getProductsOutOfStock(channelSlug)
.its("body.data.products.totalCount");
}
export function getSalesAmount(channelSlug) {
return homePage
.getSalesForChannel(channelSlug, "TODAY")
.its("body.data.ordersTotal.gross.amount");
}
export function getTodaysOrders(channelSlug) {
return homePage
.getOrdersForChannel(channelSlug, "TODAY")
.its("body.data.orders.totalCount");
}
export default HomePageUtils;

View file

@ -1,60 +1,53 @@
import Checkout from "../apiRequests/Checkout";
import Order from "../apiRequests/Order";
import * as checkoutRequest from "../apiRequests/Checkout";
import * as orderRequest from "../apiRequests/Order";
class OrdersUtils {
checkoutRequest = new Checkout();
orderRequest = new Order();
checkout;
order;
createWaitingForCaptureOrder(
channelSlug,
email,
variantsList,
shippingMethodId
) {
return this.createCheckout(channelSlug, email, variantsList)
.then(() =>
this.checkoutRequest.addShippingMethod(
this.checkout.id,
shippingMethodId
)
)
.then(() => this.addPayment(this.checkout.id))
.then(() => this.checkoutRequest.completeCheckout(this.checkout.id));
}
createReadyToFulfillOrder(
customerId,
shippingMethodId,
channelId,
variantsList
) {
return this.createDraftOrder(customerId, shippingMethodId, channelId)
.then(() => {
variantsList.forEach(variantElement => {
this.orderRequest.addProductToOrder(this.order.id, variantElement.id);
});
})
.then(() => this.orderRequest.markOrderAsPaid(this.order.id))
.then(() => this.orderRequest.completeOrder(this.order.id));
}
createDraftOrder(customerId, shippingMethodId, channelId) {
return this.orderRequest
.createDraftOrder(customerId, shippingMethodId, channelId)
.then(resp => (this.order = resp.body.data.draftOrderCreate.order));
}
createCheckout(channelSlug, email, variantsList) {
return this.checkoutRequest
.createCheckout(channelSlug, email, 1, variantsList)
.then(resp => (this.checkout = resp.body.data.checkoutCreate.checkout));
}
addPayment(checkoutId) {
return this.checkoutRequest.addPayment(
checkoutId,
"mirumee.payments.dummy",
"not-charged"
);
}
export function createWaitingForCaptureOrder(
channelSlug,
email,
variantsList,
shippingMethodId
) {
let checkout;
return createCheckout(channelSlug, email, variantsList)
.then(checkoutResp => {
checkout = checkoutResp;
checkoutRequest.addShippingMethod(checkout.id, shippingMethodId);
})
.then(() => addPayment(checkout.id))
.then(() => checkoutRequest.completeCheckout(checkout.id))
.then(() => checkout);
}
export function createReadyToFulfillOrder(
customerId,
shippingMethodId,
channelId,
variantsList
) {
let order;
return createDraftOrder(customerId, shippingMethodId, channelId)
.then(orderResp => {
order = orderResp;
variantsList.forEach(variantElement => {
orderRequest.addProductToOrder(order.id, variantElement.id);
});
})
.then(() => orderRequest.markOrderAsPaid(order.id))
.then(() => orderRequest.completeOrder(order.id));
}
export function createDraftOrder(customerId, shippingMethodId, channelId) {
return orderRequest
.createDraftOrder(customerId, shippingMethodId, channelId)
.its("body.data.draftOrderCreate.order");
}
export function createCheckout(channelSlug, email, variantsList) {
return checkoutRequest
.createCheckout(channelSlug, email, 1, variantsList)
.its("body.data.checkoutCreate.checkout");
}
export function addPayment(checkoutId) {
return checkoutRequest.addPayment(
checkoutId,
"mirumee.payments.dummy",
"not-charged"
);
}
export default OrdersUtils;

View file

@ -1,152 +1,127 @@
import Attribute from "../apiRequests/Attribute";
import Category from "../apiRequests/Category";
import Product from "../apiRequests/Product";
import * as attributeRequest from "../apiRequests/Attribute";
import * as categoryRequest from "../apiRequests/Category";
import * as productRequest from "../apiRequests/Product";
class ProductsUtils {
productRequest = new Product();
attributeRequest = new Attribute();
categoryRequest = new Category();
product;
variants;
productType;
attribute;
category;
createProductWithVariant(name, attributeId, productTypeId, categoryId) {
return this.createProduct(
attributeId,
name,
productTypeId,
categoryId
).then(() => this.createVariant(this.product.id, name));
}
createProductInChannel({
name,
channelId,
warehouseId = null,
quantityInWarehouse = 10,
productTypeId,
attributeId,
categoryId,
price = 1,
isPublished = true,
isAvailableForPurchase = true,
visibleInListings = true
}) {
return this.createProduct(attributeId, name, productTypeId, categoryId)
.then(() =>
this.productRequest.updateChannelInProduct({
productId: this.product.id,
channelId,
isPublished,
isAvailableForPurchase,
visibleInListings
})
)
.then(() => {
this.createVariant(
this.product.id,
name,
warehouseId,
quantityInWarehouse,
channelId,
price
);
export function createProductInChannel({
name,
channelId,
warehouseId = null,
quantityInWarehouse = 10,
productTypeId,
attributeId,
categoryId,
price = 1,
isPublished = true,
isAvailableForPurchase = true,
visibleInListings = true
}) {
let product;
let variants;
return createProduct(attributeId, name, productTypeId, categoryId)
.then(productResp => {
product = productResp;
productRequest.updateChannelInProduct({
productId: product.id,
channelId,
isPublished,
isAvailableForPurchase,
visibleInListings
});
}
createTypeAttributeAndCategoryForProduct(name) {
return this.createAttribute(name)
.then(() => this.createTypeProduct(name, this.attribute.id))
.then(() => this.createCategory(name));
}
createAttribute(name) {
return this.attributeRequest
.createAttribute(name)
.then(
resp => (this.attribute = resp.body.data.attributeCreate.attribute)
);
}
createTypeProduct(name, attributeId) {
return this.productRequest
.createTypeProduct(name, attributeId)
.then(
resp =>
(this.productType = resp.body.data.productTypeCreate.productType)
);
}
createCategory(name) {
return this.categoryRequest
.createCategory(name)
.then(resp => (this.category = resp.body.data.categoryCreate.category));
}
createProduct(attributeId, name, productTypeId, categoryId) {
return this.productRequest
.createProduct(attributeId, name, productTypeId, categoryId)
.then(resp => (this.product = resp.body.data.productCreate.product));
}
createVariant(
productId,
name,
warehouseId,
quantityInWarehouse,
channelId,
price
) {
return this.productRequest
.createVariant(
productId,
name,
})
.then(() => {
createVariant({
productId: product.id,
sku: name,
warehouseId,
quantityInWarehouse,
channelId,
price
)
.then(
resp =>
(this.variants =
resp.body.data.productVariantBulkCreate.productVariants)
);
}
getCreatedProduct() {
return this.product;
}
getCreatedVariants() {
return this.variants;
}
getProductType() {
return this.productType;
}
getAttribute() {
return this.attribute;
}
getCategory() {
return this.category;
}
deleteProperProducts(startsWith) {
const product = new Product();
const attribute = new Attribute();
const category = new Category();
cy.deleteProperElements(
product.deleteProductType,
product.getProductTypes,
startsWith,
"productType"
);
cy.deleteProperElements(
attribute.deleteAttribute,
attribute.getAttributes,
startsWith,
"attributes"
);
cy.deleteProperElements(
category.deleteCategory,
category.getCategories,
startsWith,
"categories"
);
}
});
})
.then(variantsResp => {
variants = variantsResp;
return { product, variants };
});
}
export function createTypeAttributeAndCategoryForProduct(
name,
attributeValues
) {
let attribute;
let productType;
let category;
return createAttribute(name, attributeValues)
.then(attributeResp => {
attribute = attributeResp;
createTypeProduct(name, attributeResp.id);
})
.then(productTypeResp => {
productType = productTypeResp;
createCategory(name);
})
.then(categoryResp => {
category = categoryResp;
return { attribute, category, productType };
});
}
export function createAttribute(name, attributeValues) {
return attributeRequest
.createAttribute(name, attributeValues)
.its("body.data.attributeCreate.attribute");
}
export function createTypeProduct(name, attributeId) {
return productRequest
.createTypeProduct(name, attributeId)
.its("body.data.productTypeCreate.productType");
}
export function createCategory(name) {
return categoryRequest
.createCategory(name)
.its("body.data.categoryCreate.category");
}
export function createProduct(attributeId, name, productTypeId, categoryId) {
return productRequest
.createProduct(attributeId, name, productTypeId, categoryId)
.its("body.data.productCreate.product");
}
export function createVariant({
productId,
sku,
warehouseId,
quantityInWarehouse,
channelId,
price
}) {
return productRequest
.createVariant({
productId,
sku,
warehouseId,
quantity: quantityInWarehouse,
channelId,
price
})
.its("body.data.productVariantBulkCreate.productVariants");
}
export function deleteProductsStartsWith(startsWith) {
cy.deleteElementsStartsWith(
productRequest.deleteProductType,
productRequest.getProductTypes,
startsWith,
"productType"
);
cy.deleteElementsStartsWith(
attributeRequest.deleteAttribute,
attributeRequest.getAttributes,
startsWith,
"attributes"
);
cy.deleteElementsStartsWith(
categoryRequest.deleteCategory,
categoryRequest.getCategories,
startsWith,
"categories"
);
}
export default ProductsUtils;

View file

@ -0,0 +1,5 @@
import { deleteSale, getSales } from "../apiRequests/Sales";
export function deleteSalesStartsWith(startsWith) {
cy.deleteElementsStartsWith(deleteSale, getSales, startsWith, "sales");
}

View file

@ -1,77 +1,58 @@
import ShippingMethod from "../apiRequests/ShippingMethod";
import Warehouse from "../apiRequests/Warehouse";
class ShippingUtils {
shippingMethodRequest = new ShippingMethod();
warehouseRequest = new Warehouse();
import * as shippingMethodRequest from "../apiRequests/ShippingMethod";
import * as warehouseRequest from "../apiRequests/Warehouse";
shippingMethod;
shippingZone;
warehouse;
export function createShipping({ channelId, name, address, price = 1 }) {
let shippingMethod;
let shippingZone;
let warehouse;
createShipping({ channelId, name, address, price = 1 }) {
return this.createShippingZone(name, address.country)
.then(() => this.createWarehouse(name, this.shippingZone.id, address))
.then(() => this.createShippingRate(name, this.shippingZone.id))
.then(() =>
this.shippingMethodRequest.addChannelToShippingMethod(
this.shippingMethod.id,
channelId,
price
)
return createShippingZone(name, address.country)
.then(shippingZoneResp => {
shippingZone = shippingZoneResp;
createWarehouse(name, shippingZone.id, address);
})
.then(warehouseResp => {
warehouse = warehouseResp;
createShippingRate(name, shippingZone.id);
})
.then(sippingMethodResp => {
shippingMethod = sippingMethodResp;
shippingMethodRequest.addChannelToShippingMethod(
shippingMethod.id,
channelId,
price
);
}
createShippingZone(name, country) {
return this.shippingMethodRequest
.createShippingZone(name, country)
.then(resp => {
this.shippingZone = resp.body.data.shippingZoneCreate.shippingZone;
});
}
createWarehouse(name, shippingZoneId, address) {
return this.warehouseRequest
.createWarehouse(name, shippingZoneId, address)
.then(resp => {
this.warehouse = resp.body.data.createWarehouse.warehouse;
});
}
createShippingRate(name, shippingZoneId) {
return this.shippingMethodRequest
.createShippingRate(name, shippingZoneId)
.then(
resp =>
(this.shippingMethod =
resp.body.data.shippingPriceCreate.shippingMethod)
);
}
getShippingMethod() {
return this.shippingMethod;
}
getShippingZone() {
return this.shippingZone;
}
getWarehouse() {
return this.warehouse;
}
deleteShipping(startsWith) {
const shippingMethod = new ShippingMethod();
const warehouse = new Warehouse();
cy.deleteProperElements(
shippingMethod.deleteShippingZone,
shippingMethod.getShippingZones,
startsWith,
"shippingZONE"
);
cy.deleteProperElements(
warehouse.deleteWarehouse,
warehouse.getWarehouses,
startsWith,
"Warehouse"
);
}
})
.then(() => ({ shippingMethod, shippingZone, warehouse }));
}
export function createShippingZone(name, country) {
return shippingMethodRequest
.createShippingZone(name, country)
.its("body.data.shippingZoneCreate.shippingZone");
}
export function createWarehouse(name, shippingZoneId, address) {
return warehouseRequest
.createWarehouse(name, shippingZoneId, address)
.its("body.data.createWarehouse.warehouse");
}
export function createShippingRate(name, shippingZoneId) {
return shippingMethodRequest
.createShippingRate(name, shippingZoneId)
.its("body.data.shippingPriceCreate.shippingMethod");
}
export function deleteShippingStartsWith(startsWith) {
cy.deleteElementsStartsWith(
shippingMethodRequest.deleteShippingZone,
shippingMethodRequest.getShippingZones,
startsWith,
"shippingZONE"
);
cy.deleteElementsStartsWith(
warehouseRequest.deleteWarehouse,
warehouseRequest.getWarehouses,
startsWith,
"Warehouse"
);
}
export default ShippingUtils;

View file

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

View file

@ -1,32 +1,34 @@
import ProductDetails from "../../apiRequests/storeFront/ProductDetails";
import Search from "../../apiRequests/storeFront/Search";
import { getProductDetails } from "../../apiRequests/storeFront/ProductDetails";
export const isProductVisible = (productId, channelSlug, name) => {
const productDetails = new ProductDetails();
return productDetails
.getProductDetails(productId, channelSlug)
.then(productDetailsResp => {
const product = productDetailsResp.body.data.product;
return product !== null && product.name === name;
});
export const isProductVisible = (resp, name) => {
const product = resp.body.data.product;
return product !== null && product.name === name;
};
export const isProductAvailableForPurchase = (productId, channelSlug) => {
const productDetails = new ProductDetails();
return productDetails
.getProductDetails(productId, channelSlug)
.then(
productDetailsResp =>
productDetailsResp.body.data.product.isAvailableForPurchase
);
export const isProductAvailableForPurchase = resp => {
const product = resp.body.data.product;
return product.isAvailableForPurchase;
};
export const isProductVisibleInSearchResult = (productName, channelSlug) => {
const search = new Search();
return search
.searchInShop(productName, channelSlug)
.then(
resp =>
resp.body.data.products.totalCount !== 0 &&
resp.body.data.products.edges[0].node.name === productName
);
export const isProductVisibleInSearchResult = (resp, productName) => {
const productsList = resp.body.data.products;
return (
productsList.totalCount !== 0 &&
productsList.edges[0].node.name === productName
);
};
export const getProductVariants = (productId, channelSlug) => {
getProductDetails(productId, channelSlug).then(resp => {
const variantsList = resp.body.data.product.variants;
return variantsList.map(element => ({
name: element.name,
price: element.pricing.price.gross.amount
}));
});
};
export const getProductPrice = (productId, channelSlug) =>
getProductDetails(productId, channelSlug).then(
resp => resp.body.data.product.variants[0].pricing.price.gross.amount
);

View file

@ -3,6 +3,10 @@
"context": "dialog header",
"string": "Cancel Order"
},
"Previous discount label id": {
"context": "Previous discount label",
"string": "Previous discount value"
},
"amount title": {
"context": "amount title",
"string": "Refunded amount"
@ -50,6 +54,14 @@
"configurationPluginsPages": {
"string": "View and update your plugins and their settings."
},
"discount value id": {
"context": "discount value",
"string": "discount"
},
"discount value label id": {
"context": "discount value label",
"string": "discount value"
},
"event products list title refunded": {
"context": "refunded products list title",
"string": "Products refunded"
@ -74,6 +86,26 @@
"context": "order marked as paid event title",
"string": "Order was marked as paid by"
},
"event title order discount auto updated": {
"context": "order discount was updated automatically event title",
"string": "Order discount was updated automatically updated"
},
"event title order discount updated": {
"context": "order discount was updated event title",
"string": "Order discount was updated by"
},
"event title order discounted": {
"context": "order was discounted event title",
"string": "Order was discounted by"
},
"event title order line discount added": {
"context": "order line discount added title",
"string": "{productName} discount was added by "
},
"event title order line discount updated": {
"context": "order line discount updated title",
"string": "{productName} discount was updated by "
},
"event title refunded": {
"context": "refunded event title",
"string": "Products were refunded by "
@ -86,6 +118,10 @@
"context": "returned event title",
"string": "Products were returned by"
},
"fixed amount subtitle id": {
"context": "Fixed amount subtitle",
"string": "Fixed amount"
},
"homeActivityCardHeader": {
"context": "header",
"string": "Activity"
@ -235,6 +271,10 @@
"menuPropertiesMenuTitle": {
"string": "Menu Title"
},
"new discount label id": {
"context": "new discount label",
"string": "New discount value"
},
"orderCustomerBillingAddressNotSet": {
"context": "no address is set in draft order",
"string": "Not set"
@ -1839,10 +1879,6 @@
"src_dot_components_dot_ErrorPage_dot_3182212440": {
"string": "We've encountered a problem..."
},
"src_dot_components_dot_FileUpload_dot_3050254265": {
"context": "upload file, button",
"string": "Upload"
},
"src_dot_components_dot_FilterBar_dot_2173195312": {
"context": "button",
"string": "Delete Search"
@ -3239,6 +3275,38 @@
"context": "return button",
"string": "Return / Replace order"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_buttonLabel": {
"context": "add button label",
"string": "Add"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_discountReasonLabel": {
"context": "discount reason input lavel",
"string": "Reason"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_discountValueLabel": {
"context": "value input label",
"string": "Discount value"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_fixedAmountOption": {
"context": "fixed amount",
"string": "Fixed Amount"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_invalidValue": {
"context": "value input helper text",
"string": "Invalid value"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_itemDiscountTitle": {
"context": "dialog title item discount",
"string": "Discount Item"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_orderDiscountTitle": {
"context": "dialog title order discount",
"string": "Discount this Order by:"
},
"src_dot_orders_dot_components_dot_OrderDiscountCommonModal_dot_percentageOption": {
"context": "percentage option",
"string": "Percentage"
},
"src_dot_orders_dot_components_dot_OrderDraftCancelDialog_dot_1961675716": {
"context": "dialog header",
"string": "Delete Daft Order"
@ -3264,22 +3332,36 @@
"context": "total price of ordered products",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_2429341469": {
"context": "button",
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_addCustomerInfo": {
"context": "add customer first label",
"string": "add customer first"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_addDiscount": {
"context": "add discount button",
"string": "Add Discount"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_addShippingCarrier": {
"context": "add shipping carrier button",
"string": "Add shipping carrier"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_3202709354": {
"string": "Taxes (VAT included)"
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_discount": {
"context": "discount button",
"string": "Discount"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_3594442178": {
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_noShippingCarriers": {
"context": "no shipping carriers title",
"string": "No applicable shipping carriers"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_781550514": {
"context": "subtotal price or an order",
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_subtotal": {
"context": "subtotal price",
"string": "Subtotal"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_878013594": {
"context": "total price of an order",
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_taxes": {
"context": "taxes title",
"string": "Taxes (VAT included)"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_total": {
"context": "total price",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderDraftDetails_dot_2343989342": {
@ -3580,6 +3662,10 @@
"context": "order history message",
"string": "Order was cancelled"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_ExtendedDiscountTimelineEvent_dot_reasonLabel": {
"context": "reason for discount label",
"string": "Reason for discount"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_description": {
"context": "replacement created order history message description",
"string": "was created for replaced products"
@ -3588,6 +3674,14 @@
"context": "replacement created order history message draft number",
"string": "Draft #{orderNumber} "
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_orderDiscountRemoved": {
"context": "order discount removed title",
"string": "Order discount was removed by "
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_productDiscountRemoved": {
"context": "product discount removed title",
"string": "{productName} discount was removed by"
},
"src_dot_orders_dot_components_dot_OrderInvoiceEmailSendDialog_dot_1821123638": {
"string": "Are you sure you want to send this invoice: {invoiceNumber} to the customer?"
},
@ -6740,10 +6834,18 @@
"context": "section header",
"string": "Events"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_144149815": {
"context": "event",
"string": "Product variant deleted"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_1606361075": {
"context": "event",
"string": "Order updated"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2126893364": {
"context": "event",
"string": "Product variant updated"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2240725235": {
"context": "event",
"string": "Checkout created"
@ -6768,6 +6870,10 @@
"context": "webhook events",
"string": "Expand or restrict webhooks permissions to register certain events in Saleor system."
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3285325968": {
"context": "event",
"string": "Product variant created"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3316426878": {
"context": "event",
"string": "Product updated"

29795
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -26,8 +26,8 @@
"@material-ui/icons": "^4.5.1",
"@material-ui/styles": "^4.5.2",
"@saleor/macaw-ui": "^0.1.1-9",
"@types/faker": "^5.1.6",
"@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6",
"apollo": "^2.21.2",
"apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8",
@ -45,7 +45,6 @@
"editorjs-undo": "^0.1.4",
"faker": "^5.1.0",
"fast-array-diff": "^0.2.0",
"fsevents": "^1.2.9",
"fuzzaldrin": "^2.1.0",
"graphql": "^14.4.2",
"graphql-tag": "^2.11.0",
@ -76,7 +75,7 @@
"react-sortable-tree": "^2.6.2",
"semver-compare": "^1.0.0",
"slugify": "^1.4.6",
"typescript": "^3.9.7",
"typescript": "^4.2.3",
"url-join": "^4.0.1",
"use-react-router": "^1.0.7"
},
@ -91,7 +90,7 @@
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
"@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.7.4",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.7.6",
"@pollyjs/adapter-node-http": "^5.0.0",
"@pollyjs/core": "^5.0.0",
@ -123,8 +122,8 @@
"@types/storybook__react": "^4.0.2",
"@types/url-join": "^4.0.0",
"@types/webappsec-credential-management": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^2.12.0",
"@typescript-eslint/parser": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^4.16.1",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.6",

2
react-intl.d.ts vendored
View file

@ -13,7 +13,7 @@ declare module "react-intl" {
MessageDescriptor
>;
type PrimitiveType = string | number | boolean | null | undefined | Date;
type FormatXMLElementFn = (...args: any[]) => string | object;
type FormatXMLElementFn = (...args: any[]) => string | {};
export interface IntlFormatters
extends Omit<ReactIntl.IntlFormatters, "formatMessage"> {
formatMessage(

View file

@ -1644,7 +1644,6 @@ type DeletePrivateMetadata {
type DigitalContent implements Node & ObjectWithMetadata {
useDefaultSettings: Boolean!
automaticFulfillment: Boolean!
productVariant: ProductVariant!
contentFile: String!
maxDownloads: Int
urlValidDays: Int
@ -1652,6 +1651,7 @@ type DigitalContent implements Node & ObjectWithMetadata {
id: ID!
privateMetadata: [MetadataItem]!
metadata: [MetadataItem]!
productVariant: ProductVariant!
}
type DigitalContentCountableConnection {
@ -2685,6 +2685,11 @@ type Mutation {
orderLinesCreate(id: ID!, input: [OrderLineCreateInput]!): OrderLinesCreate
orderLineDelete(id: ID!): OrderLineDelete
orderLineUpdate(id: ID!, input: OrderLineInput!): OrderLineUpdate
orderDiscountAdd(input: OrderDiscountCommonInput!, orderId: ID!): OrderDiscountAdd
orderDiscountUpdate(discountId: ID!, input: OrderDiscountCommonInput!): OrderDiscountUpdate
orderDiscountDelete(discountId: ID!): OrderDiscountDelete
orderLineDiscountUpdate(input: OrderDiscountCommonInput!, orderLineId: ID!): OrderLineDiscountUpdate
orderLineDiscountRemove(orderLineId: ID!): OrderLineDiscountRemove
orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid
orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund
orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate
@ -2860,9 +2865,6 @@ type Order implements Node & ObjectWithMetadata {
token: String!
voucher: Voucher
giftCards: [GiftCard]
discount: Money
discountName: String
translatedDiscountName: String
displayGrossPrices: Boolean!
customerNote: String!
weight: Weight
@ -2880,6 +2882,7 @@ type Order implements Node & ObjectWithMetadata {
paymentStatusDisplay: String!
payments: [Payment]
total: TaxedMoney!
undiscountedTotal: TaxedMoney!
subtotal: TaxedMoney!
statusDisplay: String
canFinalize: Boolean!
@ -2889,6 +2892,10 @@ type Order implements Node & ObjectWithMetadata {
totalBalance: Money!
userEmail: String
isShippingRequired: Boolean!
discount: Money @deprecated(reason: "Use discounts field. This field will be removed in Saleor 4.0.")
discountName: String @deprecated(reason: "Use discounts field. This field will be removed in Saleor 4.0.")
translatedDiscountName: String @deprecated(reason: "Use discounts field. This field will be removed in Saleor 4.0.")
discounts: [OrderDiscount!]
}
enum OrderAction {
@ -2949,6 +2956,46 @@ enum OrderDirection {
DESC
}
type OrderDiscount implements Node {
id: ID!
type: OrderDiscountType!
valueType: DiscountValueTypeEnum!
value: PositiveDecimal!
name: String
translatedName: String
reason: String
amount: Money!
}
type OrderDiscountAdd {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
order: Order
orderErrors: [OrderError!]!
}
input OrderDiscountCommonInput {
valueType: DiscountValueTypeEnum!
value: PositiveDecimal!
reason: String
}
type OrderDiscountDelete {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
order: Order
orderErrors: [OrderError!]!
}
enum OrderDiscountType {
VOUCHER
MANUAL
}
type OrderDiscountUpdate {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
order: Order
orderErrors: [OrderError!]!
}
input OrderDraftFilterInput {
customer: String
created: DateRangeInput
@ -2970,6 +3017,7 @@ enum OrderErrorCode {
CANNOT_CANCEL_FULFILLMENT
CANNOT_CANCEL_ORDER
CANNOT_DELETE
CANNOT_DISCOUNT
CANNOT_REFUND
CAPTURE_INACTIVE_PAYMENT
NOT_EDITABLE
@ -3018,6 +3066,7 @@ type OrderEvent implements Node {
transactionReference: String
shippingCostsIncluded: Boolean
relatedOrder: Order
discount: OrderEventDiscountObject
}
type OrderEventCountableConnection {
@ -3031,10 +3080,21 @@ type OrderEventCountableEdge {
cursor: String!
}
type OrderEventDiscountObject {
valueType: DiscountValueTypeEnum!
value: PositiveDecimal!
reason: String
amount: Money
oldValueType: DiscountValueTypeEnum
oldValue: PositiveDecimal
oldAmount: Money
}
type OrderEventOrderLineObject {
quantity: Int
orderLine: OrderLine
itemName: String
discount: OrderEventDiscountObject
}
enum OrderEventsEmailsEnum {
@ -3061,6 +3121,12 @@ enum OrderEventsEnum {
ORDER_MARKED_AS_PAID
ORDER_FULLY_PAID
ORDER_REPLACEMENT_CREATED
ORDER_DISCOUNT_ADDED
ORDER_DISCOUNT_AUTOMATICALLY_UPDATED
ORDER_DISCOUNT_UPDATED
ORDER_DISCOUNT_DELETED
ORDER_LINE_DISCOUNT_UPDATED
ORDER_LINE_DISCOUNT_REMOVED
UPDATED_ADDRESS
EMAIL_SENT
CONFIRMED
@ -3124,15 +3190,20 @@ type OrderLine implements Node {
isShippingRequired: Boolean!
quantity: Int!
quantityFulfilled: Int!
unitDiscountReason: String
taxRate: Float!
digitalContentUrl: DigitalContentUrl
thumbnail(size: Int): Image
unitPrice: TaxedMoney!
undiscountedUnitPrice: TaxedMoney!
unitDiscount: Money!
unitDiscountValue: PositiveDecimal!
totalPrice: TaxedMoney!
variant: ProductVariant
translatedProductName: String!
translatedVariantName: String!
allocations: [Allocation!]
unitDiscountType: DiscountValueTypeEnum
}
input OrderLineCreateInput {
@ -3147,6 +3218,20 @@ type OrderLineDelete {
orderErrors: [OrderError!]!
}
type OrderLineDiscountRemove {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
orderLine: OrderLine
order: Order
orderErrors: [OrderError!]!
}
type OrderLineDiscountUpdate {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
orderLine: OrderLine
order: Order
orderErrors: [OrderError!]!
}
input OrderLineInput {
quantity: Int!
}
@ -4509,7 +4594,7 @@ type Query {
attribute(id: ID, slug: String): Attribute
appsInstallations: [AppInstallation!]!
apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection
app(id: ID!): App
app(id: ID): App
addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData
address(id: ID!): Address
customers(filter: CustomerFilterInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection
@ -4964,6 +5049,7 @@ type Shop {
companyAddress: Address
customerSetPasswordUrl: String
staffNotificationRecipients: [StaffNotificationRecipient]
version: String!
}
type ShopAddressUpdate {
@ -5820,6 +5906,9 @@ enum WebhookEventTypeEnum {
PRODUCT_CREATED
PRODUCT_UPDATED
PRODUCT_DELETED
PRODUCT_VARIANT_CREATED
PRODUCT_VARIANT_UPDATED
PRODUCT_VARIANT_DELETED
CHECKOUT_CREATED
CHECKOUT_UPDATED
FULFILLMENT_CREATED
@ -5843,6 +5932,9 @@ enum WebhookSampleEventTypeEnum {
PRODUCT_CREATED
PRODUCT_UPDATED
PRODUCT_DELETED
PRODUCT_VARIANT_CREATED
PRODUCT_VARIANT_UPDATED
PRODUCT_VARIANT_DELETED
CHECKOUT_CREATED
CHECKOUT_UPDATED
FULFILLMENT_CREATED

View file

@ -0,0 +1,36 @@
import { Typography } from "@material-ui/core";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import HorizontalSpacer from "./HorizontalSpacer";
interface HelperWrapperProps {
children: React.ReactNode;
}
const HelperWrapper: React.FC<HelperWrapperProps> = ({ children }) => (
<div style={{ display: "flex", flexDirection: "row" }}>{children}</div>
);
const Text: React.FC = () => <Typography>{"<- The spacer is here"}</Typography>;
storiesOf("Generic / Horizontal Spacer", module)
.addDecorator(Decorator)
.add("without", () => (
<HelperWrapper>
<Typography>No spacer</Typography>
</HelperWrapper>
))
.add("default", () => (
<HelperWrapper>
<HorizontalSpacer />
<Text />
</HelperWrapper>
))
.add("with bigger spacing provided", () => (
<HelperWrapper>
<HorizontalSpacer spacing={4} />
<Text />
</HelperWrapper>
));

View file

@ -0,0 +1,23 @@
import { makeStyles } from "@material-ui/core";
import React from "react";
export interface HorizontalSpacerProps {
spacing?: number;
}
const useStyles = makeStyles(
theme => ({
container: ({ spacing }: HorizontalSpacerProps) => ({
width: theme.spacing(spacing)
})
}),
{ name: "HorizontalSpacer" }
);
const HorizontalSpacer: React.FC<HorizontalSpacerProps> = ({ spacing = 1 }) => {
const classes = useStyles({ spacing });
return <div className={classes.container} />;
};
export default HorizontalSpacer;

View file

@ -0,0 +1,2 @@
export * from "./HorizontalSpacer";
export { default } from "./HorizontalSpacer";

View file

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

View file

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

View file

@ -1,31 +1,12 @@
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import { makeStyles } from "@material-ui/core/styles";
import { buttonMessages } from "@saleor/intl";
import { DialogProps } from "@saleor/types";
import classNames from "classnames";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, {
ConfirmButtonTransitionState
} from "../ConfirmButton/ConfirmButton";
const useStyles = makeStyles(
theme => ({
deleteButton: {
"&:hover": {
backgroundColor: theme.palette.error.main
},
backgroundColor: theme.palette.error.main,
color: theme.palette.error.contrastText
}
}),
{ name: "ActionDialog" }
);
import { ConfirmButtonTransitionState } from "../ConfirmButton/ConfirmButton";
import DialogButtons from "./DialogButtons";
import { ActionDialogVariant } from "./types";
interface ActionDialogProps extends DialogProps {
children?: React.ReactNode;
@ -34,61 +15,25 @@ interface ActionDialogProps extends DialogProps {
disabled?: boolean;
maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false;
title: string;
variant?: "default" | "delete" | "info";
variant?: ActionDialogVariant;
onConfirm();
}
const ActionDialog: React.FC<ActionDialogProps> = props => {
const {
children,
confirmButtonLabel,
confirmButtonState,
disabled,
open,
title,
variant,
onConfirm,
onClose,
...rest
} = props;
const classes = useStyles(props);
const intl = useIntl();
const { children, open, title, onClose, variant, ...rest } = props;
return (
<Dialog fullWidth onClose={onClose} open={open} {...rest}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>{children}</DialogContent>
<DialogActions>
<Button data-test="back" onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
{variant !== "info" && (
<ConfirmButton
disabled={disabled}
transitionState={confirmButtonState}
color="primary"
variant="contained"
onClick={onConfirm}
className={classNames({
[classes.deleteButton]: variant === "delete"
})}
data-test="submit"
>
{confirmButtonLabel ||
(variant === "delete"
? intl.formatMessage(buttonMessages.delete)
: intl.formatMessage(buttonMessages.confirm))}
</ConfirmButton>
)}
</DialogActions>
<DialogButtons {...rest} onClose={onClose} variant={variant} />
</Dialog>
);
};
ActionDialog.defaultProps = {
maxWidth: "xs",
variant: "default"
maxWidth: "xs"
};
ActionDialog.displayName = "ActionDialog";
export default ActionDialog;

View file

@ -0,0 +1,88 @@
import Button from "@material-ui/core/Button";
import DialogActions from "@material-ui/core/DialogActions";
import { makeStyles } from "@material-ui/core/styles";
import { buttonMessages } from "@saleor/intl";
import classNames from "classnames";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, {
ConfirmButtonTransitionState
} from "../ConfirmButton/ConfirmButton";
import { ActionDialogVariant } from "./types";
const useStyles = makeStyles(
theme => ({
deleteButton: {
"&:hover": {
backgroundColor: theme.palette.error.main
},
backgroundColor: theme.palette.error.main,
color: theme.palette.error.contrastText
}
}),
{ name: "ActionDialog" }
);
interface DialogButtonsProps {
onClose: () => void;
confirmButtonLabel?: string;
confirmButtonState?: ConfirmButtonTransitionState;
disabled?: boolean;
variant?: ActionDialogVariant;
children?: React.ReactNode;
showBackButton?: boolean;
onConfirm();
}
const DialogButtons: React.FC<DialogButtonsProps> = props => {
const {
confirmButtonLabel,
confirmButtonState,
disabled,
variant,
onConfirm,
onClose,
children,
showBackButton = true
} = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<DialogActions>
{children}
{showBackButton && (
<Button data-test="back" onClick={onClose} color="secondary">
<FormattedMessage {...buttonMessages.back} />
</Button>
)}
{variant !== "info" && (
<ConfirmButton
disabled={disabled}
transitionState={confirmButtonState}
color="primary"
variant="contained"
onClick={onConfirm}
className={classNames({
[classes.deleteButton]: variant === "delete"
})}
data-test="submit"
>
{confirmButtonLabel ||
(variant === "delete"
? intl.formatMessage(buttonMessages.delete)
: intl.formatMessage(buttonMessages.confirm))}
</ConfirmButton>
)}
</DialogActions>
);
};
DialogButtons.defaultProps = {
confirmButtonState: "default",
variant: "default"
};
export default DialogButtons;

View file

@ -0,0 +1 @@
export type ActionDialogVariant = "default" | "delete" | "info";

View file

@ -165,7 +165,10 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
);
return (
<TableRow key={product.id}>
<TableRow
key={product.id}
data-test-id="assign-product-table-row"
>
<TableCellAvatar
className={classes.avatar}
thumbnail={maybe(() => product.thumbnail.url)}
@ -202,6 +205,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
data-test="submit"
transitionState={confirmButtonState}
color="primary"
variant="contained"

View file

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

View file

@ -51,15 +51,17 @@ interface ExtendedPageHeaderProps {
className?: string;
inline?: boolean;
title?: React.ReactNode;
testId?: string;
}
const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
const { children, className, inline, title } = props;
const { children, className, inline, title, testId } = props;
const classes = useStyles(props);
return (
<div
data-test-id={testId}
className={classNames(classes.root, className, {
[classes.block]: !inline
})}

View file

@ -1,61 +0,0 @@
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { FormattedMessage } from "react-intl";
const useStyles = makeStyles(
{
fileUploadField: {
display: "none"
},
root: {
display: "flex"
},
textField: {
flex: 1
}
},
{ name: "FileUpload" }
);
interface FileUploadProps {
disabled?: boolean;
name?: string;
value?: any;
onChange?(event: React.ChangeEvent<any>);
}
const FileUpload: React.FC<FileUploadProps> = props => {
const { disabled, name, value, onChange } = props;
const classes = useStyles(props);
return (
<div className={classes.root}>
<input
disabled={disabled}
name={name}
onChange={onChange}
ref={ref => (this.upload = ref)}
className={classes.fileUploadField}
type="file"
value={value}
/>
<TextField
className={classes.textField}
disabled={disabled}
onChange={undefined}
value={value}
/>
<Button disabled={disabled} onClick={() => this.upload.click()}>
<FormattedMessage
defaultMessage="Upload"
description="upload file, button"
/>
</Button>
</div>
);
};
FileUpload.displayName = "FileUpload";
export default FileUpload;

View file

@ -1,2 +0,0 @@
export { default } from "./FileUpload";
export * from "./FileUpload";

View file

@ -1,12 +1,13 @@
import { makeStyles } from "@material-ui/core/styles";
import Typography, { TypographyProps } from "@material-ui/core/Typography";
import { ITheme } from "@saleor/theme";
import classNames from "classnames";
import React from "react";
const useStyles = makeStyles(
theme => ({
(theme: ITheme) => ({
primary: {
color: theme.palette.primary.main
color: theme.palette.textHighlighted.active
},
root: {
cursor: "pointer",
@ -17,6 +18,10 @@ const useStyles = makeStyles(
},
underline: {
textDecoration: "underline"
},
disabled: {
cursor: "default",
color: theme.palette.textHighlighted.inactive
}
}),
{ name: "Link" }
@ -27,6 +32,7 @@ interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
underline?: boolean;
typographyProps?: TypographyProps;
onClick: () => void;
disabled?: boolean;
}
const Link: React.FC<LinkProps> = props => {
@ -36,6 +42,7 @@ const Link: React.FC<LinkProps> = props => {
color = "primary",
underline = false,
onClick,
disabled,
...linkProps
} = props;
@ -47,9 +54,14 @@ const Link: React.FC<LinkProps> = props => {
className={classNames(className, {
[classes.root]: true,
[classes[color]]: true,
[classes.underline]: underline
[classes.underline]: underline,
[classes.disabled]: disabled
})}
onClick={event => {
if (disabled) {
return;
}
event.preventDefault();
onClick();
}}

View file

@ -38,6 +38,7 @@ const PageHeader: React.FC<PageHeaderProps> = props => {
return (
<ExtendedPageHeader
testId="page-header"
className={className}
inline={inline}
title={

View file

@ -59,13 +59,14 @@ interface RadioGroupFieldProps {
alignTop?: boolean;
choices: RadioGroupFieldChoice[];
className?: string;
innerContainerClassName?: string;
disabled?: boolean;
error?: boolean;
hint?: string;
label?: React.ReactNode;
name?: string;
value: string | number;
variant?: "block" | "inline";
variant?: "block" | "inline" | "inlineJustify";
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -81,7 +82,8 @@ export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
onChange,
name,
hint,
variant = "block"
variant = "block",
innerContainerClassName
} = props;
const classes = useStyles(props);
@ -102,7 +104,8 @@ export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
value={value}
onChange={onChange}
className={classNames({
[classes.radioGroupInline]: variant === "inline"
[classes.radioGroupInline]: variant === "inline",
[innerContainerClassName]: !!innerContainerClassName
})}
>
{choices.length > 0 ? (

View file

@ -38,17 +38,19 @@ interface TabProps<T> {
children?: React.ReactNode;
isActive: boolean;
changeTab: (index: T) => void;
testId?: string;
}
export function Tab<T>(value: T) {
const Component: React.FC<TabProps<T>> = props => {
const { children, isActive, changeTab } = props;
const { children, isActive, changeTab, testId } = props;
const classes = useStyles(props);
return (
<Typography
component="span"
data-test-id={testId}
className={classNames({
[classes.root]: true,
[classes.active]: isActive

View file

@ -2,6 +2,8 @@ export type IThemeColors = Record<
"primary" | "secondary" | "error" | "paperBorder" | "autofill",
string
> & {
highlightInactive: Record<"default", string>;
} & {
background: Record<"default" | "paper", string>;
} & {
checkbox: Record<"default", string>;
@ -31,6 +33,9 @@ export type IThemeColors = Record<
};
export const dark: IThemeColors = {
highlightInactive: {
default: "#78797A"
},
autofill: "#5D5881",
background: {
default: "#1D1E1F",
@ -68,6 +73,9 @@ export const dark: IThemeColors = {
theme: "dark"
};
export const light: IThemeColors = {
highlightInactive: {
default: "#C8C8C8"
},
autofill: "#f4f6c5",
background: {
default: "#EFF5F8",
@ -89,6 +97,7 @@ export const light: IThemeColors = {
default: "#C8C8C8",
disabled: "rgba(216, 216, 216, 0.3)"
},
input: {
border: "#BDBDBD",
default: "#FFFFFF",

View file

@ -60,7 +60,7 @@ export const TimelineEventHeader: React.FC<TimelineEventHeaderProps> = props =>
{title && <Typography>{title}</Typography>}
{titleElements && (
<div className={classes.elementsContainer}>
{titleElements.map(({ text, link }) => {
{titleElements.filter(Boolean).map(({ text, link }) => {
if (link) {
return (
<Link

View file

@ -98,7 +98,11 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
description: "section header"
})}
toolbar={
<Button color="primary" onClick={onProductAssign}>
<Button
color="primary"
onClick={onProductAssign}
data-test-id="assign-products"
>
<FormattedMessage
defaultMessage="Assign products"
description="button"

View file

@ -208,6 +208,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
)}
</CollectionsTab>
<ProductsTab
testId="products-tab"
isActive={activeTab === SaleDetailsPageTab.products}
changeTab={onTabClick}
>

View file

@ -54,7 +54,12 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.sales)}>
<Button onClick={onAdd} variant="contained" color="primary">
<Button
onClick={onAdd}
variant="contained"
color="primary"
data-test-id="create-sale"
>
<FormattedMessage defaultMessage="Create Sale" description="button" />
</Button>
</PageHeader>

View file

@ -303,7 +303,7 @@ export const searchPageProps: SearchPageProps = {
onSearchChange: () => undefined
};
export const filterPageProps: FilterPageProps<string, object> = {
export const filterPageProps: FilterPageProps<string, {}> = {
...searchPageProps,
...tabPageProps,
filterOpts: {},

View file

@ -2,6 +2,7 @@ import gql from "graphql-tag";
import { fragmentAddress } from "./address";
import { metadataFragment } from "./metadata";
import { fragmentMoney } from "./products";
export const fragmentOrderEvent = gql`
fragment OrderEventFragment on OrderEvent {
@ -12,6 +13,21 @@ export const fragmentOrderEvent = gql`
email
emailType
invoiceNumber
discount {
valueType
value
reason
amount {
amount
currency
}
oldValueType
oldValue
oldAmount {
amount
currency
}
}
relatedOrder {
id
number
@ -28,6 +44,22 @@ export const fragmentOrderEvent = gql`
}
lines {
quantity
itemName
discount {
valueType
value
reason
amount {
amount
currency
}
oldValueType
oldValue
oldAmount {
amount
currency
}
}
orderLine {
id
productName
@ -49,6 +81,24 @@ export const fragmentOrderLine = gql`
productSku
quantity
quantityFulfilled
unitDiscount {
amount
currency
}
unitDiscountValue
unitDiscountReason
unitDiscountType
undiscountedUnitPrice {
currency
gross {
amount
currency
}
net {
amount
currency
}
}
unitPrice {
gross {
amount
@ -119,15 +169,27 @@ export const fragmentOrderDetails = gql`
${fulfillmentFragment}
${invoiceFragment}
${metadataFragment}
${fragmentMoney}
fragment OrderDetailsFragment on Order {
id
...MetadataFragment
billingAddress {
...AddressFragment
}
isShippingRequired
canFinalize
created
customerNote
discounts {
id
type
calculationMode: valueType
value
reason
amount {
...Money
}
}
events {
...OrderEventFragment
}
@ -155,28 +217,37 @@ export const fragmentOrderDetails = gql`
status
subtotal {
gross {
amount
currency
...Money
}
net {
...Money
}
}
total {
gross {
amount
currency
...Money
}
net {
...Money
}
tax {
amount
currency
...Money
}
}
actions
totalAuthorized {
amount
currency
...Money
}
totalCaptured {
amount
currency
...Money
}
undiscountedTotal {
net {
...Money
}
gross {
...Money
}
}
user {
id
@ -187,13 +258,11 @@ export const fragmentOrderDetails = gql`
id
name
price {
amount
currency
...Money
}
}
discount {
amount
currency
...Money
}
invoices {
...InvoiceFragment

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { FulfillmentStatus } from "./../../types/globalTypes";
import { DiscountValueTypeEnum, FulfillmentStatus } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: FulfillmentFragment
@ -14,6 +14,31 @@ export interface FulfillmentFragment_lines_orderLine_variant {
quantityAvailable: number;
}
export interface FulfillmentFragment_lines_orderLine_unitDiscount {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillmentFragment_lines_orderLine_undiscountedUnitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillmentFragment_lines_orderLine_undiscountedUnitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillmentFragment_lines_orderLine_undiscountedUnitPrice {
__typename: "TaxedMoney";
currency: string;
gross: FulfillmentFragment_lines_orderLine_undiscountedUnitPrice_gross;
net: FulfillmentFragment_lines_orderLine_undiscountedUnitPrice_net;
}
export interface FulfillmentFragment_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
@ -46,6 +71,11 @@ export interface FulfillmentFragment_lines_orderLine {
productSku: string;
quantity: number;
quantityFulfilled: number;
unitDiscount: FulfillmentFragment_lines_orderLine_unitDiscount;
unitDiscountValue: any;
unitDiscountReason: string | null;
unitDiscountType: DiscountValueTypeEnum | null;
undiscountedUnitPrice: FulfillmentFragment_lines_orderLine_undiscountedUnitPrice;
unitPrice: FulfillmentFragment_lines_orderLine_unitPrice;
thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null;
}

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction, JobStatusEnum } from "./../../types/globalTypes";
import { OrderDiscountType, DiscountValueTypeEnum, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction, JobStatusEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: OrderDetailsFragment
@ -42,6 +42,45 @@ export interface OrderDetailsFragment_billingAddress {
streetAddress2: string;
}
export interface OrderDetailsFragment_discounts_amount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_discounts {
__typename: "OrderDiscount";
id: string;
type: OrderDiscountType;
calculationMode: DiscountValueTypeEnum;
value: any;
reason: string | null;
amount: OrderDetailsFragment_discounts_amount;
}
export interface OrderDetailsFragment_events_discount_amount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_events_discount_oldAmount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_events_discount {
__typename: "OrderEventDiscountObject";
valueType: DiscountValueTypeEnum;
value: any;
reason: string | null;
amount: OrderDetailsFragment_events_discount_amount | null;
oldValueType: DiscountValueTypeEnum | null;
oldValue: any | null;
oldAmount: OrderDetailsFragment_events_discount_oldAmount | null;
}
export interface OrderDetailsFragment_events_relatedOrder {
__typename: "Order";
id: string;
@ -56,6 +95,29 @@ export interface OrderDetailsFragment_events_user {
lastName: string;
}
export interface OrderDetailsFragment_events_lines_discount_amount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_events_lines_discount_oldAmount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_events_lines_discount {
__typename: "OrderEventDiscountObject";
valueType: DiscountValueTypeEnum;
value: any;
reason: string | null;
amount: OrderDetailsFragment_events_lines_discount_amount | null;
oldValueType: DiscountValueTypeEnum | null;
oldValue: any | null;
oldAmount: OrderDetailsFragment_events_lines_discount_oldAmount | null;
}
export interface OrderDetailsFragment_events_lines_orderLine {
__typename: "OrderLine";
id: string;
@ -66,6 +128,8 @@ export interface OrderDetailsFragment_events_lines_orderLine {
export interface OrderDetailsFragment_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
itemName: string | null;
discount: OrderDetailsFragment_events_lines_discount | null;
orderLine: OrderDetailsFragment_events_lines_orderLine | null;
}
@ -78,6 +142,7 @@ export interface OrderDetailsFragment_events {
email: string | null;
emailType: OrderEventsEmailsEnum | null;
invoiceNumber: string | null;
discount: OrderDetailsFragment_events_discount | null;
relatedOrder: OrderDetailsFragment_events_relatedOrder | null;
message: string | null;
quantity: number | null;
@ -93,6 +158,31 @@ export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant {
quantityAvailable: number;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_unitDiscount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice {
__typename: "TaxedMoney";
currency: string;
gross: OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice_gross;
net: OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice_net;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
@ -125,6 +215,11 @@ export interface OrderDetailsFragment_fulfillments_lines_orderLine {
productSku: string;
quantity: number;
quantityFulfilled: number;
unitDiscount: OrderDetailsFragment_fulfillments_lines_orderLine_unitDiscount;
unitDiscountValue: any;
unitDiscountReason: string | null;
unitDiscountType: DiscountValueTypeEnum | null;
undiscountedUnitPrice: OrderDetailsFragment_fulfillments_lines_orderLine_undiscountedUnitPrice;
unitPrice: OrderDetailsFragment_fulfillments_lines_orderLine_unitPrice;
thumbnail: OrderDetailsFragment_fulfillments_lines_orderLine_thumbnail | null;
}
@ -158,6 +253,31 @@ export interface OrderDetailsFragment_lines_variant {
quantityAvailable: number;
}
export interface OrderDetailsFragment_lines_unitDiscount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_lines_undiscountedUnitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_lines_undiscountedUnitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_lines_undiscountedUnitPrice {
__typename: "TaxedMoney";
currency: string;
gross: OrderDetailsFragment_lines_undiscountedUnitPrice_gross;
net: OrderDetailsFragment_lines_undiscountedUnitPrice_net;
}
export interface OrderDetailsFragment_lines_unitPrice_gross {
__typename: "Money";
amount: number;
@ -190,6 +310,11 @@ export interface OrderDetailsFragment_lines {
productSku: string;
quantity: number;
quantityFulfilled: number;
unitDiscount: OrderDetailsFragment_lines_unitDiscount;
unitDiscountValue: any;
unitDiscountReason: string | null;
unitDiscountType: DiscountValueTypeEnum | null;
undiscountedUnitPrice: OrderDetailsFragment_lines_undiscountedUnitPrice;
unitPrice: OrderDetailsFragment_lines_unitPrice;
thumbnail: OrderDetailsFragment_lines_thumbnail | null;
}
@ -238,9 +363,16 @@ export interface OrderDetailsFragment_subtotal_gross {
currency: string;
}
export interface OrderDetailsFragment_subtotal_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_subtotal {
__typename: "TaxedMoney";
gross: OrderDetailsFragment_subtotal_gross;
net: OrderDetailsFragment_subtotal_net;
}
export interface OrderDetailsFragment_total_gross {
@ -249,6 +381,12 @@ export interface OrderDetailsFragment_total_gross {
currency: string;
}
export interface OrderDetailsFragment_total_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_total_tax {
__typename: "Money";
amount: number;
@ -258,6 +396,7 @@ export interface OrderDetailsFragment_total_tax {
export interface OrderDetailsFragment_total {
__typename: "TaxedMoney";
gross: OrderDetailsFragment_total_gross;
net: OrderDetailsFragment_total_net;
tax: OrderDetailsFragment_total_tax;
}
@ -273,6 +412,24 @@ export interface OrderDetailsFragment_totalCaptured {
currency: string;
}
export interface OrderDetailsFragment_undiscountedTotal_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_undiscountedTotal_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_undiscountedTotal {
__typename: "TaxedMoney";
net: OrderDetailsFragment_undiscountedTotal_net;
gross: OrderDetailsFragment_undiscountedTotal_gross;
}
export interface OrderDetailsFragment_user {
__typename: "User";
id: string;
@ -321,9 +478,11 @@ export interface OrderDetailsFragment {
metadata: (OrderDetailsFragment_metadata | null)[];
privateMetadata: (OrderDetailsFragment_privateMetadata | null)[];
billingAddress: OrderDetailsFragment_billingAddress | null;
isShippingRequired: boolean;
canFinalize: boolean;
created: any;
customerNote: string;
discounts: OrderDetailsFragment_discounts[] | null;
events: (OrderDetailsFragment_events | null)[] | null;
fulfillments: (OrderDetailsFragment_fulfillments | null)[];
lines: (OrderDetailsFragment_lines | null)[];
@ -339,6 +498,7 @@ export interface OrderDetailsFragment {
actions: (OrderAction | null)[];
totalAuthorized: OrderDetailsFragment_totalAuthorized;
totalCaptured: OrderDetailsFragment_totalCaptured;
undiscountedTotal: OrderDetailsFragment_undiscountedTotal;
user: OrderDetailsFragment_user | null;
userEmail: string | null;
availableShippingMethods: (OrderDetailsFragment_availableShippingMethods | null)[] | null;

View file

@ -2,12 +2,35 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { OrderEventsEmailsEnum, OrderEventsEnum } from "./../../types/globalTypes";
import { OrderEventsEmailsEnum, DiscountValueTypeEnum, OrderEventsEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: OrderEventFragment
// ====================================================
export interface OrderEventFragment_discount_amount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderEventFragment_discount_oldAmount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderEventFragment_discount {
__typename: "OrderEventDiscountObject";
valueType: DiscountValueTypeEnum;
value: any;
reason: string | null;
amount: OrderEventFragment_discount_amount | null;
oldValueType: DiscountValueTypeEnum | null;
oldValue: any | null;
oldAmount: OrderEventFragment_discount_oldAmount | null;
}
export interface OrderEventFragment_relatedOrder {
__typename: "Order";
id: string;
@ -22,6 +45,29 @@ export interface OrderEventFragment_user {
lastName: string;
}
export interface OrderEventFragment_lines_discount_amount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderEventFragment_lines_discount_oldAmount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderEventFragment_lines_discount {
__typename: "OrderEventDiscountObject";
valueType: DiscountValueTypeEnum;
value: any;
reason: string | null;
amount: OrderEventFragment_lines_discount_amount | null;
oldValueType: DiscountValueTypeEnum | null;
oldValue: any | null;
oldAmount: OrderEventFragment_lines_discount_oldAmount | null;
}
export interface OrderEventFragment_lines_orderLine {
__typename: "OrderLine";
id: string;
@ -32,6 +78,8 @@ export interface OrderEventFragment_lines_orderLine {
export interface OrderEventFragment_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
itemName: string | null;
discount: OrderEventFragment_lines_discount | null;
orderLine: OrderEventFragment_lines_orderLine | null;
}
@ -44,6 +92,7 @@ export interface OrderEventFragment {
email: string | null;
emailType: OrderEventsEmailsEnum | null;
invoiceNumber: string | null;
discount: OrderEventFragment_discount | null;
relatedOrder: OrderEventFragment_relatedOrder | null;
message: string | null;
quantity: number | null;

View file

@ -2,6 +2,8 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { DiscountValueTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: OrderLineFragment
// ====================================================
@ -12,6 +14,31 @@ export interface OrderLineFragment_variant {
quantityAvailable: number;
}
export interface OrderLineFragment_unitDiscount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderLineFragment_undiscountedUnitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderLineFragment_undiscountedUnitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderLineFragment_undiscountedUnitPrice {
__typename: "TaxedMoney";
currency: string;
gross: OrderLineFragment_undiscountedUnitPrice_gross;
net: OrderLineFragment_undiscountedUnitPrice_net;
}
export interface OrderLineFragment_unitPrice_gross {
__typename: "Money";
amount: number;
@ -44,6 +71,11 @@ export interface OrderLineFragment {
productSku: string;
quantity: number;
quantityFulfilled: number;
unitDiscount: OrderLineFragment_unitDiscount;
unitDiscountValue: any;
unitDiscountReason: string | null;
unitDiscountType: DiscountValueTypeEnum | null;
undiscountedUnitPrice: OrderLineFragment_undiscountedUnitPrice;
unitPrice: OrderLineFragment_unitPrice;
thumbnail: OrderLineFragment_thumbnail | null;
}

View file

@ -4,20 +4,28 @@ import gql from "graphql-tag";
import { Home, HomeVariables } from "./types/Home";
const home = gql`
query Home($channel: String!) {
salesToday: ordersTotal(period: TODAY, channel: $channel) {
query Home(
$channel: String!
$PERMISSION_MANAGE_PRODUCTS: Boolean!
$PERMISSION_MANAGE_ORDERS: Boolean!
) {
salesToday: ordersTotal(period: TODAY, channel: $channel)
@include(if: $PERMISSION_MANAGE_ORDERS) {
gross {
amount
currency
}
}
ordersToday: orders(created: TODAY, channel: $channel) {
ordersToday: orders(created: TODAY, channel: $channel)
@include(if: $PERMISSION_MANAGE_ORDERS) {
totalCount
}
ordersToFulfill: orders(status: READY_TO_FULFILL, channel: $channel) {
ordersToFulfill: orders(status: READY_TO_FULFILL, channel: $channel)
@include(if: $PERMISSION_MANAGE_ORDERS) {
totalCount
}
ordersToCapture: orders(status: READY_TO_CAPTURE, channel: $channel) {
ordersToCapture: orders(status: READY_TO_CAPTURE, channel: $channel)
@include(if: $PERMISSION_MANAGE_ORDERS) {
totalCount
}
productsOutOfStock: products(
@ -30,7 +38,7 @@ const home = gql`
period: TODAY
first: 5
channel: $channel
) {
) @include(if: $PERMISSION_MANAGE_PRODUCTS) {
edges {
node {
id
@ -57,7 +65,8 @@ const home = gql`
}
}
}
activities: homepageEvents(last: 10) {
activities: homepageEvents(last: 10)
@include(if: $PERMISSION_MANAGE_ORDERS) {
edges {
node {
amount

View file

@ -136,4 +136,6 @@ export interface Home {
export interface HomeVariables {
channel: string;
PERMISSION_MANAGE_PRODUCTS: boolean;
PERMISSION_MANAGE_ORDERS: boolean;
}

Some files were not shown because too many files have changed in this diff Show more