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" "default": "array-simple"
} }
], ],
"@typescript-eslint/ban-types": "error", "@typescript-eslint/ban-types": [
"@typescript-eslint/class-name-casing": "error", "error",
{
"extendDefaults": true,
"types": {
"{}": false
}
}
],
"@typescript-eslint/consistent-type-assertions": "error", "@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-definitions": "error", "@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/explicit-member-accessibility": "off", "@typescript-eslint/explicit-member-accessibility": "off",
@ -128,7 +135,7 @@
"no-multiple-empty-lines": "off", "no-multiple-empty-lines": "off",
"no-new-func": "error", "no-new-func": "error",
"no-new-wrappers": "error", "no-new-wrappers": "error",
"no-redeclare": "error", "no-redeclare": "off",
"no-return-await": "error", "no-return-await": "error",
"no-sequences": "error", "no-sequences": "error",
"no-shadow": [ "no-shadow": [
@ -155,7 +162,7 @@
"radix": "error", "radix": "error",
"simple-import-sort/sort": ["error"], "simple-import-sort/sort": ["error"],
"sort-imports": "off", // imports are handled by simple-import-sort/sort "sort-imports": "off", // imports are handled by simple-import-sort/sort
"sort-keys": "warn", "sort-keys": "off",
"space-before-function-paren": "off", "space-before-function-paren": "off",
"spaced-comment": "error", "spaced-comment": "error",
"use-isnan": "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 - Drop descriptionJson and contentJson fields - #950 by @jwm0
- Add error tracking with Sentry adapter - #956 by @jwm0 - Add error tracking with Sentry adapter - #956 by @jwm0
- Add OAuth2 login with OpenID support - #963 by @orzechdev - 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 - Fix no channels crash - #984 by @dominik-zeglen
- Update webhooks - #982 by @piotrgrundas - Update webhooks - #982 by @piotrgrundas
- Fix trigger form change when collections are being added to list of product collections - #987 by @gax97 - 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 - Unconfirmed order manipulation - #967 by @tomaszszymanski129
# 2.11.1 # 2.11.1

View file

@ -1,13 +1,16 @@
class Attribute { export function createAttribute(name, attributeValues = ["value"]) {
createAttribute(name) { const values = attributeValues.map(element => `{name:"${element}"}`);
const mutation = `mutation{ const mutation = `mutation{
attributeCreate(input:{ attributeCreate(input:{
name:"${name}" name:"${name}"
valueRequired:false valueRequired:false
type:PRODUCT_TYPE type:PRODUCT_TYPE
values: [${values}]
}){ }){
attribute{ attribute{
id id
name
values{name}
} }
attributeErrors{ attributeErrors{
field field
@ -16,9 +19,9 @@ class Attribute {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getAttributes(first, search) { export function getAttributes(first, search) {
const mutation = `query{ const mutation = `query{
attributes(first:${first}, filter:{ attributes(first:${first}, filter:{
search:"${search}" search:"${search}"
@ -34,9 +37,9 @@ class Attribute {
return cy return cy
.sendRequestWithQuery(mutation) .sendRequestWithQuery(mutation)
.then(resp => resp.body.data.attributes.edges); .then(resp => resp.body.data.attributes.edges);
} }
deleteAttribute(attributeId) { export function deleteAttribute(attributeId) {
const mutation = `mutation{ const mutation = `mutation{
attributeDelete(id:"${attributeId}"){ attributeDelete(id:"${attributeId}"){
attributeErrors{ attributeErrors{
@ -46,6 +49,4 @@ class Attribute {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
}
} }
export default Attribute;

View file

@ -1,5 +1,4 @@
class Category { export function createCategory(name, slug = name) {
createCategory(name, slug = name) {
const mutation = `mutation{ const mutation = `mutation{
categoryCreate(input:{name:"${name}", slug: "${slug}"}){ categoryCreate(input:{name:"${name}", slug: "${slug}"}){
productErrors{ productErrors{
@ -12,8 +11,8 @@ class Category {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getCategories(first, search) { export function getCategories(first, search) {
const mutation = `query{ const mutation = `query{
categories(first:${first}, filter:{ categories(first:${first}, filter:{
search:"${search}" search:"${search}"
@ -29,8 +28,8 @@ class Category {
return cy return cy
.sendRequestWithQuery(mutation) .sendRequestWithQuery(mutation)
.then(resp => resp.body.data.categories.edges); .then(resp => resp.body.data.categories.edges);
} }
deleteCategory(categoryId) { export function deleteCategory(categoryId) {
const mutation = `mutation{ const mutation = `mutation{
categoryDelete(id:"${categoryId}"){ categoryDelete(id:"${categoryId}"){
productErrors{ productErrors{
@ -40,6 +39,4 @@ class Category {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
}
} }
export default Category;

View file

@ -1,5 +1,4 @@
class Channels { export function createChannel(isActive, name, slug, currencyCode) {
createChannel(isActive, name, slug, currencyCode) {
const createChannelMutation = `mutation{ const createChannelMutation = `mutation{
channelCreate(input: { channelCreate(input: {
isActive: ${isActive} isActive: ${isActive}
@ -19,8 +18,8 @@ class Channels {
} }
}`; }`;
return cy.sendRequestWithQuery(createChannelMutation); return cy.sendRequestWithQuery(createChannelMutation);
} }
getChannels() { export function getChannels() {
const getChannelsInfoQuery = `query{ const getChannelsInfoQuery = `query{
channels{ channels{
name name
@ -29,12 +28,11 @@ class Channels {
slug slug
currencyCode currencyCode
} }
} }`;
`;
return cy.sendRequestWithQuery(getChannelsInfoQuery); return cy.sendRequestWithQuery(getChannelsInfoQuery);
} }
deleteChannel(channelId, targetChannelId) { export function deleteChannel(channelId, targetChannelId) {
const deleteChannelMutation = `mutation{ const deleteChannelMutation = `mutation{
channelDelete(id: "${channelId}", input:{ channelDelete(id: "${channelId}", input:{
targetChannel: "${targetChannelId}" targetChannel: "${targetChannelId}"
@ -48,6 +46,4 @@ class Channels {
} }
}`; }`;
return cy.sendRequestWithQuery(deleteChannelMutation); return cy.sendRequestWithQuery(deleteChannelMutation);
}
} }
export default Channels;

View file

@ -1,5 +1,9 @@
class Checkout { export function createCheckout(
createCheckout(channelSlug, email, productQuantity, variantsList) { channelSlug,
email,
productQuantity,
variantsList
) {
const lines = variantsList.map( const lines = variantsList.map(
variant => `{quantity:${productQuantity} variant => `{quantity:${productQuantity}
variantId:"${variant.id}"}` variantId:"${variant.id}"}`
@ -21,8 +25,8 @@ class Checkout {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
addShippingMethod(checkoutId, shippingMethodId) { export function addShippingMethod(checkoutId, shippingMethodId) {
const mutation = `mutation{ const mutation = `mutation{
checkoutShippingMethodUpdate(checkoutId:"${checkoutId}", checkoutShippingMethodUpdate(checkoutId:"${checkoutId}",
shippingMethodId:"${shippingMethodId}"){ shippingMethodId:"${shippingMethodId}"){
@ -33,8 +37,8 @@ class Checkout {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
addPayment(checkoutId, gateway, token) { export function addPayment(checkoutId, gateway, token) {
const mutation = `mutation{ const mutation = `mutation{
checkoutPaymentCreate(checkoutId:"${checkoutId}", checkoutPaymentCreate(checkoutId:"${checkoutId}",
input:{ input:{
@ -48,8 +52,8 @@ class Checkout {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
completeCheckout(checkoutId) { export function completeCheckout(checkoutId) {
const mutation = `mutation{ const mutation = `mutation{
checkoutComplete(checkoutId:"${checkoutId}"){ checkoutComplete(checkoutId:"${checkoutId}"){
order{ order{
@ -64,6 +68,4 @@ class Checkout {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); 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,5 +1,4 @@
export class Customer { export function createCustomer(email, customerName, address, isActive = false) {
createCustomer(email, customerName, address, isActive = false) {
const mutation = ` const mutation = `
mutation{ mutation{
customerCreate(input:{ customerCreate(input:{
@ -35,25 +34,24 @@ export class Customer {
message message
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
deleteCustomers(startsWith) { export function deleteCustomersStartsWith(startsWith) {
this.getCustomers(startsWith).then(resp => { getCustomers(startsWith).then(resp => {
if (resp.body.data.customers) { if (resp.body.data.customers) {
const customers = resp.body.data.customers.edges; const customers = resp.body.data.customers.edges;
customers.forEach(element => { customers.forEach(element => {
if (element.node.email.includes(startsWith)) { if (element.node.email.includes(startsWith)) {
this.deleteCustomer(element.node.id); deleteCustomer(element.node.id);
} }
}); });
} }
}); });
} }
deleteCustomer(customerId) { export function deleteCustomer(customerId) {
const mutation = `mutation{ const mutation = `mutation{
customerDelete(id:"${customerId}"){ customerDelete(id:"${customerId}"){
accountErrors{ accountErrors{
@ -63,9 +61,9 @@ export class Customer {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getCustomers(startsWith) { export function getCustomers(startsWith) {
const query = `query{ const query = `query{
customers(first:100, filter: { customers(first:100, filter: {
search: "${startsWith}" search: "${startsWith}"
@ -77,9 +75,6 @@ export class Customer {
} }
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(query); return cy.sendRequestWithQuery(query);
}
} }
export default Customer;

View file

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

View file

@ -1,5 +1,4 @@
class Order { export function markOrderAsPaid(orderId) {
markOrderAsPaid(orderId) {
const mutation = `mutation{ const mutation = `mutation{
orderMarkAsPaid(id:"${orderId}"){ orderMarkAsPaid(id:"${orderId}"){
orderErrors{ orderErrors{
@ -8,9 +7,9 @@ class Order {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
addProductToOrder(orderId, variantId, quantity = 1) { export function addProductToOrder(orderId, variantId, quantity = 1) {
const mutation = `mutation{ const mutation = `mutation{
draftOrderLinesCreate(id:"${orderId}", input:{ draftOrderLinesCreate(id:"${orderId}", input:{
quantity:${quantity} quantity:${quantity}
@ -22,11 +21,10 @@ class Order {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
createDraftOrder(customerId, shippingMethodId, channelId) { export function createDraftOrder(customerId, shippingMethodId, channelId) {
const mutation = ` const mutation = `mutation{
mutation{
draftOrderCreate(input:{ draftOrderCreate(input:{
user:"${customerId}" user:"${customerId}"
shippingMethod:"${shippingMethodId}" shippingMethod:"${shippingMethodId}"
@ -39,11 +37,10 @@ class Order {
id id
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
completeOrder(orderId) { export function completeOrder(orderId) {
const mutation = `mutation{ const mutation = `mutation{
draftOrderComplete(id:"${orderId}"){ draftOrderComplete(id:"${orderId}"){
order{ order{
@ -55,6 +52,4 @@ class Order {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
}
} }
export default Order;

View file

@ -1,8 +1,6 @@
import Utils from "./utils/Utils"; import { getValueWithDefault } from "./utils/Utils";
class Product { export function getFirstProducts(first, search) {
utils = new Utils();
getFirstProducts(first, search) {
const filter = search const filter = search
? `, filter:{ ? `, filter:{
search:"${search}" search:"${search}"
@ -19,20 +17,19 @@ class Product {
} }
} }
} }
} }`;
`;
return cy return cy
.sendRequestWithQuery(query) .sendRequestWithQuery(query)
.then(resp => resp.body.data.products.edges); .then(resp => resp.body.data.products.edges);
} }
updateChannelInProduct({ export function updateChannelInProduct({
productId, productId,
channelId, channelId,
isPublished = true, isPublished = true,
isAvailableForPurchase = true, isAvailableForPurchase = true,
visibleInListings = true visibleInListings = true
}) { }) {
const mutation = `mutation{ const mutation = `mutation{
productChannelListingUpdate(id:"${productId}", productChannelListingUpdate(id:"${productId}",
input:{ input:{
@ -49,10 +46,10 @@ class Product {
} }
} }
}`; }`;
cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
updateChannelPriceInVariant(variantId, channelId) { export function updateChannelPriceInVariant(variantId, channelId) {
const mutation = `mutation{ const mutation = `mutation{
productVariantChannelListingUpdate(id: "${variantId}", input: { productVariantChannelListingUpdate(id: "${variantId}", input: {
channelId: "${channelId}" channelId: "${channelId}"
@ -63,10 +60,10 @@ class Product {
message message
} }
} }
} `; } `;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
createProduct(attributeId, name, productType, category) { export function createProduct(attributeId, name, productType, category) {
const mutation = `mutation{ const mutation = `mutation{
productCreate(input:{ productCreate(input:{
attributes:[{ attributes:[{
@ -78,6 +75,7 @@ class Product {
}){ }){
product{ product{
id id
name
} }
productErrors{ productErrors{
field field
@ -86,9 +84,9 @@ class Product {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
createVariant( export function createVariant({
productId, productId,
sku, sku,
warehouseId, warehouseId,
@ -96,8 +94,8 @@ class Product {
channelId, channelId,
price = 1, price = 1,
costPrice = 1 costPrice = 1
) { }) {
const channelListings = this.utils.getValueWithDefault( const channelListings = getValueWithDefault(
channelId, channelId,
`channelListings:{ `channelListings:{
channelId:"${channelId}" channelId:"${channelId}"
@ -106,7 +104,7 @@ class Product {
}` }`
); );
const stocks = this.utils.getValueWithDefault( const stocks = getValueWithDefault(
warehouseId, warehouseId,
`stocks:{ `stocks:{
warehouse:"${warehouseId}" warehouse:"${warehouseId}"
@ -132,15 +130,16 @@ class Product {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
createTypeProduct(name, attributeId, slug = name) { export function createTypeProduct(name, attributeId, slug = name) {
const mutation = `mutation{ const mutation = `mutation{
productTypeCreate(input: { productTypeCreate(input: {
name: "${name}" name: "${name}"
slug: "${slug}" slug: "${slug}"
isShippingRequired: true isShippingRequired: true
productAttributes: "${attributeId}" productAttributes: "${attributeId}"
variantAttributes: "${attributeId}"
}){ }){
productErrors{ productErrors{
field field
@ -150,11 +149,11 @@ class Product {
id id
} }
} }
} `; } `;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
deleteProduct(productId) { export function deleteProduct(productId) {
const mutation = `mutation{ const mutation = `mutation{
productDelete(id: "${productId}"){ productDelete(id: "${productId}"){
productErrors{ productErrors{
@ -162,11 +161,11 @@ class Product {
message message
} }
} }
} `; } `;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getProductTypes(first, search) { export function getProductTypes(first, search) {
const query = `query{ const query = `query{
productTypes(first:${first}, filter:{ productTypes(first:${first}, filter:{
search:"${search}" search:"${search}"
@ -182,9 +181,9 @@ class Product {
return cy return cy
.sendRequestWithQuery(query) .sendRequestWithQuery(query)
.then(resp => resp.body.data.productTypes.edges); .then(resp => resp.body.data.productTypes.edges);
} }
deleteProductType(productTypeId) { export function deleteProductType(productTypeId) {
const mutation = `mutation{ const mutation = `mutation{
productTypeDelete(id:"${productTypeId}"){ productTypeDelete(id:"${productTypeId}"){
productErrors{ productErrors{
@ -194,7 +193,4 @@ class Product {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
}
} }
export default Product;

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,7 +1,5 @@
class ShippingMethod { export function createShippingRate(name, shippingZone) {
createShippingRate(name, shippingZone) { const mutation = `mutation{
const mutation = `
mutation{
shippingPriceCreate(input:{ shippingPriceCreate(input:{
name: "${name}" name: "${name}"
shippingZone: "${shippingZone}" shippingZone: "${shippingZone}"
@ -11,14 +9,12 @@ class ShippingMethod {
id id
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
createShippingZone(name, country) { export function createShippingZone(name, country) {
const mutation = ` const mutation = `mutation{
mutation{
shippingZoneCreate(input:{ shippingZoneCreate(input:{
name: "${name}" name: "${name}"
countries: "${country}" countries: "${country}"
@ -27,14 +23,12 @@ class ShippingMethod {
id id
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
addChannelToShippingMethod(shippingRateId, channelId, price) { export function addChannelToShippingMethod(shippingRateId, channelId, price) {
const mutation = ` const mutation = `mutation{
mutation{
shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{ shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{
addChannels: { addChannels: {
channelId:"${channelId}" channelId:"${channelId}"
@ -49,12 +43,11 @@ class ShippingMethod {
message message
} }
} }
} }`;
`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
deleteShippingZone(shippingZoneId) { export function deleteShippingZone(shippingZoneId) {
const mutation = `mutation{ const mutation = `mutation{
shippingZoneDelete(id:"${shippingZoneId}"){ shippingZoneDelete(id:"${shippingZoneId}"){
shippingErrors{ shippingErrors{
@ -64,9 +57,9 @@ class ShippingMethod {
} }
`; `;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getShippingZones() { export function getShippingZones() {
const query = `query{ const query = `query{
shippingZones(first:100){ shippingZones(first:100){
edges{ edges{
@ -81,6 +74,4 @@ class ShippingMethod {
return cy return cy
.sendRequestWithQuery(query) .sendRequestWithQuery(query)
.then(resp => resp.body.data.shippingZones.edges); .then(resp => resp.body.data.shippingZones.edges);
}
} }
export default ShippingMethod;

View file

@ -1,5 +1,4 @@
class Warehouse { export function createWarehouse(name, shippingZone, address, slug = name) {
createWarehouse(name, shippingZone, address, slug = name) {
const mutation = `mutation{ const mutation = `mutation{
createWarehouse(input:{ createWarehouse(input:{
name:"${name}" name:"${name}"
@ -20,12 +19,13 @@ class Warehouse {
} }
warehouse{ warehouse{
id id
name
} }
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); return cy.sendRequestWithQuery(mutation);
} }
getWarehouses(first, search) { export function getWarehouses(first, search) {
const query = `query{ const query = `query{
warehouses(first:${first}, filter:{ warehouses(first:${first}, filter:{
search:"${search}" search:"${search}"
@ -41,8 +41,8 @@ class Warehouse {
return cy return cy
.sendRequestWithQuery(query) .sendRequestWithQuery(query)
.then(resp => resp.body.data.warehouses.edges); .then(resp => resp.body.data.warehouses.edges);
} }
deleteWarehouse(warehouseId) { export function deleteWarehouse(warehouseId) {
const mutation = `mutation{ const mutation = `mutation{
deleteWarehouse(id:"${warehouseId}"){ deleteWarehouse(id:"${warehouseId}"){
warehouseErrors{ warehouseErrors{
@ -52,6 +52,4 @@ class Warehouse {
} }
}`; }`;
return cy.sendRequestWithQuery(mutation); 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 { export function getProductDetails(productId, channelId) {
getProductDetails(productId, channelId) {
const query = `fragment BasicProductFields on Product { const query = `fragment BasicProductFields on Product {
id id
name name
} }
fragment Price on TaxedMoney {
gross {
amount
currency
}
}
fragment ProductVariantFields on ProductVariant {
id
sku
name
pricing {
price {
...Price
}
}
}
query ProductDetails{ query ProductDetails{
product(id: "${productId}", channel: "${channelId}") { product(id: "${productId}", channel: "${channelId}") {
...BasicProductFields ...BasicProductFields
variants {
...ProductVariantFields
}
isAvailable isAvailable
isAvailableForPurchase isAvailableForPurchase
availableForPurchase availableForPurchase
} }
}`; }`;
return cy.sendRequestWithQuery(query, "token"); return cy.sendRequestWithQuery(query, "token");
}
} }
export default ProductDetails;

View file

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

View file

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

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 = { export const PRODUCTS_SELECTORS = {
productsList: "[data-test-id][data-test='id']", productsList: "[data-test-id][data-test='id']",
products: "[data-test='submenu-item-label'][data-test-id='products']", products: "[data-test='submenu-item-label'][data-test-id='products']",
@ -23,6 +22,8 @@ export const PRODUCTS_SELECTORS = {
channelAvailabilityList: "ul[role='menu']", channelAvailabilityList: "ul[role='menu']",
goBackButton: "[data-test-id='app-header-back-button']", goBackButton: "[data-test-id='app-header-back-button']",
assignedChannels: "[data-test='channel-availability-item']", assignedChannels: "[data-test='channel-availability-item']",
publishedRadioButton: "[role=radiogroup]",
addVariantsButton: "[data-test*='button-add-variant']",
publishedRadioButtons: "[name*='isPublished']", publishedRadioButtons: "[name*='isPublished']",
availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']", availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
radioButtonsValueTrue: "[value='true']", 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 = { export const CONFIGURATION_SELECTORS = {
channels: "[data-testid='channels']" 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 = { 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 = { export const ORDERS_SELECTORS = {
orders: "[data-test='submenu-item-label'][data-test-id='orders']", 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 = { export const BUTTON_SELECTORS = {
back: '[data-test="back"]', 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" /> // <reference types="cypress" />
import faker from "faker"; 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 { 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 { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors"; import { CHANNEL_FORM_SELECTORS } from "../elements/channels/channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-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 { DRAFT_ORDER_SELECTORS } from "../elements/orders/draft-order-selectors";
import { ORDERS_SELECTORS } from "../elements/orders/orders-selectors"; import { ORDERS_SELECTORS } from "../elements/orders/orders-selectors";
import { BUTTON_SELECTORS } from "../elements/shared/button-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 { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils"; import { deleteChannelsStartsWith } from "../utils/channelsUtils";
describe("Channels", () => { describe("Channels", () => {
const channelStartsWith = "Cypress:"; const channelStartsWith = "Cypress:";
const currency = "PLN"; const currency = "PLN";
const channels = new Channels();
const channelsUtils = new ChannelsUtils();
const channelsSteps = new ChannelsSteps();
before(() => { before(() => {
cy.clearSessionData().loginUserViaRequest(); cy.clearSessionData().loginUserViaRequest();
channelsUtils.deleteChannels(channelStartsWith); deleteChannelsStartsWith(channelStartsWith);
}); });
beforeEach(() => { beforeEach(() => {
@ -48,7 +45,7 @@ describe("Channels", () => {
cy.visit(urlList.channels); cy.visit(urlList.channels);
cy.wait("@Channels"); cy.wait("@Channels");
cy.addAliasToGraphRequest("Channel"); cy.addAliasToGraphRequest("Channel");
channelsSteps.createChannelByView(randomChannel, currency); createChannelByView(randomChannel, currency);
// New channel should be visible in channels list // New channel should be visible in channels list
cy.wait("@Channel") cy.wait("@Channel")
.get(ADD_CHANNEL_FORM_SELECTORS.backToChannelsList) .get(ADD_CHANNEL_FORM_SELECTORS.backToChannelsList)
@ -79,22 +76,18 @@ describe("Channels", () => {
it("should validate slug name", () => { it("should validate slug name", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`; const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency); createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.channels); cy.visit(urlList.channels);
channelsSteps.createChannelByView(randomChannel, currency); createChannelByView(randomChannel, currency);
cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should( cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should(
"be.visible" "be.visible"
); );
}); });
it("should validate currency", () => { it("should validate duplicated currency", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`; const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
cy.visit(urlList.channels); cy.visit(urlList.channels);
channelsSteps.createChannelByView( createChannelByView(randomChannel, "notExistingCurrency");
randomChannel,
currency,
"notExistingCurrency"
);
cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyValidationMessage).should( cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyValidationMessage).should(
"be.visible" "be.visible"
); );
@ -102,7 +95,7 @@ describe("Channels", () => {
it("should delete channel", () => { it("should delete channel", () => {
const randomChannelToDelete = `${channelStartsWith} ${faker.random.number()}`; const randomChannelToDelete = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel( createChannel(
false, false,
randomChannelToDelete, randomChannelToDelete,
randomChannelToDelete, randomChannelToDelete,
@ -111,8 +104,7 @@ describe("Channels", () => {
cy.addAliasToGraphRequest("Channels"); cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels); cy.visit(urlList.channels);
cy.wait("@Channels"); cy.wait("@Channels");
cy.get(CHANNELS_SELECTORS.channelName) cy.contains(CHANNELS_SELECTORS.channelName, randomChannelToDelete)
.contains(randomChannelToDelete)
.parentsUntil(CHANNELS_SELECTORS.channelsTable) .parentsUntil(CHANNELS_SELECTORS.channelsTable)
.find("button") .find("button")
.click(); .click();
@ -127,7 +119,7 @@ describe("Channels", () => {
it("should not be possible to add products to order with inactive channel", () => { it("should not be possible to add products to order with inactive channel", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`; const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency); createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.orders) cy.visit(urlList.orders)
.get(ORDERS_SELECTORS.createOrder) .get(ORDERS_SELECTORS.createOrder)
.click() .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 faker from "faker";
import Customer from "../apiRequests/Customer"; import {
createCustomer,
deleteCustomersStartsWith
} from "../apiRequests/Customer";
import { HOMEPAGE_SELECTORS } from "../elements/homePage/homePage-selectors"; import { HOMEPAGE_SELECTORS } from "../elements/homePage/homePage-selectors";
import HomePageSteps from "../steps/homePageSteps"; import { changeChannel } from "../steps/homePageSteps";
import { urlList } from "../url/urlList"; import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils"; import { getDefaultChannel } from "../utils/channelsUtils";
import HomePageUtils from "../utils/homePageUtils"; import * as homePageUtils from "../utils/homePageUtils";
import OrdersUtils from "../utils/ordersUtils"; import {
import ProductsUtils from "../utils/productsUtils"; createReadyToFulfillOrder,
import ShippingUtils from "../utils/shippingUtils"; createWaitingForCaptureOrder
} from "../utils/ordersUtils";
import * as productsUtils from "../utils/productsUtils";
import * as shippingUtils from "../utils/shippingUtils";
// <reference types="cypress" /> // <reference types="cypress" />
describe("Homepage analytics", () => { describe("Homepage analytics", () => {
const startsWith = "Cy-"; 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 customerId;
let defaultChannel; let defaultChannel;
let createdVariants;
let productType;
let attribute;
let category;
let warehouse;
let shippingMethod;
const productPrice = 22; const productPrice = 22;
const shippingPrice = 12; const shippingPrice = 12;
const randomName = startsWith + faker.random.number(); const randomName = startsWith + faker.random.number();
@ -31,21 +36,18 @@ describe("Homepage analytics", () => {
before(() => { before(() => {
cy.clearSessionData().loginUserViaRequest(); cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith); productsUtils.deleteProductsStartsWith(startsWith);
customer.deleteCustomers(startsWith); deleteCustomersStartsWith(startsWith);
shippingUtils.deleteShipping(startsWith); shippingUtils.deleteShippingStartsWith(startsWith);
let addresses; let addresses;
channelsUtils getDefaultChannel()
.getDefaultChannel()
.then(channel => { .then(channel => {
defaultChannel = channel; defaultChannel = channel;
cy.fixture("addresses"); cy.fixture("addresses");
}) })
.then(addressesFixture => (addresses = addressesFixture)) .then(addressesFixture => (addresses = addressesFixture))
.then(() => .then(() => createCustomer(randomEmail, randomName, addresses.plAddress))
customer.createCustomer(randomEmail, randomName, addresses.plAddress)
)
.then(resp => { .then(resp => {
customerId = resp.body.data.customerCreate.user.id; customerId = resp.body.data.customerCreate.user.id;
shippingUtils.createShipping({ shippingUtils.createShipping({
@ -55,14 +57,22 @@ describe("Homepage analytics", () => {
price: shippingPrice price: shippingPrice
}); });
}) })
.then(() => { .then(
({ warehouse: warehouseResp, shippingMethod: shippingMethodResp }) => {
warehouse = warehouseResp;
shippingMethod = shippingMethodResp;
productsUtils.createTypeAttributeAndCategoryForProduct(randomName); productsUtils.createTypeAttributeAndCategoryForProduct(randomName);
}) }
.then(() => { )
const warehouse = shippingUtils.getWarehouse(); .then(
const productType = productsUtils.getProductType(); ({
const attribute = productsUtils.getAttribute(); productType: productTypeResp,
const category = productsUtils.getCategory(); attribute: attributeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
productsUtils.createProductInChannel({ productsUtils.createProductInChannel({
name: randomName, name: randomName,
channelId: defaultChannel.id, channelId: defaultChannel.id,
@ -73,6 +83,10 @@ describe("Homepage analytics", () => {
categoryId: category.id, categoryId: category.id,
price: productPrice price: productPrice
}); });
}
)
.then(({ variants: variantsResp }) => {
createdVariants = variantsResp;
}); });
}); });
@ -96,11 +110,11 @@ describe("Homepage analytics", () => {
.getOrdersReadyToFulfill(defaultChannel.slug) .getOrdersReadyToFulfill(defaultChannel.slug)
.as("ordersReadyToFulfill"); .as("ordersReadyToFulfill");
ordersUtils.createReadyToFulfillOrder( createReadyToFulfillOrder(
customerId, customerId,
shippingUtils.getShippingMethod().id, shippingMethod.id,
defaultChannel.id, defaultChannel.id,
productsUtils.getCreatedVariants() createdVariants
); );
cy.get("@ordersReadyToFulfill").then(ordersReadyToFulfillBefore => { cy.get("@ordersReadyToFulfill").then(ordersReadyToFulfillBefore => {
const allOrdersReadyToFulfill = ordersReadyToFulfillBefore + 1; const allOrdersReadyToFulfill = ordersReadyToFulfillBefore + 1;
@ -109,7 +123,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrdersReadyToFulfill}${notANumberRegex}` `${notANumberRegex}${allOrdersReadyToFulfill}${notANumberRegex}`
); );
cy.visit(urlList.homePage); cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name); changeChannel(defaultChannel.name);
cy.contains( cy.contains(
HOMEPAGE_SELECTORS.ordersReadyToFulfill, HOMEPAGE_SELECTORS.ordersReadyToFulfill,
ordersReadyToFulfillRegexp ordersReadyToFulfillRegexp
@ -120,13 +134,12 @@ describe("Homepage analytics", () => {
homePageUtils homePageUtils
.getOrdersReadyForCapture(defaultChannel.slug) .getOrdersReadyForCapture(defaultChannel.slug)
.as("ordersReadyForCapture"); .as("ordersReadyForCapture");
const variantsList = productsUtils.getCreatedVariants();
ordersUtils.createWaitingForCaptureOrder( createWaitingForCaptureOrder(
defaultChannel.slug, defaultChannel.slug,
randomEmail, randomEmail,
variantsList, createdVariants,
shippingUtils.getShippingMethod().id shippingMethod.id
); );
cy.get("@ordersReadyForCapture").then(ordersReadyForCaptureBefore => { cy.get("@ordersReadyForCapture").then(ordersReadyForCaptureBefore => {
@ -136,7 +149,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrdersReadyForCapture}${notANumberRegex}` `${notANumberRegex}${allOrdersReadyForCapture}${notANumberRegex}`
); );
cy.visit(urlList.homePage); cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name); changeChannel(defaultChannel.name);
cy.contains( cy.contains(
HOMEPAGE_SELECTORS.ordersReadyForCapture, HOMEPAGE_SELECTORS.ordersReadyForCapture,
ordersReadyForCaptureRegexp ordersReadyForCaptureRegexp
@ -148,13 +161,8 @@ describe("Homepage analytics", () => {
.getProductsOutOfStock(defaultChannel.slug) .getProductsOutOfStock(defaultChannel.slug)
.as("productsOutOfStock"); .as("productsOutOfStock");
const productOutOfStockRandomName = startsWith + faker.random.number(); 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, name: productOutOfStockRandomName,
channelId: defaultChannel.id, channelId: defaultChannel.id,
warehouseId: warehouse.id, warehouseId: warehouse.id,
@ -172,7 +180,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allProductsOutOfStock}${notANumberRegex}` `${notANumberRegex}${allProductsOutOfStock}${notANumberRegex}`
); );
cy.visit(urlList.homePage); cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name); changeChannel(defaultChannel.name);
cy.contains( cy.contains(
HOMEPAGE_SELECTORS.productsOutOfStock, HOMEPAGE_SELECTORS.productsOutOfStock,
productsOutOfStockRegexp productsOutOfStockRegexp
@ -182,11 +190,11 @@ describe("Homepage analytics", () => {
it("should correct amount of sales be displayed", () => { it("should correct amount of sales be displayed", () => {
homePageUtils.getSalesAmount(defaultChannel.slug).as("salesAmount"); homePageUtils.getSalesAmount(defaultChannel.slug).as("salesAmount");
ordersUtils.createReadyToFulfillOrder( createReadyToFulfillOrder(
customerId, customerId,
shippingUtils.getShippingMethod().id, shippingMethod.id,
defaultChannel.id, defaultChannel.id,
productsUtils.getCreatedVariants() createdVariants
); );
cy.get("@salesAmount").then(salesAmount => { cy.get("@salesAmount").then(salesAmount => {
@ -205,7 +213,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${totalAmountWithSeparators}${notANumberRegex}` `${notANumberRegex}${totalAmountWithSeparators}${notANumberRegex}`
); );
cy.visit(urlList.homePage); cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name); changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.sales, salesAmountRegexp).should( cy.contains(HOMEPAGE_SELECTORS.sales, salesAmountRegexp).should(
"be.visible" "be.visible"
); );
@ -214,11 +222,11 @@ describe("Homepage analytics", () => {
it("should correct amount of orders be displayed", () => { it("should correct amount of orders be displayed", () => {
homePageUtils.getTodaysOrders(defaultChannel.slug).as("todaysOrders"); homePageUtils.getTodaysOrders(defaultChannel.slug).as("todaysOrders");
ordersUtils.createReadyToFulfillOrder( createReadyToFulfillOrder(
customerId, customerId,
shippingUtils.getShippingMethod().id, shippingMethod.id,
defaultChannel.id, defaultChannel.id,
productsUtils.getCreatedVariants() createdVariants
); );
cy.get("@todaysOrders").then(ordersBefore => { cy.get("@todaysOrders").then(ordersBefore => {
@ -228,7 +236,7 @@ describe("Homepage analytics", () => {
`${notANumberRegex}${allOrders}${notANumberRegex}` `${notANumberRegex}${allOrders}${notANumberRegex}`
); );
cy.visit(urlList.homePage); cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name); changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.orders, ordersRegexp).should("be.visible"); 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 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 { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils"; import { getDefaultChannel } from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils"; import * as productsUtils from "../../../utils/productsUtils";
import ShippingUtils from "../../../utils/shippingUtils"; import * as shippingUtils from "../../../utils/shippingUtils";
import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFrontProductUtils"; import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" /> // <reference types="cypress" />
describe("Products available in listings", () => { 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 startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`; const name = `${startsWith}${faker.random.number()}`;
let productType; let productType;
@ -23,11 +20,10 @@ describe("Products available in listings", () => {
before(() => { before(() => {
cy.clearSessionData().loginUserViaRequest(); cy.clearSessionData().loginUserViaRequest();
shippingUtils.deleteShipping(startsWith); shippingUtils.deleteShippingStartsWith(startsWith);
productsUtils.deleteProperProducts(startsWith); productsUtils.deleteProductsStartsWith(startsWith);
channelsUtils getDefaultChannel()
.getDefaultChannel()
.then(channel => { .then(channel => {
defaultChannel = channel; defaultChannel = channel;
cy.fixture("addresses"); cy.fixture("addresses");
@ -39,15 +35,23 @@ describe("Products available in listings", () => {
address: addressesFixture.plAddress address: addressesFixture.plAddress
}); });
}) })
.then(() => { .then(({ warehouse: warehouseResp }) => {
warehouse = shippingUtils.getWarehouse(); warehouse = warehouseResp;
}); });
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => { productsUtils
productType = productsUtils.getProductType(); .createTypeAttributeAndCategoryForProduct(name)
attribute = productsUtils.getAttribute(); .then(
category = productsUtils.getCategory(); ({
}); attribute: attributeResp,
productType: productTypeResp,
category: categoryResp
}) => {
productType = productTypeResp;
attribute = attributeResp;
category = categoryResp;
}
);
}); });
beforeEach(() => { beforeEach(() => {
@ -56,6 +60,8 @@ describe("Products available in listings", () => {
it("should update product to available for purchase", () => { it("should update product to available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`; const productName = `${startsWith}${faker.random.number()}`;
let product;
productsUtils productsUtils
.createProductInChannel({ .createProductInChannel({
name: productName, name: productName,
@ -66,25 +72,22 @@ describe("Products available in listings", () => {
categoryId: category.id, categoryId: category.id,
isAvailableForPurchase: false isAvailableForPurchase: false
}) })
.then(() => { .then(({ product: productResp }) => {
const productUrl = productDetailsUrl( product = productResp;
productsUtils.getCreatedProduct().id const productUrl = productDetailsUrl(product.id);
); updateProductIsAvailableForPurchase(productUrl, true);
productSteps.updateProductIsAvailableForPurchase(productUrl, true);
}) })
.then(() => { .then(() => {
isProductAvailableForPurchase( getProductDetails(product.id, defaultChannel.slug);
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
}) })
.then(isVisibleResp => { .then(resp => {
expect(isVisibleResp).to.be.eq(true); expect(isProductAvailableForPurchase(resp)).to.be.eq(true);
}); });
}); });
it("should update product to not available for purchase", () => { it("should update product to not available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`; const productName = `${startsWith}${faker.random.number()}`;
let product;
productsUtils productsUtils
.createProductInChannel({ .createProductInChannel({
name: productName, name: productName,
@ -94,21 +97,16 @@ describe("Products available in listings", () => {
attributeId: attribute.id, attributeId: attribute.id,
categoryId: category.id categoryId: category.id
}) })
.then(() => { .then(({ product: productResp }) => {
const productUrl = productDetailsUrl( product = productResp;
productsUtils.getCreatedProduct().id const productUrl = productDetailsUrl(product.id);
); updateProductIsAvailableForPurchase(productUrl, false);
productSteps.updateProductIsAvailableForPurchase(productUrl, false);
}) })
.then(() => { .then(() => {
isProductAvailableForPurchase( getProductDetails(product.id, defaultChannel.slug);
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
}) })
.then(isProductVisible => { .then(resp => {
expect(isProductVisible).to.be.eq(false); expect(isProductAvailableForPurchase(resp)).to.be.eq(false);
}); });
}); });
}); });

View file

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

View file

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

View file

@ -1,6 +1,6 @@
// <reference types="cypress" /> // <reference types="cypress" />
import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors"; 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"; import { urlList } from "../../url/urlList";
describe("Products", () => { 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,8 +1,9 @@
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-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"; import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
import { HEADER_SELECTORS } from "../elements/header/header-selectors";
class ChannelsSteps { export function createChannelByView(name, currency, slug = name) {
createChannelByView(name, currency, otherCurrency, slug = name) {
cy.get(CHANNELS_SELECTORS.createChannelButton) cy.get(CHANNELS_SELECTORS.createChannelButton)
.click() .click()
.get(ADD_CHANNEL_FORM_SELECTORS.channelName) .get(ADD_CHANNEL_FORM_SELECTORS.channelName)
@ -11,16 +12,27 @@ class ChannelsSteps {
.type(slug) .type(slug)
.get(ADD_CHANNEL_FORM_SELECTORS.currency) .get(ADD_CHANNEL_FORM_SELECTORS.currency)
.click(); .click();
if (!otherCurrency) {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency); cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency);
cy.get(`[data-test-value=${currency}]`).click(); cy.get("body").then($body => {
if ($body.find(currency).length) {
cy.contains(ADD_CHANNEL_FORM_SELECTORS.currencyOptions, currency).click();
} else { } else {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency) cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyAutocompleteDropdown).click();
.type(otherCurrency)
.get(ADD_CHANNEL_FORM_SELECTORS.currencyAutocompleteDropdown)
.click();
} }
});
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click(); cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
}
} }
export default ChannelsSteps; 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();
}

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"; import { HEADER_SELECTORS } from "../elements/header/header-selectors";
class HomePageSteps { export function changeChannel(channelName) {
changeChannel(channelName) {
cy.get(HEADER_SELECTORS.channelSelect).click(); cy.get(HEADER_SELECTORS.channelSelect).click();
cy.addAliasToGraphRequest("Home"); cy.addAliasToGraphRequest("Home");
cy.get(HEADER_SELECTORS.channelSelectList) cy.get(HEADER_SELECTORS.channelSelectList)
.contains(channelName) .contains(channelName)
.click(); .click();
cy.wait("@Home"); 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( Cypress.Commands.add(
"deleteProperElements", "deleteElementsStartsWith",
(deleteFunction, getFunction, startsWith, name) => { (deleteFunction, getFunction, startsWith, name) => {
getFunction(100, startsWith).then(elements => { getFunction(100, startsWith).then(elements => {
elements.forEach(element => { 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 "./user";
import "./softAssertions"; import "./softAssertions";
import "./deleteElement/index.js"; import "./deleteElement/index.js";
import "./elements/index";
import { urlList } from "../url/urlList"; import { urlList } from "../url/urlList";

View file

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

View file

@ -1,10 +1,7 @@
import Channels from "../apiRequests/Channels"; import * as channels from "../apiRequests/Channels";
class ChannelsUtils { export function deleteChannelsStartsWith(nameStartsWith) {
channels = new Channels(); channels.getChannels().then(resp => {
deleteChannels(nameStartsWith) {
this.channels.getChannels().then(resp => {
const channelsArray = new Set(resp.body.data.channels); const channelsArray = new Set(resp.body.data.channels);
if (!channelsArray) { if (!channelsArray) {
return; return;
@ -20,22 +17,28 @@ class ChannelsUtils {
); );
}); });
if (targetChannels[0]) { if (targetChannels[0]) {
this.channels.deleteChannel(element.id, targetChannels[0].id); channels.deleteChannel(element.id, targetChannels[0].id);
channelsArray.delete(element); 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 default ChannelsUtils; 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");
}

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

View file

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

View file

@ -1,28 +1,8 @@
import Attribute from "../apiRequests/Attribute"; import * as attributeRequest from "../apiRequests/Attribute";
import Category from "../apiRequests/Category"; import * as categoryRequest from "../apiRequests/Category";
import Product from "../apiRequests/Product"; import * as productRequest from "../apiRequests/Product";
class ProductsUtils { export function createProductInChannel({
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, name,
channelId, channelId,
warehouseId = null, warehouseId = null,
@ -34,119 +14,114 @@ class ProductsUtils {
isPublished = true, isPublished = true,
isAvailableForPurchase = true, isAvailableForPurchase = true,
visibleInListings = true visibleInListings = true
}) { }) {
return this.createProduct(attributeId, name, productTypeId, categoryId) let product;
.then(() => let variants;
this.productRequest.updateChannelInProduct({ return createProduct(attributeId, name, productTypeId, categoryId)
productId: this.product.id, .then(productResp => {
product = productResp;
productRequest.updateChannelInProduct({
productId: product.id,
channelId, channelId,
isPublished, isPublished,
isAvailableForPurchase, isAvailableForPurchase,
visibleInListings visibleInListings
})
)
.then(() => {
this.createVariant(
this.product.id,
name,
warehouseId,
quantityInWarehouse,
channelId,
price
);
}); });
} })
.then(() => {
createVariant({
productId: product.id,
sku: name,
warehouseId,
quantityInWarehouse,
channelId,
price
});
})
.then(variantsResp => {
variants = variantsResp;
return { product, variants };
});
}
createTypeAttributeAndCategoryForProduct(name) { export function createTypeAttributeAndCategoryForProduct(
return this.createAttribute(name) name,
.then(() => this.createTypeProduct(name, this.attribute.id)) attributeValues
.then(() => this.createCategory(name)); ) {
} let attribute;
createAttribute(name) { let productType;
return this.attributeRequest let category;
.createAttribute(name) return createAttribute(name, attributeValues)
.then( .then(attributeResp => {
resp => (this.attribute = resp.body.data.attributeCreate.attribute) attribute = attributeResp;
); createTypeProduct(name, attributeResp.id);
} })
createTypeProduct(name, attributeId) { .then(productTypeResp => {
return this.productRequest 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) .createTypeProduct(name, attributeId)
.then( .its("body.data.productTypeCreate.productType");
resp => }
(this.productType = resp.body.data.productTypeCreate.productType) export function createCategory(name) {
); return categoryRequest
}
createCategory(name) {
return this.categoryRequest
.createCategory(name) .createCategory(name)
.then(resp => (this.category = resp.body.data.categoryCreate.category)); .its("body.data.categoryCreate.category");
} }
createProduct(attributeId, name, productTypeId, categoryId) { export function createProduct(attributeId, name, productTypeId, categoryId) {
return this.productRequest return productRequest
.createProduct(attributeId, name, productTypeId, categoryId) .createProduct(attributeId, name, productTypeId, categoryId)
.then(resp => (this.product = resp.body.data.productCreate.product)); .its("body.data.productCreate.product");
} }
createVariant( export function createVariant({
productId, productId,
name, sku,
warehouseId, warehouseId,
quantityInWarehouse, quantityInWarehouse,
channelId, channelId,
price price
) { }) {
return this.productRequest return productRequest
.createVariant( .createVariant({
productId, productId,
name, sku,
warehouseId, warehouseId,
quantityInWarehouse, quantity: quantityInWarehouse,
channelId, channelId,
price price
) })
.then( .its("body.data.productVariantBulkCreate.productVariants");
resp => }
(this.variants =
resp.body.data.productVariantBulkCreate.productVariants) export function deleteProductsStartsWith(startsWith) {
); cy.deleteElementsStartsWith(
} productRequest.deleteProductType,
getCreatedProduct() { productRequest.getProductTypes,
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, startsWith,
"productType" "productType"
); );
cy.deleteProperElements( cy.deleteElementsStartsWith(
attribute.deleteAttribute, attributeRequest.deleteAttribute,
attribute.getAttributes, attributeRequest.getAttributes,
startsWith, startsWith,
"attributes" "attributes"
); );
cy.deleteProperElements( cy.deleteElementsStartsWith(
category.deleteCategory, categoryRequest.deleteCategory,
category.getCategories, categoryRequest.getCategories,
startsWith, startsWith,
"categories" "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 * as shippingMethodRequest from "../apiRequests/ShippingMethod";
import Warehouse from "../apiRequests/Warehouse"; import * as warehouseRequest from "../apiRequests/Warehouse";
class ShippingUtils {
shippingMethodRequest = new ShippingMethod();
warehouseRequest = new Warehouse();
shippingMethod; export function createShipping({ channelId, name, address, price = 1 }) {
shippingZone; let shippingMethod;
warehouse; let shippingZone;
let warehouse;
createShipping({ channelId, name, address, price = 1 }) { return createShippingZone(name, address.country)
return this.createShippingZone(name, address.country) .then(shippingZoneResp => {
.then(() => this.createWarehouse(name, this.shippingZone.id, address)) shippingZone = shippingZoneResp;
.then(() => this.createShippingRate(name, this.shippingZone.id)) createWarehouse(name, shippingZone.id, address);
.then(() => })
this.shippingMethodRequest.addChannelToShippingMethod( .then(warehouseResp => {
this.shippingMethod.id, warehouse = warehouseResp;
createShippingRate(name, shippingZone.id);
})
.then(sippingMethodResp => {
shippingMethod = sippingMethodResp;
shippingMethodRequest.addChannelToShippingMethod(
shippingMethod.id,
channelId, channelId,
price price
)
); );
} })
.then(() => ({ shippingMethod, shippingZone, warehouse }));
}
createShippingZone(name, country) { export function createShippingZone(name, country) {
return this.shippingMethodRequest return shippingMethodRequest
.createShippingZone(name, country) .createShippingZone(name, country)
.then(resp => { .its("body.data.shippingZoneCreate.shippingZone");
this.shippingZone = resp.body.data.shippingZoneCreate.shippingZone; }
}); export function createWarehouse(name, shippingZoneId, address) {
} return warehouseRequest
createWarehouse(name, shippingZoneId, address) {
return this.warehouseRequest
.createWarehouse(name, shippingZoneId, address) .createWarehouse(name, shippingZoneId, address)
.then(resp => { .its("body.data.createWarehouse.warehouse");
this.warehouse = resp.body.data.createWarehouse.warehouse; }
}); export function createShippingRate(name, shippingZoneId) {
} return shippingMethodRequest
createShippingRate(name, shippingZoneId) {
return this.shippingMethodRequest
.createShippingRate(name, shippingZoneId) .createShippingRate(name, shippingZoneId)
.then( .its("body.data.shippingPriceCreate.shippingMethod");
resp => }
(this.shippingMethod =
resp.body.data.shippingPriceCreate.shippingMethod)
);
}
getShippingMethod() { export function deleteShippingStartsWith(startsWith) {
return this.shippingMethod; cy.deleteElementsStartsWith(
} shippingMethodRequest.deleteShippingZone,
shippingMethodRequest.getShippingZones,
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, startsWith,
"shippingZONE" "shippingZONE"
); );
cy.deleteProperElements( cy.deleteElementsStartsWith(
warehouse.deleteWarehouse, warehouseRequest.deleteWarehouse,
warehouse.getWarehouses, warehouseRequest.getWarehouses,
startsWith, startsWith,
"Warehouse" "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 { getProductDetails } from "../../apiRequests/storeFront/ProductDetails";
import Search from "../../apiRequests/storeFront/Search";
export const isProductVisible = (productId, channelSlug, name) => { export const isProductVisible = (resp, name) => {
const productDetails = new ProductDetails(); const product = resp.body.data.product;
return productDetails
.getProductDetails(productId, channelSlug)
.then(productDetailsResp => {
const product = productDetailsResp.body.data.product;
return product !== null && product.name === name; return product !== null && product.name === name;
};
export const isProductAvailableForPurchase = resp => {
const product = resp.body.data.product;
return product.isAvailableForPurchase;
};
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 isProductAvailableForPurchase = (productId, channelSlug) => { export const getProductPrice = (productId, channelSlug) =>
const productDetails = new ProductDetails(); getProductDetails(productId, channelSlug).then(
return productDetails resp => resp.body.data.product.variants[0].pricing.price.gross.amount
.getProductDetails(productId, channelSlug)
.then(
productDetailsResp =>
productDetailsResp.body.data.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
);
};

View file

@ -3,6 +3,10 @@
"context": "dialog header", "context": "dialog header",
"string": "Cancel Order" "string": "Cancel Order"
}, },
"Previous discount label id": {
"context": "Previous discount label",
"string": "Previous discount value"
},
"amount title": { "amount title": {
"context": "amount title", "context": "amount title",
"string": "Refunded amount" "string": "Refunded amount"
@ -50,6 +54,14 @@
"configurationPluginsPages": { "configurationPluginsPages": {
"string": "View and update your plugins and their settings." "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": { "event products list title refunded": {
"context": "refunded products list title", "context": "refunded products list title",
"string": "Products refunded" "string": "Products refunded"
@ -74,6 +86,26 @@
"context": "order marked as paid event title", "context": "order marked as paid event title",
"string": "Order was marked as paid by" "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": { "event title refunded": {
"context": "refunded event title", "context": "refunded event title",
"string": "Products were refunded by " "string": "Products were refunded by "
@ -86,6 +118,10 @@
"context": "returned event title", "context": "returned event title",
"string": "Products were returned by" "string": "Products were returned by"
}, },
"fixed amount subtitle id": {
"context": "Fixed amount subtitle",
"string": "Fixed amount"
},
"homeActivityCardHeader": { "homeActivityCardHeader": {
"context": "header", "context": "header",
"string": "Activity" "string": "Activity"
@ -235,6 +271,10 @@
"menuPropertiesMenuTitle": { "menuPropertiesMenuTitle": {
"string": "Menu Title" "string": "Menu Title"
}, },
"new discount label id": {
"context": "new discount label",
"string": "New discount value"
},
"orderCustomerBillingAddressNotSet": { "orderCustomerBillingAddressNotSet": {
"context": "no address is set in draft order", "context": "no address is set in draft order",
"string": "Not set" "string": "Not set"
@ -1839,10 +1879,6 @@
"src_dot_components_dot_ErrorPage_dot_3182212440": { "src_dot_components_dot_ErrorPage_dot_3182212440": {
"string": "We've encountered a problem..." "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": { "src_dot_components_dot_FilterBar_dot_2173195312": {
"context": "button", "context": "button",
"string": "Delete Search" "string": "Delete Search"
@ -3239,6 +3275,38 @@
"context": "return button", "context": "return button",
"string": "Return / Replace order" "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": { "src_dot_orders_dot_components_dot_OrderDraftCancelDialog_dot_1961675716": {
"context": "dialog header", "context": "dialog header",
"string": "Delete Daft Order" "string": "Delete Daft Order"
@ -3264,22 +3332,36 @@
"context": "total price of ordered products", "context": "total price of ordered products",
"string": "Total" "string": "Total"
}, },
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_2429341469": { "src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_addCustomerInfo": {
"context": "button", "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" "string": "Add shipping carrier"
}, },
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_3202709354": { "src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_discount": {
"string": "Taxes (VAT included)" "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" "string": "No applicable shipping carriers"
}, },
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_781550514": { "src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_subtotal": {
"context": "subtotal price or an order", "context": "subtotal price",
"string": "Subtotal" "string": "Subtotal"
}, },
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_878013594": { "src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_taxes": {
"context": "total price of an order", "context": "taxes title",
"string": "Taxes (VAT included)"
},
"src_dot_orders_dot_components_dot_OrderDraftDetailsSummary_dot_total": {
"context": "total price",
"string": "Total" "string": "Total"
}, },
"src_dot_orders_dot_components_dot_OrderDraftDetails_dot_2343989342": { "src_dot_orders_dot_components_dot_OrderDraftDetails_dot_2343989342": {
@ -3580,6 +3662,10 @@
"context": "order history message", "context": "order history message",
"string": "Order was cancelled" "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": { "src_dot_orders_dot_components_dot_OrderHistory_dot_description": {
"context": "replacement created order history message description", "context": "replacement created order history message description",
"string": "was created for replaced products" "string": "was created for replaced products"
@ -3588,6 +3674,14 @@
"context": "replacement created order history message draft number", "context": "replacement created order history message draft number",
"string": "Draft #{orderNumber} " "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": { "src_dot_orders_dot_components_dot_OrderInvoiceEmailSendDialog_dot_1821123638": {
"string": "Are you sure you want to send this invoice: {invoiceNumber} to the customer?" "string": "Are you sure you want to send this invoice: {invoiceNumber} to the customer?"
}, },
@ -6740,10 +6834,18 @@
"context": "section header", "context": "section header",
"string": "Events" "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": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_1606361075": {
"context": "event", "context": "event",
"string": "Order updated" "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": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2240725235": {
"context": "event", "context": "event",
"string": "Checkout created" "string": "Checkout created"
@ -6768,6 +6870,10 @@
"context": "webhook events", "context": "webhook events",
"string": "Expand or restrict webhooks permissions to register certain events in Saleor system." "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": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3316426878": {
"context": "event", "context": "event",
"string": "Product updated" "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/icons": "^4.5.1",
"@material-ui/styles": "^4.5.2", "@material-ui/styles": "^4.5.2",
"@saleor/macaw-ui": "^0.1.1-9", "@saleor/macaw-ui": "^0.1.1-9",
"@types/faker": "^5.1.6",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6",
"apollo": "^2.21.2", "apollo": "^2.21.2",
"apollo-cache-inmemory": "^1.6.5", "apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8", "apollo-client": "^2.6.8",
@ -45,7 +45,6 @@
"editorjs-undo": "^0.1.4", "editorjs-undo": "^0.1.4",
"faker": "^5.1.0", "faker": "^5.1.0",
"fast-array-diff": "^0.2.0", "fast-array-diff": "^0.2.0",
"fsevents": "^1.2.9",
"fuzzaldrin": "^2.1.0", "fuzzaldrin": "^2.1.0",
"graphql": "^14.4.2", "graphql": "^14.4.2",
"graphql-tag": "^2.11.0", "graphql-tag": "^2.11.0",
@ -76,7 +75,7 @@
"react-sortable-tree": "^2.6.2", "react-sortable-tree": "^2.6.2",
"semver-compare": "^1.0.0", "semver-compare": "^1.0.0",
"slugify": "^1.4.6", "slugify": "^1.4.6",
"typescript": "^3.9.7", "typescript": "^4.2.3",
"url-join": "^4.0.1", "url-join": "^4.0.1",
"use-react-router": "^1.0.7" "use-react-router": "^1.0.7"
}, },
@ -91,7 +90,7 @@
"@babel/plugin-proposal-optional-chaining": "^7.8.3", "@babel/plugin-proposal-optional-chaining": "^7.8.3",
"@babel/preset-env": "^7.5.4", "@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.7.4", "@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.7.4", "@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.7.6", "@babel/runtime": "^7.7.6",
"@pollyjs/adapter-node-http": "^5.0.0", "@pollyjs/adapter-node-http": "^5.0.0",
"@pollyjs/core": "^5.0.0", "@pollyjs/core": "^5.0.0",
@ -123,8 +122,8 @@
"@types/storybook__react": "^4.0.2", "@types/storybook__react": "^4.0.2",
"@types/url-join": "^4.0.0", "@types/url-join": "^4.0.0",
"@types/webappsec-credential-management": "^0.5.1", "@types/webappsec-credential-management": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^2.12.0", "@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^2.9.0", "@typescript-eslint/parser": "^4.16.1",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0", "babel-jest": "^23.6.0",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",

2
react-intl.d.ts vendored
View file

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

View file

@ -1644,7 +1644,6 @@ type DeletePrivateMetadata {
type DigitalContent implements Node & ObjectWithMetadata { type DigitalContent implements Node & ObjectWithMetadata {
useDefaultSettings: Boolean! useDefaultSettings: Boolean!
automaticFulfillment: Boolean! automaticFulfillment: Boolean!
productVariant: ProductVariant!
contentFile: String! contentFile: String!
maxDownloads: Int maxDownloads: Int
urlValidDays: Int urlValidDays: Int
@ -1652,6 +1651,7 @@ type DigitalContent implements Node & ObjectWithMetadata {
id: ID! id: ID!
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
productVariant: ProductVariant!
} }
type DigitalContentCountableConnection { type DigitalContentCountableConnection {
@ -2685,6 +2685,11 @@ type Mutation {
orderLinesCreate(id: ID!, input: [OrderLineCreateInput]!): OrderLinesCreate orderLinesCreate(id: ID!, input: [OrderLineCreateInput]!): OrderLinesCreate
orderLineDelete(id: ID!): OrderLineDelete orderLineDelete(id: ID!): OrderLineDelete
orderLineUpdate(id: ID!, input: OrderLineInput!): OrderLineUpdate 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 orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid
orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund
orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate
@ -2860,9 +2865,6 @@ type Order implements Node & ObjectWithMetadata {
token: String! token: String!
voucher: Voucher voucher: Voucher
giftCards: [GiftCard] giftCards: [GiftCard]
discount: Money
discountName: String
translatedDiscountName: String
displayGrossPrices: Boolean! displayGrossPrices: Boolean!
customerNote: String! customerNote: String!
weight: Weight weight: Weight
@ -2880,6 +2882,7 @@ type Order implements Node & ObjectWithMetadata {
paymentStatusDisplay: String! paymentStatusDisplay: String!
payments: [Payment] payments: [Payment]
total: TaxedMoney! total: TaxedMoney!
undiscountedTotal: TaxedMoney!
subtotal: TaxedMoney! subtotal: TaxedMoney!
statusDisplay: String statusDisplay: String
canFinalize: Boolean! canFinalize: Boolean!
@ -2889,6 +2892,10 @@ type Order implements Node & ObjectWithMetadata {
totalBalance: Money! totalBalance: Money!
userEmail: String userEmail: String
isShippingRequired: Boolean! 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 { enum OrderAction {
@ -2949,6 +2956,46 @@ enum OrderDirection {
DESC 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 { input OrderDraftFilterInput {
customer: String customer: String
created: DateRangeInput created: DateRangeInput
@ -2970,6 +3017,7 @@ enum OrderErrorCode {
CANNOT_CANCEL_FULFILLMENT CANNOT_CANCEL_FULFILLMENT
CANNOT_CANCEL_ORDER CANNOT_CANCEL_ORDER
CANNOT_DELETE CANNOT_DELETE
CANNOT_DISCOUNT
CANNOT_REFUND CANNOT_REFUND
CAPTURE_INACTIVE_PAYMENT CAPTURE_INACTIVE_PAYMENT
NOT_EDITABLE NOT_EDITABLE
@ -3018,6 +3066,7 @@ type OrderEvent implements Node {
transactionReference: String transactionReference: String
shippingCostsIncluded: Boolean shippingCostsIncluded: Boolean
relatedOrder: Order relatedOrder: Order
discount: OrderEventDiscountObject
} }
type OrderEventCountableConnection { type OrderEventCountableConnection {
@ -3031,10 +3080,21 @@ type OrderEventCountableEdge {
cursor: String! cursor: String!
} }
type OrderEventDiscountObject {
valueType: DiscountValueTypeEnum!
value: PositiveDecimal!
reason: String
amount: Money
oldValueType: DiscountValueTypeEnum
oldValue: PositiveDecimal
oldAmount: Money
}
type OrderEventOrderLineObject { type OrderEventOrderLineObject {
quantity: Int quantity: Int
orderLine: OrderLine orderLine: OrderLine
itemName: String itemName: String
discount: OrderEventDiscountObject
} }
enum OrderEventsEmailsEnum { enum OrderEventsEmailsEnum {
@ -3061,6 +3121,12 @@ enum OrderEventsEnum {
ORDER_MARKED_AS_PAID ORDER_MARKED_AS_PAID
ORDER_FULLY_PAID ORDER_FULLY_PAID
ORDER_REPLACEMENT_CREATED 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 UPDATED_ADDRESS
EMAIL_SENT EMAIL_SENT
CONFIRMED CONFIRMED
@ -3124,15 +3190,20 @@ type OrderLine implements Node {
isShippingRequired: Boolean! isShippingRequired: Boolean!
quantity: Int! quantity: Int!
quantityFulfilled: Int! quantityFulfilled: Int!
unitDiscountReason: String
taxRate: Float! taxRate: Float!
digitalContentUrl: DigitalContentUrl digitalContentUrl: DigitalContentUrl
thumbnail(size: Int): Image thumbnail(size: Int): Image
unitPrice: TaxedMoney! unitPrice: TaxedMoney!
undiscountedUnitPrice: TaxedMoney!
unitDiscount: Money!
unitDiscountValue: PositiveDecimal!
totalPrice: TaxedMoney! totalPrice: TaxedMoney!
variant: ProductVariant variant: ProductVariant
translatedProductName: String! translatedProductName: String!
translatedVariantName: String! translatedVariantName: String!
allocations: [Allocation!] allocations: [Allocation!]
unitDiscountType: DiscountValueTypeEnum
} }
input OrderLineCreateInput { input OrderLineCreateInput {
@ -3147,6 +3218,20 @@ type OrderLineDelete {
orderErrors: [OrderError!]! 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 { input OrderLineInput {
quantity: Int! quantity: Int!
} }
@ -4509,7 +4594,7 @@ type Query {
attribute(id: ID, slug: String): Attribute attribute(id: ID, slug: String): Attribute
appsInstallations: [AppInstallation!]! appsInstallations: [AppInstallation!]!
apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection 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 addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData
address(id: ID!): Address address(id: ID!): Address
customers(filter: CustomerFilterInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection customers(filter: CustomerFilterInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection
@ -4964,6 +5049,7 @@ type Shop {
companyAddress: Address companyAddress: Address
customerSetPasswordUrl: String customerSetPasswordUrl: String
staffNotificationRecipients: [StaffNotificationRecipient] staffNotificationRecipients: [StaffNotificationRecipient]
version: String!
} }
type ShopAddressUpdate { type ShopAddressUpdate {
@ -5820,6 +5906,9 @@ enum WebhookEventTypeEnum {
PRODUCT_CREATED PRODUCT_CREATED
PRODUCT_UPDATED PRODUCT_UPDATED
PRODUCT_DELETED PRODUCT_DELETED
PRODUCT_VARIANT_CREATED
PRODUCT_VARIANT_UPDATED
PRODUCT_VARIANT_DELETED
CHECKOUT_CREATED CHECKOUT_CREATED
CHECKOUT_UPDATED CHECKOUT_UPDATED
FULFILLMENT_CREATED FULFILLMENT_CREATED
@ -5843,6 +5932,9 @@ enum WebhookSampleEventTypeEnum {
PRODUCT_CREATED PRODUCT_CREATED
PRODUCT_UPDATED PRODUCT_UPDATED
PRODUCT_DELETED PRODUCT_DELETED
PRODUCT_VARIANT_CREATED
PRODUCT_VARIANT_UPDATED
PRODUCT_VARIANT_DELETED
CHECKOUT_CREATED CHECKOUT_CREATED
CHECKOUT_UPDATED CHECKOUT_UPDATED
FULFILLMENT_CREATED 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} disabled={disabled}
variant="contained" variant="contained"
onClick={onAdd} onClick={onAdd}
data-test-id="create-collection"
> >
<FormattedMessage <FormattedMessage
defaultMessage="Create collection" defaultMessage="Create collection"

View file

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

View file

@ -1,31 +1,12 @@
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog"; import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent"; import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle"; 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 { DialogProps } from "@saleor/types";
import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import { ConfirmButtonTransitionState } from "../ConfirmButton/ConfirmButton";
ConfirmButtonTransitionState import DialogButtons from "./DialogButtons";
} 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 ActionDialogProps extends DialogProps { interface ActionDialogProps extends DialogProps {
children?: React.ReactNode; children?: React.ReactNode;
@ -34,61 +15,25 @@ interface ActionDialogProps extends DialogProps {
disabled?: boolean; disabled?: boolean;
maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false; maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false;
title: string; title: string;
variant?: "default" | "delete" | "info"; variant?: ActionDialogVariant;
onConfirm(); onConfirm();
} }
const ActionDialog: React.FC<ActionDialogProps> = props => { const ActionDialog: React.FC<ActionDialogProps> = props => {
const { const { children, open, title, onClose, variant, ...rest } = props;
children,
confirmButtonLabel,
confirmButtonState,
disabled,
open,
title,
variant,
onConfirm,
onClose,
...rest
} = props;
const classes = useStyles(props);
const intl = useIntl();
return ( return (
<Dialog fullWidth onClose={onClose} open={open} {...rest}> <Dialog fullWidth onClose={onClose} open={open} {...rest}>
<DialogTitle>{title}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogContent>{children}</DialogContent> <DialogContent>{children}</DialogContent>
<DialogActions> <DialogButtons {...rest} onClose={onClose} variant={variant} />
<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>
</Dialog> </Dialog>
); );
}; };
ActionDialog.defaultProps = { ActionDialog.defaultProps = {
maxWidth: "xs", maxWidth: "xs"
variant: "default"
}; };
ActionDialog.displayName = "ActionDialog"; ActionDialog.displayName = "ActionDialog";
export default 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 ( return (
<TableRow key={product.id}> <TableRow
key={product.id}
data-test-id="assign-product-table-row"
>
<TableCellAvatar <TableCellAvatar
className={classes.avatar} className={classes.avatar}
thumbnail={maybe(() => product.thumbnail.url)} thumbnail={maybe(() => product.thumbnail.url)}
@ -202,6 +205,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
<FormattedMessage {...buttonMessages.back} /> <FormattedMessage {...buttonMessages.back} />
</Button> </Button>
<ConfirmButton <ConfirmButton
data-test="submit"
transitionState={confirmButtonState} transitionState={confirmButtonState}
color="primary" color="primary"
variant="contained" variant="contained"

View file

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

View file

@ -51,15 +51,17 @@ interface ExtendedPageHeaderProps {
className?: string; className?: string;
inline?: boolean; inline?: boolean;
title?: React.ReactNode; title?: React.ReactNode;
testId?: string;
} }
const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => { const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
const { children, className, inline, title } = props; const { children, className, inline, title, testId } = props;
const classes = useStyles(props); const classes = useStyles(props);
return ( return (
<div <div
data-test-id={testId}
className={classNames(classes.root, className, { className={classNames(classes.root, className, {
[classes.block]: !inline [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 { makeStyles } from "@material-ui/core/styles";
import Typography, { TypographyProps } from "@material-ui/core/Typography"; import Typography, { TypographyProps } from "@material-ui/core/Typography";
import { ITheme } from "@saleor/theme";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ (theme: ITheme) => ({
primary: { primary: {
color: theme.palette.primary.main color: theme.palette.textHighlighted.active
}, },
root: { root: {
cursor: "pointer", cursor: "pointer",
@ -17,6 +18,10 @@ const useStyles = makeStyles(
}, },
underline: { underline: {
textDecoration: "underline" textDecoration: "underline"
},
disabled: {
cursor: "default",
color: theme.palette.textHighlighted.inactive
} }
}), }),
{ name: "Link" } { name: "Link" }
@ -27,6 +32,7 @@ interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
underline?: boolean; underline?: boolean;
typographyProps?: TypographyProps; typographyProps?: TypographyProps;
onClick: () => void; onClick: () => void;
disabled?: boolean;
} }
const Link: React.FC<LinkProps> = props => { const Link: React.FC<LinkProps> = props => {
@ -36,6 +42,7 @@ const Link: React.FC<LinkProps> = props => {
color = "primary", color = "primary",
underline = false, underline = false,
onClick, onClick,
disabled,
...linkProps ...linkProps
} = props; } = props;
@ -47,9 +54,14 @@ const Link: React.FC<LinkProps> = props => {
className={classNames(className, { className={classNames(className, {
[classes.root]: true, [classes.root]: true,
[classes[color]]: true, [classes[color]]: true,
[classes.underline]: underline [classes.underline]: underline,
[classes.disabled]: disabled
})} })}
onClick={event => { onClick={event => {
if (disabled) {
return;
}
event.preventDefault(); event.preventDefault();
onClick(); onClick();
}} }}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -54,7 +54,12 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
return ( return (
<Container> <Container>
<PageHeader title={intl.formatMessage(sectionNames.sales)}> <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" /> <FormattedMessage defaultMessage="Create Sale" description="button" />
</Button> </Button>
</PageHeader> </PageHeader>

View file

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

View file

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

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { FulfillmentStatus } from "./../../types/globalTypes"; import { DiscountValueTypeEnum, FulfillmentStatus } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: FulfillmentFragment // GraphQL fragment: FulfillmentFragment
@ -14,6 +14,31 @@ export interface FulfillmentFragment_lines_orderLine_variant {
quantityAvailable: number; 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 { export interface FulfillmentFragment_lines_orderLine_unitPrice_gross {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -46,6 +71,11 @@ export interface FulfillmentFragment_lines_orderLine {
productSku: string; productSku: string;
quantity: number; quantity: number;
quantityFulfilled: 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; unitPrice: FulfillmentFragment_lines_orderLine_unitPrice;
thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null; thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null;
} }

View file

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

View file

@ -2,12 +2,35 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // 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 { export interface OrderEventFragment_relatedOrder {
__typename: "Order"; __typename: "Order";
id: string; id: string;
@ -22,6 +45,29 @@ export interface OrderEventFragment_user {
lastName: string; 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 { export interface OrderEventFragment_lines_orderLine {
__typename: "OrderLine"; __typename: "OrderLine";
id: string; id: string;
@ -32,6 +78,8 @@ export interface OrderEventFragment_lines_orderLine {
export interface OrderEventFragment_lines { export interface OrderEventFragment_lines {
__typename: "OrderEventOrderLineObject"; __typename: "OrderEventOrderLineObject";
quantity: number | null; quantity: number | null;
itemName: string | null;
discount: OrderEventFragment_lines_discount | null;
orderLine: OrderEventFragment_lines_orderLine | null; orderLine: OrderEventFragment_lines_orderLine | null;
} }
@ -44,6 +92,7 @@ export interface OrderEventFragment {
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
invoiceNumber: string | null; invoiceNumber: string | null;
discount: OrderEventFragment_discount | null;
relatedOrder: OrderEventFragment_relatedOrder | null; relatedOrder: OrderEventFragment_relatedOrder | null;
message: string | null; message: string | null;
quantity: number | null; quantity: number | null;

View file

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

View file

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

View file

@ -136,4 +136,6 @@ export interface Home {
export interface HomeVariables { export interface HomeVariables {
channel: string; 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