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

This commit is contained in:
Tomasz Szymanski 2021-03-03 09:35:09 +01:00
commit 5d63968a6f
171 changed files with 34016 additions and 1879 deletions

View file

@ -21,6 +21,8 @@ greatly reduce the amount of work needed to review your work. -->
1. [ ] Data-test are added for new elements. 1. [ ] Data-test are added for new elements.
1. [ ] Type definitions are up to date. 1. [ ] Type definitions are up to date.
1. [ ] Changes are mentioned in the changelog. 1. [ ] Changes are mentioned in the changelog.
1. [ ] The changes are tested in different browsers (Chrome, Firefox, Safari).
1. [ ] The changes are tested in light and dark mode.
### Test environment config ### Test environment config

View file

@ -8,7 +8,7 @@ jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
env: env:
API_URI: https://master.staging.saleor.cloud/graphql/ API_URI: /graphql/
APP_MOUNT_URI: /dashboard/ APP_MOUNT_URI: /dashboard/
STATIC_URL: /dashboard/static/ STATIC_URL: /dashboard/static/
SENTRY_ORG: saleor SENTRY_ORG: saleor

View file

@ -70,6 +70,7 @@ jobs:
npm run test npm run test
cypress-run: cypress-run:
if: github.event.pull_request.head.repo.full_name == 'mirumee/saleor-dashboard'
runs-on: ubuntu-16.04 runs-on: ubuntu-16.04
steps: steps:
- name: Checkout - name: Checkout
@ -90,11 +91,12 @@ jobs:
env: env:
API_URI: ${{ steps.api_uri.outputs.custom_api_uri || secrets.API_URI }} API_URI: ${{ steps.api_uri.outputs.custom_api_uri || secrets.API_URI }}
APP_MOUNT_URI: ${{ secrets.APP_MOUNT_URI }} APP_MOUNT_URI: ${{ secrets.APP_MOUNT_URI }}
CYPRESS_baseUrl: ${{ secrets.CYPRESS_BASEURL }}
CYPRESS_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }} CYPRESS_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }}
CYPRESS_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }} CYPRESS_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }}
with: with:
build: npm run build build: npm run build
start: npx http-server -a localhost -p 9000 build/dashboard start: npx local-web-server --spa index.html
wait-on: http://localhost:9000/ wait-on: http://localhost:9000/
wait-on-timeout: 120 wait-on-timeout: 120
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1

View file

@ -19,6 +19,9 @@ 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
- Fix no channels crash - #984 by @dominik-zeglen
- Update webhooks - #982 by @piotrgrundas
- Fix trigger form change when collections are being added to list of product collections - #987 by @gax97
- Unconfirmed order manipulation - #967 by @tomaszszymanski129 - Unconfirmed order manipulation - #967 by @tomaszszymanski129
# 2.11.1 # 2.11.1

View file

@ -8,8 +8,8 @@ ARG APP_MOUNT_URI
ARG API_URI ARG API_URI
ARG STATIC_URL ARG STATIC_URL
ENV API_URI ${API_URI:-http://localhost:8000/graphql/} ENV API_URI ${API_URI:-http://localhost:8000/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/dashboard/} ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/}
ENV STATIC_URL ${STATIC_URL:-/dashboard/} ENV STATIC_URL ${STATIC_URL:-/}
EXPOSE 9000 EXPOSE 9000
CMD npm start -- --host 0.0.0.0 CMD npm start -- --host 0.0.0.0

View file

@ -1,5 +1,5 @@
{ {
"baseUrl": "http://localhost:9000", "baseUrl": "http://localhost:9000/dashboard/",
"defaultCommandTimeout": 15000, "defaultCommandTimeout": 15000,
"requestTimeout": 15000, "requestTimeout": 15000,
"viewportWidth": 1400, "viewportWidth": 1400,

View file

@ -0,0 +1,51 @@
class Attribute {
createAttribute(name) {
const mutation = `mutation{
attributeCreate(input:{
name:"${name}"
valueRequired:false
type:PRODUCT_TYPE
}){
attribute{
id
}
attributeErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
getAttributes(first, search) {
const mutation = `query{
attributes(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(mutation)
.then(resp => resp.body.data.attributes.edges);
}
deleteAttribute(attributeId) {
const mutation = `mutation{
attributeDelete(id:"${attributeId}"){
attributeErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
}
export default Attribute;

View file

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

View file

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

View file

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

View file

@ -0,0 +1,85 @@
export class Customer {
createCustomer(email, customerName, address, isActive = false) {
const mutation = `
mutation{
customerCreate(input:{
firstName: "${customerName}"
lastName: "${customerName}"
email: "${email}"
isActive: ${isActive}
defaultBillingAddress: {
companyName: "${address.companyName}"
streetAddress1: "${address.streetAddress1}"
streetAddress2: "${address.streetAddress2}"
city: "${address.city}"
postalCode: "${address.postalCode}"
country: ${address.country}
phone: "${address.phone}"
}
defaultShippingAddress: {
companyName: "${address.companyName}"
streetAddress1: "${address.streetAddress1}"
streetAddress2: "${address.streetAddress2}"
city: "${address.city}"
postalCode: "${address.postalCode}"
country: ${address.country}
phone: "${address.phone}"
}
}){
user{
id
email
}
accountErrors{
code
message
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
deleteCustomers(startsWith) {
this.getCustomers(startsWith).then(resp => {
if (resp.body.data.customers) {
const customers = resp.body.data.customers.edges;
customers.forEach(element => {
if (element.node.email.includes(startsWith)) {
this.deleteCustomer(element.node.id);
}
});
}
});
}
deleteCustomer(customerId) {
const mutation = `mutation{
customerDelete(id:"${customerId}"){
accountErrors{
code
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
getCustomers(startsWith) {
const query = `query{
customers(first:100, filter: {
search: "${startsWith}"
}){
edges{
node{
id
email
}
}
}
}
`;
return cy.sendRequestWithQuery(query);
}
}
export default Customer;

View file

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

View file

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

View file

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

View file

@ -0,0 +1,86 @@
class ShippingMethod {
createShippingRate(name, shippingZone) {
const mutation = `
mutation{
shippingPriceCreate(input:{
name: "${name}"
shippingZone: "${shippingZone}"
type: PRICE
}){
shippingMethod{
id
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
createShippingZone(name, country) {
const mutation = `
mutation{
shippingZoneCreate(input:{
name: "${name}"
countries: "${country}"
}){
shippingZone{
id
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
addChannelToShippingMethod(shippingRateId, channelId, price) {
const mutation = `
mutation{
shippingMethodChannelListingUpdate(id:"${shippingRateId}", input:{
addChannels: {
channelId:"${channelId}"
price: ${price}
}
}){
shippingMethod{
id
}
shippingErrors{
code
message
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
deleteShippingZone(shippingZoneId) {
const mutation = `mutation{
shippingZoneDelete(id:"${shippingZoneId}"){
shippingErrors{
message
}
}
}
`;
return cy.sendRequestWithQuery(mutation);
}
getShippingZones() {
const query = `query{
shippingZones(first:100){
edges{
node{
name
id
}
}
}
}
`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.shippingZones.edges);
}
}
export default ShippingMethod;

View file

@ -0,0 +1,57 @@
class Warehouse {
createWarehouse(name, shippingZone, address, slug = name) {
const mutation = `mutation{
createWarehouse(input:{
name:"${name}"
slug:"${slug}"
shippingZones:"${shippingZone}"
address:{
streetAddress1: "${address.streetAddress1}"
streetAddress2: "${address.streetAddress2}"
city: "${address.city}"
postalCode: "${address.postalCode}"
country: ${address.country}
phone: "${address.phone}"
}
}){
warehouseErrors{
field
message
}
warehouse{
id
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
getWarehouses(first, search) {
const query = `query{
warehouses(first:${first}, filter:{
search:"${search}"
}){
edges{
node{
id
name
}
}
}
}`;
return cy
.sendRequestWithQuery(query)
.then(resp => resp.body.data.warehouses.edges);
}
deleteWarehouse(warehouseId) {
const mutation = `mutation{
deleteWarehouse(id:"${warehouseId}"){
warehouseErrors{
field
message
}
}
}`;
return cy.sendRequestWithQuery(mutation);
}
}
export default Warehouse;

View file

@ -0,0 +1,18 @@
class ProductDetails {
getProductDetails(productId, channelId) {
const query = `fragment BasicProductFields on Product {
id
name
}
query ProductDetails{
product(id: "${productId}", channel: "${channelId}") {
...BasicProductFields
isAvailable
isAvailableForPurchase
availableForPurchase
}
}`;
return cy.sendRequestWithQuery(query, "token");
}
}
export default ProductDetails;

View file

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

View file

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

View file

@ -1,4 +1,7 @@
/* eslint-disable sort-keys */
export const LEFT_MENU_SELECTORS = { export const LEFT_MENU_SELECTORS = {
catalog: "[data-test-id='catalogue']" catalog: "[data-test='menu-item-label'][data-test-id='catalogue']",
configuration: "[data-test='menu-item-label'][data-test-id='configure']",
home: "[data-test='menu-item-label'][data-test-id='home']",
orders: "[data-test='menu-item-label'][data-test-id=orders']",
products: "[data-test='submenu-item-label'][data-test-id='products']"
}; };

View file

@ -1,5 +1,6 @@
/* eslint-disable sort-keys */ /* eslint-disable sort-keys */
export const PRODUCTS_SELECTORS = { export const PRODUCTS_SELECTORS = {
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']",
createProductBtn: "[data-test='add-product']", createProductBtn: "[data-test='add-product']",
productNameInput: "[name='name']", productNameInput: "[name='name']",
@ -11,5 +12,21 @@ export const PRODUCTS_SELECTORS = {
visibleRadioBtn: "[name='isPublished']", visibleRadioBtn: "[name='isPublished']",
saveBtn: "[data-test='button-bar-confirm']", saveBtn: "[data-test='button-bar-confirm']",
confirmationMsg: "[data-test='notification-success']", confirmationMsg: "[data-test='notification-success']",
channelAvailabilityItem: "[data-test='channel-availability-item']" channelAvailabilityItem: "[data-test='channel-availability-item']",
searchProducts: "[placeholder='Search Products...']",
availableManageButton:
"[data-test-id='channels-availiability-manage-button']",
channelsAvailabilityForm:
"[data-test-id='manage-products-channels-availiability-list']",
channelAvailabilityColumn:
"[data-test='availability'][data-test-availability='true']",
channelAvailabilityList: "ul[role='menu']",
goBackButton: "[data-test-id='app-header-back-button']",
assignedChannels: "[data-test='channel-availability-item']",
publishedRadioButtons: "[name*='isPublished']",
availableForPurchaseRadioButtons: "[name*='isAvailableForPurchase']",
radioButtonsValueTrue: "[value='true']",
radioButtonsValueFalse: "[value='false']",
visibleInListingsButton: "[name*='visibleInListings']",
emptyProductRow: "[class*='Skeleton']"
}; };

View file

@ -0,0 +1,12 @@
export const ADD_CHANNEL_FORM_SELECTORS = {
channelName: "[name='name']",
slug: "[name='slug']",
currency: "[data-test-id='channel-currency-select-input']",
currencyOptions: "[data-test='singleautocomplete-select-option']",
saveButton: "[data-test='button-bar-confirm']",
backToChannelsList: "[data-test-id='app-header-back-button']",
currencyValidationMessage: "[data-testid='currency-text-input-helper-text']",
slugValidationMessage: "[data-testid='slug-text-input-helper-text']",
currencyAutocompleteDropdown:
"[data-test='singleautocomplete-select-option'][data-test-type='custom']"
};

View file

@ -0,0 +1,5 @@
export const CHANNEL_FORM_SELECTORS = {
channelSelect: "[id='mui-component-select-channels']",
channelOption: "[data-test='selectFieldOption']",
confirmButton: "[data-test='submit']"
};

View file

@ -0,0 +1,5 @@
export const CHANNELS_SELECTORS = {
createChannelButton: "[data-test='add-channel']",
channelsTable: "[class='MuiTableBody-root']",
channelName: "[data-test='name']"
};

View file

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

View file

@ -0,0 +1,4 @@
export const HEADER_SELECTORS = {
channelSelect: "[data-test-id='app-channel-select']",
channelSelectList: "[class*='MuiMenu-paper']"
};

View file

@ -0,0 +1,9 @@
export const HOMEPAGE_SELECTORS = {
sales: "[data-test-id='sales-analytics']",
orders: "[data-test-id='orders-analytics']",
activity: "[data-test-id='activity-card']",
topProducts: "[data-test-id='top-products']",
ordersReadyToFulfill: "[data-test-id='orders-to-fulfill']",
paymentsWaitingForCapture: "[data-test-id='orders-to-capture']",
productsOutOfStock: "[data-test-id='products-out-of-stock']"
};

View file

@ -0,0 +1,3 @@
export const DRAFT_ORDER_SELECTORS = {
addProducts: "[data-test-id='add-products-button']"
};

View file

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

View file

@ -0,0 +1,13 @@
{
"plAddress": {
"companyName": "Test3",
"streetAddress1": "Smolna",
"streetAddress2": "13/1",
"city": "Wrocław",
"postalCode": "53-346",
"country": "PL",
"countryArea": "Dolny Śląsk",
"phone": "123456787",
"currency": "PLN"
}
}

View file

@ -0,0 +1,149 @@
// <reference types="cypress" />
import faker from "faker";
import Channels from "../apiRequests/Channels";
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
import { 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 { CONFIGURATION_SELECTORS } from "../elements/configuration/configuration-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 { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
import ChannelsSteps from "../steps/channelsSteps";
import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils";
describe("Channels", () => {
const channelStartsWith = "Cypress:";
const currency = "PLN";
const channels = new Channels();
const channelsUtils = new ChannelsUtils();
const channelsSteps = new ChannelsSteps();
before(() => {
cy.clearSessionData().loginUserViaRequest();
channelsUtils.deleteChannels(channelStartsWith);
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should navigate to channels page", () => {
cy.visit(urlList.homePage)
.get(LEFT_MENU_SELECTORS.configuration)
.click()
.get(CONFIGURATION_SELECTORS.channels)
.click()
.location("pathname")
.should("contain", "channels");
});
it("should create new channel", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels);
cy.wait("@Channels");
cy.addAliasToGraphRequest("Channel");
channelsSteps.createChannelByView(randomChannel, currency);
// New channel should be visible in channels list
cy.wait("@Channel")
.get(ADD_CHANNEL_FORM_SELECTORS.backToChannelsList)
.click()
.get(CHANNELS_SELECTORS.channelsTable)
.contains(randomChannel);
// new channel should be visible in channel selector
cy.visit(urlList.homePage)
.get(HEADER_SELECTORS.channelSelect)
.click()
.get(HEADER_SELECTORS.channelSelectList)
.contains(randomChannel)
.click();
// new channel should be visible at product availability form
cy.addAliasToGraphRequest("InitialProductFilterData");
cy.visit(urlList.products);
cy.wait("@InitialProductFilterData");
cy.get(PRODUCTS_SELECTORS.productsList)
.first()
.click()
.get(PRODUCTS_SELECTORS.availableManageButton)
.click()
.get(PRODUCTS_SELECTORS.channelsAvailabilityForm)
.contains(randomChannel);
});
it("should validate slug name", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.channels);
channelsSteps.createChannelByView(randomChannel, currency);
cy.get(ADD_CHANNEL_FORM_SELECTORS.slugValidationMessage).should(
"be.visible"
);
});
it("should validate currency", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
cy.visit(urlList.channels);
channelsSteps.createChannelByView(
randomChannel,
currency,
"notExistingCurrency"
);
cy.get(ADD_CHANNEL_FORM_SELECTORS.currencyValidationMessage).should(
"be.visible"
);
});
it("should delete channel", () => {
const randomChannelToDelete = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(
false,
randomChannelToDelete,
randomChannelToDelete,
currency
);
cy.addAliasToGraphRequest("Channels");
cy.visit(urlList.channels);
cy.wait("@Channels");
cy.get(CHANNELS_SELECTORS.channelName)
.contains(randomChannelToDelete)
.parentsUntil(CHANNELS_SELECTORS.channelsTable)
.find("button")
.click();
cy.addAliasToGraphRequest("Channels");
cy.get(BUTTON_SELECTORS.submit).click();
cy.wait("@Channels");
cy.get(CHANNELS_SELECTORS.channelName)
.contains(randomChannelToDelete)
.should("not.exist");
});
it("should not be possible to add products to order with inactive channel", () => {
const randomChannel = `${channelStartsWith} ${faker.random.number()}`;
channels.createChannel(false, randomChannel, randomChannel, currency);
cy.visit(urlList.orders)
.get(ORDERS_SELECTORS.createOrder)
.click()
.get(CHANNEL_FORM_SELECTORS.channelSelect)
.click()
.get(CHANNEL_FORM_SELECTORS.channelOption)
.contains(randomChannel)
.click()
.get(CHANNEL_FORM_SELECTORS.confirmButton)
.click();
cy.location()
.should(loc => {
const urlRegex = new RegExp(`${urlList.orders}.+`, "g");
expect(loc.pathname).to.match(urlRegex);
})
.get(DRAFT_ORDER_SELECTORS.addProducts)
.should("not.exist");
});
});

View file

@ -0,0 +1,235 @@
import faker from "faker";
import Customer from "../apiRequests/Customer";
import { HOMEPAGE_SELECTORS } from "../elements/homePage/homePage-selectors";
import HomePageSteps from "../steps/homePageSteps";
import { urlList } from "../url/urlList";
import ChannelsUtils from "../utils/channelsUtils";
import HomePageUtils from "../utils/homePageUtils";
import OrdersUtils from "../utils/ordersUtils";
import ProductsUtils from "../utils/productsUtils";
import ShippingUtils from "../utils/shippingUtils";
// <reference types="cypress" />
describe("Homepage analytics", () => {
const startsWith = "Cy-";
const customer = new Customer();
const productsUtils = new ProductsUtils();
const shippingUtils = new ShippingUtils();
const ordersUtils = new OrdersUtils();
const channelsUtils = new ChannelsUtils();
const homePageUtils = new HomePageUtils();
const homePageSteps = new HomePageSteps();
let customerId;
let defaultChannel;
const productPrice = 22;
const shippingPrice = 12;
const randomName = startsWith + faker.random.number();
const randomEmail = randomName + "@example.com";
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
customer.deleteCustomers(startsWith);
shippingUtils.deleteShipping(startsWith);
let addresses;
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
})
.then(addressesFixture => (addresses = addressesFixture))
.then(() =>
customer.createCustomer(randomEmail, randomName, addresses.plAddress)
)
.then(resp => {
customerId = resp.body.data.customerCreate.user.id;
shippingUtils.createShipping({
channelId: defaultChannel.id,
name: randomName,
address: addresses.plAddress,
price: shippingPrice
});
})
.then(() => {
productsUtils.createTypeAttributeAndCategoryForProduct(randomName);
})
.then(() => {
const warehouse = shippingUtils.getWarehouse();
const productType = productsUtils.getProductType();
const attribute = productsUtils.getAttribute();
const category = productsUtils.getCategory();
productsUtils.createProductInChannel({
name: randomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
quantityInWarehouse: 20,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
});
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should all elements be visible on the dashboard", () => {
cy.visit(urlList.homePage)
.softAssertVisibility(HOMEPAGE_SELECTORS.sales)
.softAssertVisibility(HOMEPAGE_SELECTORS.orders)
.softAssertVisibility(HOMEPAGE_SELECTORS.activity)
.softAssertVisibility(HOMEPAGE_SELECTORS.topProducts)
.softAssertVisibility(HOMEPAGE_SELECTORS.ordersReadyToFulfill)
.softAssertVisibility(HOMEPAGE_SELECTORS.paymentsWaitingForCapture)
.softAssertVisibility(HOMEPAGE_SELECTORS.productsOutOfStock);
});
it("should correct amount of ready to fullfil orders be displayed", () => {
homePageUtils
.getOrdersReadyToFulfill(defaultChannel.slug)
.as("ordersReadyToFulfill");
ordersUtils.createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
defaultChannel.id,
productsUtils.getCreatedVariants()
);
cy.get("@ordersReadyToFulfill").then(ordersReadyToFulfillBefore => {
const allOrdersReadyToFulfill = ordersReadyToFulfillBefore + 1;
const notANumberRegex = "\\D*";
const ordersReadyToFulfillRegexp = new RegExp(
`${notANumberRegex}${allOrdersReadyToFulfill}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.ordersReadyToFulfill,
ordersReadyToFulfillRegexp
).should("be.visible");
});
});
it("should correct amount of payments waiting for capture be displayed", () => {
homePageUtils
.getOrdersReadyForCapture(defaultChannel.slug)
.as("ordersReadyForCapture");
const variantsList = productsUtils.getCreatedVariants();
ordersUtils.createWaitingForCaptureOrder(
defaultChannel.slug,
randomEmail,
variantsList,
shippingUtils.getShippingMethod().id
);
cy.get("@ordersReadyForCapture").then(ordersReadyForCaptureBefore => {
const allOrdersReadyForCapture = ordersReadyForCaptureBefore + 1;
const notANumberRegex = "\\D*";
const ordersReadyForCaptureRegexp = new RegExp(
`${notANumberRegex}${allOrdersReadyForCapture}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.ordersReadyForCapture,
ordersReadyForCaptureRegexp
).should("be.visible");
});
});
it("should correct amount of products out of stock be displayed", () => {
homePageUtils
.getProductsOutOfStock(defaultChannel.slug)
.as("productsOutOfStock");
const productOutOfStockRandomName = startsWith + faker.random.number();
const productsOutOfStockUtils = new ProductsUtils();
const warehouse = shippingUtils.getWarehouse();
const productType = productsUtils.getProductType();
const attribute = productsUtils.getAttribute();
const category = productsUtils.getCategory();
productsOutOfStockUtils.createProductInChannel({
name: productOutOfStockRandomName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
quantityInWarehouse: 0,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
price: productPrice
});
cy.get("@productsOutOfStock").then(productsOutOfStockBefore => {
const allProductsOutOfStock = productsOutOfStockBefore + 1;
const notANumberRegex = "\\D*";
const productsOutOfStockRegexp = new RegExp(
`${notANumberRegex}${allProductsOutOfStock}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
cy.contains(
HOMEPAGE_SELECTORS.productsOutOfStock,
productsOutOfStockRegexp
).should("be.visible");
});
});
it("should correct amount of sales be displayed", () => {
homePageUtils.getSalesAmount(defaultChannel.slug).as("salesAmount");
ordersUtils.createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
defaultChannel.id,
productsUtils.getCreatedVariants()
);
cy.get("@salesAmount").then(salesAmount => {
const totalAmount = salesAmount + productPrice;
const totalAmountString = totalAmount.toFixed(2);
const totalAmountIntegerValue = totalAmountString.split(".")[0];
const totalAmountDecimalValue = totalAmountString.split(".")[1];
const decimalSeparator = "[,.]";
const totalAmountIntegerWithThousandsSeparator = totalAmountIntegerValue.replace(
/(\d)(?=(\d{3})+(?!\d))/g,
"1[,.]*"
);
const totalAmountWithSeparators = `${totalAmountIntegerWithThousandsSeparator}${decimalSeparator}${totalAmountDecimalValue}`;
const notANumberRegex = "\\D*";
const salesAmountRegexp = new RegExp(
`${notANumberRegex}${totalAmountWithSeparators}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.sales, salesAmountRegexp).should(
"be.visible"
);
});
});
it("should correct amount of orders be displayed", () => {
homePageUtils.getTodaysOrders(defaultChannel.slug).as("todaysOrders");
ordersUtils.createReadyToFulfillOrder(
customerId,
shippingUtils.getShippingMethod().id,
defaultChannel.id,
productsUtils.getCreatedVariants()
);
cy.get("@todaysOrders").then(ordersBefore => {
const allOrders = ordersBefore + 1;
const notANumberRegex = "\\D*";
const ordersRegexp = new RegExp(
`${notANumberRegex}${allOrders}${notANumberRegex}`
);
cy.visit(urlList.homePage);
homePageSteps.changeChannel(defaultChannel.name);
cy.contains(HOMEPAGE_SELECTORS.orders, ordersRegexp).should("be.visible");
});
});
});

View file

@ -1,19 +1,20 @@
import { LOGIN_SELECTORS } from "../elements/account/login-selectors";
// <reference types="cypress" /> // <reference types="cypress" />
import { LOGIN_SELECTORS } from "../elements/account/login-selectors";
import { urlList } from "../url/urlList";
describe("User authorization", () => { describe("User authorization", () => {
beforeEach(() => { beforeEach(() => {
cy.clearSessionData(); cy.clearSessionData();
}); });
it("should successfully log in an user", () => { it("should successfully log in an user", () => {
cy.visit("/"); cy.visit(urlList.homePage);
cy.loginUser(); cy.loginUser();
cy.get(LOGIN_SELECTORS.welcomePage); cy.get(LOGIN_SELECTORS.welcomePage);
}); });
it("should fail for wrong password", () => { it("should fail for wrong password", () => {
cy.visit("/") cy.visit(urlList.homePage)
.get(LOGIN_SELECTORS.emailAddressInput) .get(LOGIN_SELECTORS.emailAddressInput)
.type("admin@example.com") .type("admin@example.com")
.get(LOGIN_SELECTORS.emailPasswordInput) .get(LOGIN_SELECTORS.emailPasswordInput)
@ -27,7 +28,7 @@ describe("User authorization", () => {
cy.window().then(win => { cy.window().then(win => {
win.sessionStorage.clear(); win.sessionStorage.clear();
}); });
cy.visit("/"); cy.visit(urlList.homePage);
cy.loginUser(); cy.loginUser();
cy.get(LOGIN_SELECTORS.userMenu) cy.get(LOGIN_SELECTORS.userMenu)
.click() .click()

View file

@ -0,0 +1,114 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import ShippingUtils from "../../../utils/shippingUtils";
import { isProductAvailableForPurchase } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Products available in listings", () => {
const shippingUtils = new ShippingUtils();
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
let attribute;
let category;
let defaultChannel;
let warehouse;
before(() => {
cy.clearSessionData().loginUserViaRequest();
shippingUtils.deleteShipping(startsWith);
productsUtils.deleteProperProducts(startsWith);
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
cy.fixture("addresses");
})
.then(addressesFixture => {
shippingUtils.createShipping({
channelId: defaultChannel.id,
name,
address: addressesFixture.plAddress
});
})
.then(() => {
warehouse = shippingUtils.getWarehouse();
});
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should update product to available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`;
productsUtils
.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
isAvailableForPurchase: false
})
.then(() => {
const productUrl = productDetailsUrl(
productsUtils.getCreatedProduct().id
);
productSteps.updateProductIsAvailableForPurchase(productUrl, true);
})
.then(() => {
isProductAvailableForPurchase(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
})
.then(isVisibleResp => {
expect(isVisibleResp).to.be.eq(true);
});
});
it("should update product to not available for purchase", () => {
const productName = `${startsWith}${faker.random.number()}`;
productsUtils
.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
warehouseId: warehouse.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id
})
.then(() => {
const productUrl = productDetailsUrl(
productsUtils.getCreatedProduct().id
);
productSteps.updateProductIsAvailableForPurchase(productUrl, false);
})
.then(() => {
isProductAvailableForPurchase(
productsUtils.getCreatedProduct().id,
defaultChannel.slug,
productName
);
})
.then(isProductVisible => {
expect(isProductVisible).to.be.eq(false);
});
});
});

View file

@ -0,0 +1,95 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import { isProductVisible } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Published products", () => {
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
let attribute;
let category;
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should update product to published", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
isPublished: false,
isAvailableForPurchase: false
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, true);
isProductVisible(product.id, defaultChannel.slug, productName);
})
.then(isVisible => {
expect(isVisible).to.be.eq(true);
});
});
it("should update product to not published", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
let product;
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id
});
})
.then(() => {
product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductPublish(productUrl, false);
isProductVisible(product.id, defaultChannel.slug, productName);
})
.then(isVisible => {
expect(isVisible).to.be.eq(false);
cy.loginInShop();
})
.then(() => {
isProductVisible(product.id, defaultChannel.slug, productName);
})
.then(isVisible => {
expect(isVisible).to.be.eq(true);
});
});
});

View file

@ -0,0 +1,95 @@
import faker from "faker";
import ProductSteps from "../../../steps/productSteps";
import { productDetailsUrl } from "../../../url/urlList";
import ChannelsUtils from "../../../utils/channelsUtils";
import ProductsUtils from "../../../utils/productsUtils";
import { isProductVisibleInSearchResult } from "../../../utils/storeFront/storeFrontProductUtils";
// <reference types="cypress" />
describe("Products displayed in listings", () => {
const channelsUtils = new ChannelsUtils();
const productsUtils = new ProductsUtils();
const productSteps = new ProductSteps();
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
let productType;
let attribute;
let category;
before(() => {
cy.clearSessionData().loginUserViaRequest();
productsUtils.deleteProperProducts(startsWith);
productsUtils.createTypeAttributeAndCategoryForProduct(name).then(() => {
productType = productsUtils.getProductType();
attribute = productsUtils.getAttribute();
category = productsUtils.getCategory();
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should update product to visible in listings", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
visibleInListings: false,
isAvailableForPurchase: false
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug);
})
.then(isProductVisible => {
expect(isProductVisible).to.be.eq(true);
});
});
it("should update product to not visible in listings", () => {
const productName = `${startsWith}${faker.random.number()}`;
let defaultChannel;
channelsUtils
.getDefaultChannel()
.then(channel => {
defaultChannel = channel;
productsUtils.createProductInChannel({
name: productName,
channelId: defaultChannel.id,
productTypeId: productType.id,
attributeId: attribute.id,
categoryId: category.id,
visibleInListings: true
});
})
.then(() => {
const product = productsUtils.getCreatedProduct();
const productUrl = productDetailsUrl(product.id);
productSteps.updateProductVisibleInListings(productUrl);
isProductVisibleInSearchResult(productName, defaultChannel.slug).then(
isProductVisible => {
expect(isProductVisible).to.be.eq(false);
}
);
cy.loginInShop();
})
.then(() => {
isProductVisibleInSearchResult(productName, defaultChannel.slug);
})
.then(isProductVisible => {
expect(isProductVisible).to.be.eq(true);
});
});
});

View file

@ -1,18 +1,25 @@
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
// <reference types="cypress" /> // <reference types="cypress" />
import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../../elements/catalog/product-selectors";
import { urlList } from "../../url/urlList";
describe("Products", () => { describe("Products", () => {
beforeEach(() => { beforeEach(() => {
cy.clearSessionData().loginUserViaRequest(); cy.clearSessionData().loginUserViaRequest();
}); });
it("should add new visible product", () => { it("should navigate to channels page", () => {
cy.visit("/") cy.visit(urlList.homePage)
.get(LEFT_MENU_SELECTORS.catalog) .get(LEFT_MENU_SELECTORS.catalog)
.click() .click()
.get(PRODUCTS_SELECTORS.products) .get(LEFT_MENU_SELECTORS.products)
.click() .click()
.location("pathname")
.should("contain", "/products");
});
it("should add new visible product", () => {
cy.visit(urlList.products)
.get(PRODUCTS_SELECTORS.createProductBtn) .get(PRODUCTS_SELECTORS.createProductBtn)
.click() .click()
.get(PRODUCTS_SELECTORS.productNameInput) .get(PRODUCTS_SELECTORS.productNameInput)

View file

@ -1,11 +1,13 @@
// <reference types="cypress" /> // <reference types="cypress" />
import { urlList } from "../url/urlList";
describe("Warehouse settings", () => { describe("Warehouse settings", () => {
beforeEach(() => { beforeEach(() => {
cy.clearSessionData(); cy.clearSessionData();
}); });
xit("Warehouse section visible in the configuration", () => { xit("Warehouse section visible in the configuration", () => {
cy.visit("/configuration/") cy.visit(urlList.configuration)
.loginUser() .loginUser()
.get("[data-test-id=warehouses][data-test=settingsSubsection]") .get("[data-test-id=warehouses][data-test=settingsSubsection]")
.click(); .click();
@ -13,7 +15,7 @@ describe("Warehouse settings", () => {
}); });
xit("Editing warehouse is available", () => { xit("Editing warehouse is available", () => {
cy.visit(`/warehouses`) cy.visit(urlList.warehouses)
.loginUser() .loginUser()
.get("[data-test=editButton]") .get("[data-test=editButton]")
.first() .first()

View file

@ -16,7 +16,13 @@
* @type {Cypress.PluginConfig} * @type {Cypress.PluginConfig}
*/ */
module.exports = (on, config) => { module.exports = (on, config) => {
// make env variables visible for cypress
config.env.API_URI = process.env.API_URI; config.env.API_URI = process.env.API_URI;
config.env.APP_MOUNT_URI = process.env.APP_MOUNT_URI;
on("before:browser:launch", (browser = {}, launchOptions) => {
launchOptions.args.push("--proxy-bypass-list=<-loopback>");
return launchOptions;
});
return config; return config;
}; };

View file

@ -0,0 +1,26 @@
import { ADD_CHANNEL_FORM_SELECTORS } from "../elements/channels/add-channel-form-selectors";
import { CHANNELS_SELECTORS } from "../elements/channels/channels-selectors";
class ChannelsSteps {
createChannelByView(name, currency, otherCurrency, slug = name) {
cy.get(CHANNELS_SELECTORS.createChannelButton)
.click()
.get(ADD_CHANNEL_FORM_SELECTORS.channelName)
.type(name)
.get(ADD_CHANNEL_FORM_SELECTORS.slug)
.type(slug)
.get(ADD_CHANNEL_FORM_SELECTORS.currency)
.click();
if (!otherCurrency) {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency).type(currency);
cy.get(`[data-test-value=${currency}]`).click();
} else {
cy.get(ADD_CHANNEL_FORM_SELECTORS.currency)
.type(otherCurrency)
.get(ADD_CHANNEL_FORM_SELECTORS.currencyAutocompleteDropdown)
.click();
}
cy.get(ADD_CHANNEL_FORM_SELECTORS.saveButton).click();
}
}
export default ChannelsSteps;

View file

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

View file

@ -0,0 +1,37 @@
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,18 @@
Cypress.Commands.add(
"handleDeleteElement",
(element, deleteFunction, startsWith) => {
if (element.node.name.includes(startsWith)) {
deleteFunction(element.node.id);
}
}
);
Cypress.Commands.add(
"deleteProperElements",
(deleteFunction, getFunction, startsWith, name) => {
getFunction(100, startsWith).then(elements => {
elements.forEach(element => {
cy.handleDeleteElement(element, deleteFunction, startsWith);
});
});
}
);

View file

@ -1,19 +1,55 @@
import "./user"; import "./user";
import "./softAssertions";
import "./deleteElement/index.js";
import { urlList } from "../url/urlList";
Cypress.Commands.add("clearSessionData", () => { Cypress.Commands.add("clearSessionData", () => {
// Because of known cypress bug, not all local storage data are cleared. // Because of known cypress bug, not all local storage data are cleared.
// Here is workaround to ensure tests have no side effects. // Here is workaround to ensure tests have no side effects.
// Suggested usage: // Suggested usage:
// beforeEach(() => { // beforeEach(() => {
// cy.clearSessionData(); // cy.clearSessionData();
// }); // });
cy.clearCookies(); cy.clearCookies();
cy.clearLocalStorage(); cy.clearLocalStorage();
cy.visit("/", { cy.visit(urlList.homePage, {
onBeforeLoad: win => { onBeforeLoad: win => {
win.sessionStorage.clear(); win.sessionStorage.clear();
} }
}); });
}); });
Cypress.Commands.add("addAliasToGraphRequest", operationName => {
cy.intercept("POST", urlList.apiUri, req => {
req.statusCode = 200;
const requestBody = req.body;
if (Array.isArray(requestBody)) {
requestBody.forEach(element => {
if (element.operationName === operationName) {
req.alias = operationName;
}
});
} else {
if (requestBody.operationName === operationName) {
req.alias = operationName;
}
}
});
});
Cypress.Commands.add("sendRequestWithQuery", (query, authorization = "auth") =>
cy.request({
body: {
method: "POST",
query,
url: urlList.apiUri
},
headers: {
Authorization: `JWT ${window.sessionStorage.getItem(authorization)}`
},
method: "POST",
url: urlList.apiUri
})
);

View file

@ -0,0 +1,89 @@
let isSoftAssertion = false;
let errors = [];
chai.softExpect = function(...args) {
isSoftAssertion = true;
return chai.expect(...args);
};
chai.softAssert = function(...args) {
isSoftAssertion = true;
return chai.assert(...args);
};
const origAssert = chai.Assertion.prototype.assert;
chai.Assertion.prototype.assert = function(...args) {
if (isSoftAssertion) {
try {
origAssert.call(this, ...args);
} catch (error) {
errors.push(error);
}
isSoftAssertion = false;
} else {
origAssert.call(this, ...args);
}
};
// monkey-patch `Cypress.log` so that the last `cy.then()` isn't logged to command log
const origLog = Cypress.log;
Cypress.log = function(data) {
if (data && data.error && /soft assertions/i.test(data.error.message)) {
data.error.message = "\n\n\t" + data.error.message + "\n\n";
throw data.error;
}
return origLog.call(Cypress, ...arguments);
};
// monkey-patch `it` callback so we insert `cy.then()` as a last command
// to each test case where we'll assert if there are any soft assertion errors
function itCallback(func) {
func();
cy.then(() => {
if (errors.length) {
const _ = Cypress._;
let msg = "";
if (Cypress.browser.isHeaded) {
msg = "Failed soft assertions... check log above ↑";
} else {
_.each(errors, error => {
msg += "\n" + error;
});
msg = msg.replace(/^/gm, "\t");
}
throw new Error(msg);
}
});
}
const origIt = window.it;
window.it = (title, func) => {
origIt(title, func && (() => itCallback(func)));
};
window.it.only = (title, func) => {
origIt.only(title, func && (() => itCallback(func)));
};
window.it.skip = (title, func) => {
origIt.skip(title, func);
};
beforeEach(() => {
errors = [];
});
afterEach(() => {
errors = [];
isSoftAssertion = false;
});
Cypress.Commands.add("softAssertMatch", (selector, regexp) => {
cy.get(selector)
.invoke("text")
.then(text =>
chai.softExpect(assert.match(text, regexp, "regexp matches"))
);
});
Cypress.Commands.add("softAssertVisibility", selector => {
cy.get(selector).then(element => chai.softExpect(element).to.be.visible);
});

View file

@ -1,4 +1,3 @@
/* eslint-disable sort-keys */
import { LOGIN_SELECTORS } from "../../elements/account/login-selectors"; import { LOGIN_SELECTORS } from "../../elements/account/login-selectors";
Cypress.Commands.add("loginUser", () => Cypress.Commands.add("loginUser", () =>
@ -11,38 +10,30 @@ Cypress.Commands.add("loginUser", () =>
.click() .click()
); );
Cypress.Commands.add("loginUserViaRequest", () => { Cypress.Commands.add("loginInShop", () => {
const logInMutationQuery = `mutation TokenAuth($email: String!, $password: String!) { cy.loginUserViaRequest("token");
tokenCreate(email: $email, password: $password) { });
Cypress.Commands.add("loginUserViaRequest", (authorization = "auth") => {
const mutation = `mutation TokenAuth{
tokenCreate(email: "${Cypress.env("USER_NAME")}", password: "${Cypress.env(
"USER_PASSWORD"
)}") {
token token
errors: accountErrors { errors: accountErrors {
code code
field field
message message
__typename
} }
user { user {
id id
__typename
} }
__typename
} }
}`; }`;
return cy.sendRequestWithQuery(mutation, authorization).then(resp => {
return cy window.sessionStorage.setItem(
.request({ authorization,
method: "POST", resp.body.data.tokenCreate.token
url: Cypress.env("API_URI"), );
body: { });
operationName: "TokenAuth",
variables: {
email: Cypress.env("USER_NAME"),
password: Cypress.env("USER_PASSWORD")
},
query: logInMutationQuery
}
})
.then(resp => {
window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token);
});
}); });

10
cypress/url/urlList.js Normal file
View file

@ -0,0 +1,10 @@
export const urlList = {
apiUri: Cypress.env("API_URI"),
channels: "channels/",
configuration: "configuration/",
homePage: "/",
orders: "orders/",
products: "products/",
warehouses: "warehouses/"
};
export const productDetailsUrl = productId => `${urlList.products}${productId}`;

View file

@ -0,0 +1,41 @@
import Channels from "../apiRequests/Channels";
class ChannelsUtils {
channels = new Channels();
deleteChannels(nameStartsWith) {
this.channels.getChannels().then(resp => {
const channelsArray = new Set(resp.body.data.channels);
if (!channelsArray) {
return;
}
channelsArray.forEach(element => {
if (element.name.startsWith(nameStartsWith)) {
const targetChannels = Array.from(channelsArray).filter(function(
channelElement
) {
return (
element.currencyCode === channelElement.currencyCode &&
element.id !== channelElement.id
);
});
if (targetChannels[0]) {
this.channels.deleteChannel(element.id, targetChannels[0].id);
channelsArray.delete(element);
}
}
});
});
}
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;

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,32 @@
import ProductDetails from "../../apiRequests/storeFront/ProductDetails";
import Search from "../../apiRequests/storeFront/Search";
export const isProductVisible = (productId, channelSlug, name) => {
const productDetails = new ProductDetails();
return productDetails
.getProductDetails(productId, channelSlug)
.then(productDetailsResp => {
const product = productDetailsResp.body.data.product;
return product !== null && product.name === name;
});
};
export const isProductAvailableForPurchase = (productId, channelSlug) => {
const productDetails = new ProductDetails();
return productDetails
.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

@ -5603,13 +5603,6 @@
"context": "label", "context": "label",
"string": "Shipping rate name" "string": "Shipping rate name"
}, },
"src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_3640694505": {
"string": "Are you sure you want to remove this postal code rule?"
},
"src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_76039652": {
"context": "header",
"string": "Remove postal codes from Shipping Rate"
},
"src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_2863708228": { "src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_2863708228": {
"string": "This unit will be used as default shipping weight" "string": "This unit will be used as default shipping weight"
}, },
@ -5693,6 +5686,54 @@
"context": "label", "context": "label",
"string": "Shipping zone name" "string": "Shipping zone name"
}, },
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodeRangeDialog_dot_3070993206": {
"context": "range input label",
"string": "Postal codes (end)"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodeRangeDialog_dot_3099331554": {
"context": "add postal code range, button",
"string": "Add"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodeRangeDialog_dot_3419096551": {
"context": "range input label",
"string": "Postal codes (start)"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodeRangeDialog_dot_3668595137": {
"string": "Please provide range of postal codes you want to add to the include/exclude list."
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodeRangeDialog_dot_3849853790": {
"context": "dialog header",
"string": "Add postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_1301350004": {
"string": "This shipping rate has no postal codes assigned"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_1680649143": {
"context": "action",
"string": "Include postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_1779803917": {
"string": "Added postal codes will be excluded from using this delivery methods. If none are added all postal codes will be able to use that shipping rate"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_1909179974": {
"context": "button",
"string": "Add postal code range"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_2274108851": {
"context": "number of postal code ranges",
"string": "{number} postal code ranges"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_2965119249": {
"string": "Only added postal codes will be able to use this shipping rate"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_3782353530": {
"context": "postal codes, header",
"string": "Postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZonePostalCodes_dot_399764149": {
"context": "action",
"string": "Exclude postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1161979494": { "src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1161979494": {
"context": "page title", "context": "page title",
"string": "Price Rate Create" "string": "Price Rate Create"
@ -5753,54 +5794,6 @@
"context": "input placeholder", "context": "input placeholder",
"string": "Select Warehouse" "string": "Select Warehouse"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodeRangeDialog_dot_3070993206": {
"context": "range input label",
"string": "Postal codes (end)"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodeRangeDialog_dot_3099331554": {
"context": "add postal code range, button",
"string": "Add"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodeRangeDialog_dot_3419096551": {
"context": "range input label",
"string": "Postal codes (start)"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodeRangeDialog_dot_3668595137": {
"string": "Please provide range of postal codes you want to add to the include/exclude list."
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodeRangeDialog_dot_3849853790": {
"context": "dialog header",
"string": "Add postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_1301350004": {
"string": "This shipping rate has no postal codes assigned"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_1680649143": {
"context": "action",
"string": "Include postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_1779803917": {
"string": "Added postal codes will be excluded from using this delivery methods. If none are added all postal codes will be able to use that shipping rate"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_1909179974": {
"context": "button",
"string": "Add postal code range"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_2274108851": {
"context": "number of postal code ranges",
"string": "{number} postal code ranges"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_2965119249": {
"string": "Only added postal codes will be able to use this shipping rate"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_3782353530": {
"context": "postal codes, header",
"string": "Postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZoneZipCodes_dot_399764149": {
"context": "action",
"string": "Exclude postal codes"
},
"src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1325966144": { "src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1325966144": {
"context": "header", "context": "header",
"string": "Shipping" "string": "Shipping"
@ -5860,10 +5853,6 @@
"src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_3823295269": { "src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_3823295269": {
"string": "Manage Channel Availability" "string": "Manage Channel Availability"
}, },
"src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_4243341946": {
"context": "postal code range add error text",
"string": "Cannot add specified postal codes range."
},
"src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_870815507": { "src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_870815507": {
"context": "unassign products from shipping method, button", "context": "unassign products from shipping method, button",
"string": "Unassign" "string": "Unassign"
@ -5886,10 +5875,6 @@
"src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_3014453080": { "src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_3014453080": {
"string": "Manage Channels Availability" "string": "Manage Channels Availability"
}, },
"src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_4243341946": {
"context": "postal code range add error text",
"string": "Cannot add specified postal codes range."
},
"src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_870815507": { "src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_870815507": {
"context": "unassign products from shipping method, button", "context": "unassign products from shipping method, button",
"string": "Unassign" "string": "Unassign"
@ -6767,6 +6752,10 @@
"context": "event", "context": "event",
"string": "All events" "string": "All events"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2745028894": {
"context": "event",
"string": "Page deleted"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2862596150": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2862596150": {
"context": "event", "context": "event",
"string": "Invoice sent" "string": "Invoice sent"
@ -6795,6 +6784,14 @@
"context": "event", "context": "event",
"string": "Order cancelled" "string": "Order cancelled"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3618648517": {
"context": "event",
"string": "Page updated"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3671033983": {
"context": "event",
"string": "Product deleted"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3907151399": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3907151399": {
"context": "event", "context": "event",
"string": "Order fulfilled" "string": "Order fulfilled"
@ -6803,10 +6800,6 @@
"context": "event", "context": "event",
"string": "Customer created" "string": "Customer created"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_40035964": {
"context": "event",
"string": "Changed quantity in checkout"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_4186057882": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_4186057882": {
"context": "event", "context": "event",
"string": "Invoice requested" "string": "Invoice requested"
@ -6815,6 +6808,14 @@
"context": "event", "context": "event",
"string": "Fulfillment created" "string": "Fulfillment created"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_679080833": {
"context": "event",
"string": "Page created"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_787792649": {
"context": "event",
"string": "Customer updated"
},
"src_dot_webhooks_dot_components_dot_WebhookInfo_dot_1690209105": { "src_dot_webhooks_dot_components_dot_WebhookInfo_dot_1690209105": {
"context": "webhook", "context": "webhook",
"string": "Target URL" "string": "Target URL"

4
lws.config.js Normal file
View file

@ -0,0 +1,4 @@
module.exports = {
directory: "build/dashboard/",
port: 9000
};

29032
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "saleor-dashboard", "name": "saleor-dashboard",
"version": "3.0.0-a.0", "version": "3.0.0-a.5",
"main": "src/index.tsx", "main": "src/index.tsx",
"repository": { "repository": {
"type": "git", "type": "git",
@ -26,6 +26,7 @@
"@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",
"apollo": "^2.21.2", "apollo": "^2.21.2",
"apollo-cache-inmemory": "^1.6.5", "apollo-cache-inmemory": "^1.6.5",
@ -42,7 +43,9 @@
"downshift": "^1.31.16", "downshift": "^1.31.16",
"editorjs-inline-tool": "^0.4.0", "editorjs-inline-tool": "^0.4.0",
"editorjs-undo": "^0.1.4", "editorjs-undo": "^0.1.4",
"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",
@ -130,7 +133,7 @@
"codecov": "^3.7.1", "codecov": "^3.7.1",
"core-js": "^3.7.0", "core-js": "^3.7.0",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"cypress": "^4.9.0", "cypress": "^6.4.0",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5", "enzyme-adapter-react-16": "^1.15.5",
"enzyme-to-json": "^3.6.1", "enzyme-to-json": "^3.6.1",

View file

@ -671,7 +671,7 @@ type Category implements Node & ObjectWithMetadata {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
slug: String! slug: String!
parent: Category parent: Category
level: Int! level: Int!
@ -746,7 +746,7 @@ type CategoryTranslatableContent implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
translation(languageCode: LanguageCodeEnum!): CategoryTranslation translation(languageCode: LanguageCodeEnum!): CategoryTranslation
category: Category category: Category
@ -763,7 +763,7 @@ type CategoryTranslation implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
language: LanguageDisplay! language: LanguageDisplay!
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
} }
@ -1050,7 +1050,7 @@ type Collection implements Node & ObjectWithMetadata {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
slug: String! slug: String!
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
@ -1205,7 +1205,7 @@ type CollectionTranslatableContent implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
translation(languageCode: LanguageCodeEnum!): CollectionTranslation translation(languageCode: LanguageCodeEnum!): CollectionTranslation
collection: Collection collection: Collection
@ -1222,7 +1222,7 @@ type CollectionTranslation implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
name: String! name: String!
description: JSONString! description: JSONString
language: LanguageDisplay! language: LanguageDisplay!
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
} }
@ -2055,10 +2055,6 @@ type GatewayConfigLine {
scalar GenericScalar scalar GenericScalar
type Geolocalization {
country: CountryDisplay
}
type GiftCard implements Node { type GiftCard implements Node {
code: String code: String
user: User user: User
@ -2590,8 +2586,6 @@ type Mutation {
shopAddressUpdate(input: AddressInput): ShopAddressUpdate shopAddressUpdate(input: AddressInput): ShopAddressUpdate
orderSettingsUpdate(input: OrderSettingsUpdateInput!): OrderSettingsUpdate orderSettingsUpdate(input: OrderSettingsUpdateInput!): OrderSettingsUpdate
shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate
shippingMethodZipCodeRulesCreate(input: ShippingZipCodeRulesCreateInput!, shippingMethodId: ID!): ShippingZipCodeRulesCreate
shippingMethodZipCodeRulesDelete(id: ID!): ShippingZipCodeRulesDelete
shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate
shippingPriceDelete(id: ID!): ShippingPriceDelete shippingPriceDelete(id: ID!): ShippingPriceDelete
shippingPriceBulkDelete(ids: [ID]!): ShippingPriceBulkDelete shippingPriceBulkDelete(ids: [ID]!): ShippingPriceBulkDelete
@ -3311,7 +3305,7 @@ type Page implements Node & ObjectWithMetadata {
seoDescription: String seoDescription: String
id: ID! id: ID!
title: String! title: String!
content: JSONString! content: JSONString
publicationDate: Date publicationDate: Date
isPublished: Boolean! isPublished: Boolean!
slug: String! slug: String!
@ -3319,7 +3313,7 @@ type Page implements Node & ObjectWithMetadata {
created: DateTime! created: DateTime!
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
contentJson: String! @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") contentJson: JSONString! @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.")
translation(languageCode: LanguageCodeEnum!): PageTranslation translation(languageCode: LanguageCodeEnum!): PageTranslation
attributes: [SelectedAttribute!]! attributes: [SelectedAttribute!]!
} }
@ -3445,8 +3439,8 @@ type PageTranslatableContent implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
title: String! title: String!
content: JSONString! content: JSONString
contentJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") contentJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.")
translation(languageCode: LanguageCodeEnum!): PageTranslation translation(languageCode: LanguageCodeEnum!): PageTranslation
page: Page page: Page
} }
@ -3462,9 +3456,9 @@ type PageTranslation implements Node {
seoDescription: String seoDescription: String
id: ID! id: ID!
title: String! title: String!
content: JSONString! content: JSONString
language: LanguageDisplay! language: LanguageDisplay!
contentJson: String @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.") contentJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `content` field instead.")
} }
input PageTranslationInput { input PageTranslationInput {
@ -3657,7 +3651,6 @@ input PaymentInput {
gateway: String! gateway: String!
token: String token: String
amount: PositiveDecimal amount: PositiveDecimal
billingAddress: AddressInput
returnUrl: String returnUrl: String
} }
@ -3829,6 +3822,11 @@ input PluginUpdateInput {
scalar PositiveDecimal scalar PositiveDecimal
enum PostalCodeRuleInclusionTypeEnum {
INCLUDE
EXCLUDE
}
input PriceRangeInput { input PriceRangeInput {
gte: Float gte: Float
lte: Float lte: Float
@ -3839,7 +3837,7 @@ type Product implements Node & ObjectWithMetadata {
seoTitle: String seoTitle: String
seoDescription: String seoDescription: String
name: String! name: String!
description: JSONString! description: JSONString
productType: ProductType! productType: ProductType!
slug: String! slug: String!
category: Category category: Category
@ -3853,8 +3851,8 @@ type Product implements Node & ObjectWithMetadata {
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
url: String! @deprecated(reason: "This field will be removed after 2020-07-31.") url: String! @deprecated(reason: "This field will be removed after 2020-07-31.")
thumbnail(size: Int): Image thumbnail(size: Int): Image
pricing: ProductPricingInfo pricing(address: AddressInput): ProductPricingInfo
isAvailable: Boolean isAvailable(address: AddressInput): Boolean
taxType: TaxType taxType: TaxType
attributes: [SelectedAttribute!]! attributes: [SelectedAttribute!]!
channelListings: [ProductChannelListing!] channelListings: [ProductChannelListing!]
@ -3906,7 +3904,7 @@ type ProductChannelListing implements Node {
purchaseCost: MoneyRange purchaseCost: MoneyRange
margin: Margin margin: Margin
isAvailableForPurchase: Boolean isAvailableForPurchase: Boolean
pricing: ProductPricingInfo pricing(address: AddressInput): ProductPricingInfo
} }
input ProductChannelListingAddInput { input ProductChannelListingAddInput {
@ -4109,6 +4107,7 @@ input ProductOrder {
enum ProductOrderField { enum ProductOrderField {
NAME NAME
RANK
PRICE PRICE
MINIMAL_PRICE MINIMAL_PRICE
DATE DATE
@ -4144,7 +4143,7 @@ type ProductTranslatableContent implements Node {
seoTitle: String seoTitle: String
seoDescription: String seoDescription: String
name: String! name: String!
description: JSONString! description: JSONString
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
translation(languageCode: LanguageCodeEnum!): ProductTranslation translation(languageCode: LanguageCodeEnum!): ProductTranslation
product: Product product: Product
@ -4161,7 +4160,7 @@ type ProductTranslation implements Node {
seoTitle: String seoTitle: String
seoDescription: String seoDescription: String
name: String! name: String!
description: JSONString! description: JSONString
language: LanguageDisplay! language: LanguageDisplay!
descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.") descriptionJson: JSONString @deprecated(reason: "Will be removed in Saleor 4.0. Use the `description` field instead.")
} }
@ -4177,7 +4176,6 @@ type ProductType implements Node & ObjectWithMetadata {
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "Use the top-level `products` query with the `productTypes` filter.") products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "Use the top-level `products` query with the `productTypes` filter.")
taxRate: TaxRateType
taxType: TaxType taxType: TaxType
variantAttributes(variantSelection: VariantAttributeScope): [Attribute] variantAttributes(variantSelection: VariantAttributeScope): [Attribute]
productAttributes: [Attribute] productAttributes: [Attribute]
@ -4281,7 +4279,7 @@ type ProductVariant implements Node & ObjectWithMetadata {
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
channelListings: [ProductVariantChannelListing!] channelListings: [ProductVariantChannelListing!]
pricing: VariantPricingInfo pricing(address: AddressInput): VariantPricingInfo
attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]! attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]!
costPrice: Money costPrice: Money
margin: Int margin: Int
@ -4290,8 +4288,8 @@ type ProductVariant implements Node & ObjectWithMetadata {
images: [ProductImage] images: [ProductImage]
translation(languageCode: LanguageCodeEnum!): ProductVariantTranslation translation(languageCode: LanguageCodeEnum!): ProductVariantTranslation
digitalContent: DigitalContent digitalContent: DigitalContent
stocks(countryCode: CountryCode): [Stock] stocks(address: AddressInput, countryCode: CountryCode): [Stock]
quantityAvailable(countryCode: CountryCode): Int! quantityAvailable(address: AddressInput, countryCode: CountryCode): Int!
} }
type ProductVariantBulkCreate { type ProductVariantBulkCreate {
@ -4751,7 +4749,7 @@ type ShippingMethod implements Node & ObjectWithMetadata {
price: Money price: Money
maximumOrderPrice: Money maximumOrderPrice: Money
minimumOrderPrice: Money minimumOrderPrice: Money
zipCodeRules: [ShippingMethodZipCodeRule] postalCodeRules: [ShippingMethodPostalCodeRule]
excludedProducts(before: String, after: String, first: Int, last: Int): ProductCountableConnection excludedProducts(before: String, after: String, first: Int, last: Int): ProductCountableConnection
} }
@ -4781,6 +4779,13 @@ type ShippingMethodChannelListingUpdate {
shippingErrors: [ShippingError!]! shippingErrors: [ShippingError!]!
} }
type ShippingMethodPostalCodeRule implements Node {
start: String
end: String
inclusionType: PostalCodeRuleInclusionTypeEnum
id: ID!
}
type ShippingMethodTranslatableContent implements Node { type ShippingMethodTranslatableContent implements Node {
id: ID! id: ID!
name: String! name: String!
@ -4799,10 +4804,9 @@ enum ShippingMethodTypeEnum {
WEIGHT WEIGHT
} }
type ShippingMethodZipCodeRule implements Node { input ShippingPostalCodeRulesCreateInputRange {
start: String start: String!
end: String end: String
id: ID!
} }
type ShippingPriceBulkDelete { type ShippingPriceBulkDelete {
@ -4843,6 +4847,9 @@ input ShippingPriceInput {
minimumDeliveryDays: Int minimumDeliveryDays: Int
type: ShippingMethodTypeEnum type: ShippingMethodTypeEnum
shippingZone: ID shippingZone: ID
addPostalCodeRules: [ShippingPostalCodeRulesCreateInputRange!]
deletePostalCodeRules: [ID!]
inclusionType: PostalCodeRuleInclusionTypeEnum
} }
type ShippingPriceRemoveProductFromExclude { type ShippingPriceRemoveProductFromExclude {
@ -4864,29 +4871,6 @@ type ShippingPriceUpdate {
shippingErrors: [ShippingError!]! shippingErrors: [ShippingError!]!
} }
type ShippingZipCodeRulesCreate {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
zipCodeRules: [ShippingMethodZipCodeRule]
shippingMethod: ShippingMethod
shippingErrors: [ShippingError!]!
}
input ShippingZipCodeRulesCreateInput {
zipCodeRules: [ShippingZipCodeRulesCreateInputRange]!
}
input ShippingZipCodeRulesCreateInputRange {
start: String!
end: String
}
type ShippingZipCodeRulesDelete {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
shippingMethod: ShippingMethod
shippingErrors: [ShippingError!]!
shippingMethodZipCodeRule: ShippingMethodZipCodeRule
}
type ShippingZone implements Node & ObjectWithMetadata { type ShippingZone implements Node & ObjectWithMetadata {
id: ID! id: ID!
name: String! name: String!
@ -4956,7 +4940,6 @@ type Shop {
availablePaymentGateways(currency: String): [PaymentGateway!]! availablePaymentGateways(currency: String): [PaymentGateway!]!
availableExternalAuthentications: [ExternalAuthentication!]! availableExternalAuthentications: [ExternalAuthentication!]!
availableShippingMethods(channel: String!, address: AddressInput): [ShippingMethod] availableShippingMethods(channel: String!, address: AddressInput): [ShippingMethod]
geolocalization: Geolocalization
countries(languageCode: LanguageCodeEnum): [CountryDisplay!]! countries(languageCode: LanguageCodeEnum): [CountryDisplay!]!
defaultCountry: CountryDisplay defaultCountry: CountryDisplay
defaultMailSenderName: String defaultMailSenderName: String
@ -5833,12 +5816,16 @@ enum WebhookEventTypeEnum {
INVOICE_DELETED INVOICE_DELETED
INVOICE_SENT INVOICE_SENT
CUSTOMER_CREATED CUSTOMER_CREATED
CUSTOMER_UPDATED
PRODUCT_CREATED PRODUCT_CREATED
PRODUCT_UPDATED PRODUCT_UPDATED
CHECKOUT_QUANTITY_CHANGED PRODUCT_DELETED
CHECKOUT_CREATED CHECKOUT_CREATED
CHECKOUT_UPDATED CHECKOUT_UPDATED
FULFILLMENT_CREATED FULFILLMENT_CREATED
PAGE_CREATED
PAGE_UPDATED
PAGE_DELETED
} }
enum WebhookSampleEventTypeEnum { enum WebhookSampleEventTypeEnum {
@ -5852,12 +5839,16 @@ enum WebhookSampleEventTypeEnum {
INVOICE_DELETED INVOICE_DELETED
INVOICE_SENT INVOICE_SENT
CUSTOMER_CREATED CUSTOMER_CREATED
CUSTOMER_UPDATED
PRODUCT_CREATED PRODUCT_CREATED
PRODUCT_UPDATED PRODUCT_UPDATED
CHECKOUT_QUANTITY_CHANGED PRODUCT_DELETED
CHECKOUT_CREATED CHECKOUT_CREATED
CHECKOUT_UPDATED CHECKOUT_UPDATED
FULFILLMENT_CREATED FULFILLMENT_CREATED
PAGE_CREATED
PAGE_UPDATED
PAGE_DELETED
} }
type WebhookUpdate { type WebhookUpdate {

View file

@ -86,7 +86,7 @@ const LoginView: React.FC<LoginViewProps> = ({ params }) => {
externalAuthentications={ externalAuthentications={
externalAuthentications?.shop?.availableExternalAuthentications externalAuthentications?.shop?.availableExternalAuthentications
} }
loading={externalAuthenticationsLoading} loading={externalAuthenticationsLoading || tokenAuthLoading}
onExternalAuthentication={handleRequestExternalAuthentication} onExternalAuthentication={handleRequestExternalAuthentication}
onPasswordRecovery={() => navigate(passwordResetUrl)} onPasswordRecovery={() => navigate(passwordResetUrl)}
onSubmit={handleSubmit} onSubmit={handleSubmit}

View file

@ -39,7 +39,7 @@ export interface CategoryCreate_categoryCreate_category {
backgroundImage: CategoryCreate_categoryCreate_category_backgroundImage | null; backgroundImage: CategoryCreate_categoryCreate_category_backgroundImage | null;
name: string; name: string;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
parent: CategoryCreate_categoryCreate_category_parent | null; parent: CategoryCreate_categoryCreate_category_parent | null;

View file

@ -165,7 +165,7 @@ export interface CategoryDetails_category {
backgroundImage: CategoryDetails_category_backgroundImage | null; backgroundImage: CategoryDetails_category_backgroundImage | null;
name: string; name: string;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
parent: CategoryDetails_category_parent | null; parent: CategoryDetails_category_parent | null;

View file

@ -39,7 +39,7 @@ export interface CategoryUpdate_categoryUpdate_category {
backgroundImage: CategoryUpdate_categoryUpdate_category_backgroundImage | null; backgroundImage: CategoryUpdate_categoryUpdate_category_backgroundImage | null;
name: string; name: string;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
parent: CategoryUpdate_categoryUpdate_category_parent | null; parent: CategoryUpdate_categoryUpdate_category_parent | null;

View file

@ -1,6 +1,7 @@
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getParsedDataForJsonStringField } from "@saleor/translations/utils";
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
import { import {
useMetadataUpdate, useMetadataUpdate,
@ -48,7 +49,7 @@ export const CategoryCreateView: React.FC<CategoryCreateViewProps> = ({
const result = await createCategory({ const result = await createCategory({
variables: { variables: {
input: { input: {
description: JSON.stringify(formData.description), description: getParsedDataForJsonStringField(formData.description),
name: formData.name, name: formData.name,
seo: { seo: {
description: formData.seoDescription, description: formData.seoDescription,

View file

@ -13,6 +13,7 @@ import usePaginator, {
createPaginationState createPaginationState
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getParsedDataForJsonStringField } from "@saleor/translations/utils";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import { mapNodeToChoice } from "@saleor/utils/maps"; import { mapNodeToChoice } from "@saleor/utils/maps";
@ -188,7 +189,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
id, id,
input: { input: {
backgroundImageAlt: formData.backgroundImageAlt, backgroundImageAlt: formData.backgroundImageAlt,
description: JSON.stringify(formData.description), description: getParsedDataForJsonStringField(formData.description),
name: formData.name, name: formData.name,
seo: { seo: {
description: formData.seoDescription, description: formData.seoDescription,

View file

@ -10,8 +10,8 @@ import ChannelDeleteDialog, {
const props: ChannelDeleteDialogProps = { const props: ChannelDeleteDialogProps = {
channelsChoices: mapNodeToChoice(channelsList), channelsChoices: mapNodeToChoice(channelsList),
hasOrders: true,
confirmButtonState: "default", confirmButtonState: "default",
hasOrders: true,
onBack: () => undefined, onBack: () => undefined,
onClose: () => undefined, onClose: () => undefined,
onConfirm: () => undefined, onConfirm: () => undefined,

View file

@ -13,16 +13,20 @@ import { defineMessages, useIntl } from "react-intl";
import { useStyles } from "../styles"; import { useStyles } from "../styles";
const messages = defineMessages({ const messages = defineMessages({
needToBeMoved: { deleteChannel: {
defaultMessage: defaultMessage: "Delete Channel",
"All order information from this channel need to be moved to a different channel. Please select channel orders need to be moved to:.", description: "dialog header"
description: "delete channel"
}, },
deletingAllProductData: { deletingAllProductData: {
defaultMessage: defaultMessage:
"Deleting channel will delete all product data regarding this channel. Are you sure you want to delete this channel?", "Deleting channel will delete all product data regarding this channel. Are you sure you want to delete this channel?",
description: "delete channel" description: "delete channel"
}, },
needToBeMoved: {
defaultMessage:
"All order information from this channel need to be moved to a different channel. Please select channel orders need to be moved to:.",
description: "delete channel"
},
noAvailableChannel: { noAvailableChannel: {
defaultMessage: defaultMessage:
"There is no available channel to move order information to. Please create a channel with same currency so that information can be moved to it.", "There is no available channel to move order information to. Please create a channel with same currency so that information can be moved to it.",
@ -31,10 +35,6 @@ const messages = defineMessages({
selectChannel: { selectChannel: {
defaultMessage: "Select Channel", defaultMessage: "Select Channel",
description: "dialog header" description: "dialog header"
},
deleteChannel: {
defaultMessage: "Delete Channel",
description: "dialog header"
} }
}); });

View file

@ -72,8 +72,8 @@ export const channelsList: Channels_channels[] = [
{ {
__typename: "Channel", __typename: "Channel",
currencyCode: "euro", currencyCode: "euro",
id: "Q2hhbm5lbDo5w0z",
hasOrders: false, hasOrders: false,
id: "Q2hhbm5lbDo5w0z",
isActive: true, isActive: true,
name: "Channel USD", name: "Channel USD",
slug: "channel-usd1" slug: "channel-usd1"

View file

@ -102,7 +102,7 @@ export interface CollectionDetails_collection {
privateMetadata: (CollectionDetails_collection_privateMetadata | null)[]; privateMetadata: (CollectionDetails_collection_privateMetadata | null)[];
backgroundImage: CollectionDetails_collection_backgroundImage | null; backgroundImage: CollectionDetails_collection_backgroundImage | null;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
products: CollectionDetails_collection_products | null; products: CollectionDetails_collection_products | null;

View file

@ -48,7 +48,7 @@ export interface CollectionUpdate_collectionUpdate_collection {
privateMetadata: (CollectionUpdate_collectionUpdate_collection_privateMetadata | null)[]; privateMetadata: (CollectionUpdate_collectionUpdate_collection_privateMetadata | null)[];
backgroundImage: CollectionUpdate_collectionUpdate_collection_backgroundImage | null; backgroundImage: CollectionUpdate_collectionUpdate_collection_backgroundImage | null;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }

View file

@ -48,7 +48,7 @@ export interface CreateCollection_collectionCreate_collection {
privateMetadata: (CreateCollection_collectionCreate_collection_privateMetadata | null)[]; privateMetadata: (CreateCollection_collectionCreate_collection_privateMetadata | null)[];
backgroundImage: CreateCollection_collectionCreate_collection_backgroundImage | null; backgroundImage: CreateCollection_collectionCreate_collection_backgroundImage | null;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }

View file

@ -7,6 +7,7 @@ import useChannels from "@saleor/hooks/useChannels";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getParsedDataForJsonStringField } from "@saleor/translations/utils";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
import { import {
@ -102,7 +103,7 @@ export const CollectionCreate: React.FC<CollectionCreateProps> = ({
input: { input: {
backgroundImage: formData.backgroundImage.value, backgroundImage: formData.backgroundImage.value,
backgroundImageAlt: formData.backgroundImageAlt, backgroundImageAlt: formData.backgroundImageAlt,
description: JSON.stringify(formData.description), description: getParsedDataForJsonStringField(formData.description),
name: formData.name, name: formData.name,
seo: { seo: {
description: formData.seoDescription, description: formData.seoDescription,

View file

@ -21,6 +21,7 @@ import usePaginator, {
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import useProductSearch from "@saleor/searches/useProductSearch"; import useProductSearch from "@saleor/searches/useProductSearch";
import { getParsedDataForJsonStringField } from "@saleor/translations/utils";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import { import {
@ -198,7 +199,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
const handleUpdate = async (formData: CollectionUpdateData) => { const handleUpdate = async (formData: CollectionUpdateData) => {
const input: CollectionInput = { const input: CollectionInput = {
backgroundImageAlt: formData.backgroundImageAlt, backgroundImageAlt: formData.backgroundImageAlt,
description: JSON.stringify(formData.description), description: getParsedDataForJsonStringField(formData.description),
name: formData.name, name: formData.name,
seo: { seo: {
description: formData.seoDescription, description: formData.seoDescription,

View file

@ -33,7 +33,7 @@ export const AppChannelProvider: React.FC = ({ children }) => {
const [isPickerActive, setPickerActive] = React.useState(false); const [isPickerActive, setPickerActive] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
if (!selectedChannel && channelData?.channels) { if (!selectedChannel && channelData?.channels?.length > 0) {
setSelectedChannel(channelData.channels[0].id); setSelectedChannel(channelData.channels[0].id);
} }
}, [channelData]); }, [channelData]);

View file

@ -26,26 +26,26 @@ const messages = defineMessages({
defaultAddress: { defaultAddress: {
defaultMessage: "Default Address" defaultMessage: "Default Address"
}, },
defaultShippingAddress: {
defaultMessage: "Default Shipping Address"
},
defaultBillingAddress: { defaultBillingAddress: {
defaultMessage: "Default Billing Address" defaultMessage: "Default Billing Address"
}, },
setDefaultShipping: { defaultShippingAddress: {
defaultMessage: "Set as default shipping address", defaultMessage: "Default Shipping Address"
description: "button"
}, },
setDefaultBilling: { deleteAddress: {
defaultMessage: "Set as default billing address", defaultMessage: "Delete Address",
description: "button" description: "button"
}, },
editAddress: { editAddress: {
defaultMessage: "Edit Address", defaultMessage: "Edit Address",
description: "button" description: "button"
}, },
deleteAddress: { setDefaultBilling: {
defaultMessage: "Delete Address", defaultMessage: "Set as default billing address",
description: "button"
},
setDefaultShipping: {
defaultMessage: "Set as default shipping address",
description: "button" description: "button"
} }
}); });

View file

@ -23,6 +23,14 @@ export interface CustomerAddressListPageProps {
} }
const messages = defineMessages({ const messages = defineMessages({
addAddress: {
defaultMessage: "Add address",
description: "button"
},
doesntHaveAddresses: {
defaultMessage:
"This customer doesnt have any adresses added to his address book. You can add address using the button below."
},
fullNameAddress: { fullNameAddress: {
defaultMessage: "{fullName}'s Address Book", defaultMessage: "{fullName}'s Address Book",
description: "customer's address book, header" description: "customer's address book, header"
@ -31,16 +39,8 @@ const messages = defineMessages({
defaultMessage: "{fullName} Details", defaultMessage: "{fullName} Details",
description: "customer details, header" description: "customer details, header"
}, },
addAddress: {
defaultMessage: "Add address",
description: "button"
},
noAddressToShow: { noAddressToShow: {
defaultMessage: "There is no address to show for this customer" defaultMessage: "There is no address to show for this customer"
},
doesntHaveAddresses: {
defaultMessage:
"This customer doesnt have any adresses added to his address book. You can add address using the button below."
} }
}); });

View file

@ -64,8 +64,8 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
firstName: customer?.firstName || "", firstName: customer?.firstName || "",
isActive: customer?.isActive || false, isActive: customer?.isActive || false,
lastName: customer?.lastName || "", lastName: customer?.lastName || "",
note: customer?.note || "",
metadata: customer?.metadata.map(mapMetadataItemToInput), metadata: customer?.metadata.map(mapMetadataItemToInput),
note: customer?.note || "",
privateMetadata: customer?.privateMetadata.map(mapMetadataItemToInput) privateMetadata: customer?.privateMetadata.map(mapMetadataItemToInput)
}; };

View file

@ -946,8 +946,6 @@ export const customerList: ListCustomers_customers_edges_node[] = [
]; ];
export const customer: CustomerDetails_user & CustomerAddresses_user = { export const customer: CustomerDetails_user & CustomerAddresses_user = {
__typename: "User", __typename: "User",
metadata: [],
privateMetadata: [],
addresses: [ addresses: [
{ {
__typename: "Address", __typename: "Address",
@ -1046,6 +1044,7 @@ export const customer: CustomerDetails_user & CustomerAddresses_user = {
} }
] ]
}, },
metadata: [],
note: null, note: null,
orders: { orders: {
__typename: "OrderCountableConnection", __typename: "OrderCountableConnection",
@ -1069,5 +1068,6 @@ export const customer: CustomerDetails_user & CustomerAddresses_user = {
} }
} }
] ]
} },
privateMetadata: []
}; };

View file

@ -17,11 +17,12 @@ export const shippingZoneFragment = gql`
} }
`; `;
export const shippingMethodWithZipCodesFragment = gql` export const shippingMethodWithPostalCodesFragment = gql`
fragment ShippingMethodWithZipCodesFragment on ShippingMethod { fragment ShippingMethodWithPostalCodesFragment on ShippingMethod {
id id
zipCodeRules { postalCodeRules {
id id
inclusionType
start start
end end
} }
@ -30,9 +31,9 @@ export const shippingMethodWithZipCodesFragment = gql`
export const shippingMethodFragment = gql` export const shippingMethodFragment = gql`
${metadataFragment} ${metadataFragment}
${fragmentMoney} ${fragmentMoney}
${shippingMethodWithZipCodesFragment} ${shippingMethodWithPostalCodesFragment}
fragment ShippingMethodFragment on ShippingMethod { fragment ShippingMethodFragment on ShippingMethod {
...ShippingMethodWithZipCodesFragment ...ShippingMethodWithPostalCodesFragment
...MetadataFragment ...MetadataFragment
minimumOrderWeight { minimumOrderWeight {
unit unit

View file

@ -37,7 +37,7 @@ export interface CategoryDetailsFragment {
backgroundImage: CategoryDetailsFragment_backgroundImage | null; backgroundImage: CategoryDetailsFragment_backgroundImage | null;
name: string; name: string;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
parent: CategoryDetailsFragment_parent | null; parent: CategoryDetailsFragment_parent | null;

View file

@ -14,7 +14,7 @@ export interface CategoryTranslationFragment_translation_language {
export interface CategoryTranslationFragment_translation { export interface CategoryTranslationFragment_translation {
__typename: "CategoryTranslation"; __typename: "CategoryTranslation";
id: string; id: string;
description: any; description: any | null;
language: CategoryTranslationFragment_translation_language; language: CategoryTranslationFragment_translation_language;
name: string; name: string;
seoDescription: string | null; seoDescription: string | null;
@ -25,7 +25,7 @@ export interface CategoryTranslationFragment_category {
__typename: "Category"; __typename: "Category";
id: string; id: string;
name: string; name: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }

View file

@ -46,7 +46,7 @@ export interface CollectionDetailsFragment {
privateMetadata: (CollectionDetailsFragment_privateMetadata | null)[]; privateMetadata: (CollectionDetailsFragment_privateMetadata | null)[];
backgroundImage: CollectionDetailsFragment_backgroundImage | null; backgroundImage: CollectionDetailsFragment_backgroundImage | null;
slug: string; slug: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }

View file

@ -10,7 +10,7 @@ export interface CollectionTranslationFragment_collection {
__typename: "Collection"; __typename: "Collection";
id: string; id: string;
name: string; name: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }
@ -23,7 +23,7 @@ export interface CollectionTranslationFragment_translation_language {
export interface CollectionTranslationFragment_translation { export interface CollectionTranslationFragment_translation {
__typename: "CollectionTranslation"; __typename: "CollectionTranslation";
id: string; id: string;
description: any; description: any | null;
language: CollectionTranslationFragment_translation_language; language: CollectionTranslationFragment_translation_language;
name: string; name: string;
seoDescription: string | null; seoDescription: string | null;

View file

@ -109,7 +109,7 @@ export interface PageDetailsFragment {
pageType: PageDetailsFragment_pageType; pageType: PageDetailsFragment_pageType;
metadata: (PageDetailsFragment_metadata | null)[]; metadata: (PageDetailsFragment_metadata | null)[];
privateMetadata: (PageDetailsFragment_privateMetadata | null)[]; privateMetadata: (PageDetailsFragment_privateMetadata | null)[];
content: any; content: any | null;
seoTitle: string | null; seoTitle: string | null;
seoDescription: string | null; seoDescription: string | null;
publicationDate: any | null; publicationDate: any | null;

View file

@ -17,7 +17,7 @@ export interface PageTranslatableFragment_translation_language {
export interface PageTranslatableFragment_translation { export interface PageTranslatableFragment_translation {
__typename: "PageTranslation"; __typename: "PageTranslation";
id: string; id: string;
content: any; content: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
title: string; title: string;
@ -27,7 +27,7 @@ export interface PageTranslatableFragment_translation {
export interface PageTranslatableFragment { export interface PageTranslatableFragment {
__typename: "PageTranslatableContent"; __typename: "PageTranslatableContent";
id: string; id: string;
content: any; content: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
title: string; title: string;

View file

@ -11,7 +11,7 @@ import { LanguageCodeEnum } from "./../../types/globalTypes";
export interface PageTranslationFragment_page { export interface PageTranslationFragment_page {
__typename: "Page"; __typename: "Page";
id: string; id: string;
content: any; content: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
title: string; title: string;
@ -26,7 +26,7 @@ export interface PageTranslationFragment_translation_language {
export interface PageTranslationFragment_translation { export interface PageTranslationFragment_translation {
__typename: "PageTranslation"; __typename: "PageTranslation";
id: string; id: string;
content: any; content: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
title: string; title: string;

View file

@ -253,7 +253,7 @@ export interface Product {
privateMetadata: (Product_privateMetadata | null)[]; privateMetadata: (Product_privateMetadata | null)[];
name: string; name: string;
slug: string; slug: string;
description: any; description: any | null;
seoTitle: string | null; seoTitle: string | null;
seoDescription: string | null; seoDescription: string | null;
rating: number | null; rating: number | null;

View file

@ -12,7 +12,7 @@ export interface ProductTranslationFragment_product {
__typename: "Product"; __typename: "Product";
id: string; id: string;
name: string; name: string;
description: any; description: any | null;
seoDescription: string | null; seoDescription: string | null;
seoTitle: string | null; seoTitle: string | null;
} }
@ -26,7 +26,7 @@ export interface ProductTranslationFragment_translation_language {
export interface ProductTranslationFragment_translation { export interface ProductTranslationFragment_translation {
__typename: "ProductTranslation"; __typename: "ProductTranslation";
id: string; id: string;
description: any; description: any | null;
language: ProductTranslationFragment_translation_language; language: ProductTranslationFragment_translation_language;
name: string; name: string;
seoDescription: string | null; seoDescription: string | null;

View file

@ -2,15 +2,16 @@
/* 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 { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingMethodFragment // GraphQL fragment: ShippingMethodFragment
// ==================================================== // ====================================================
export interface ShippingMethodFragment_zipCodeRules { export interface ShippingMethodFragment_postalCodeRules {
__typename: "ShippingMethodZipCodeRule"; __typename: "ShippingMethodPostalCodeRule";
id: string; id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null; start: string | null;
end: string | null; end: string | null;
} }
@ -76,7 +77,7 @@ export interface ShippingMethodFragment_channelListings {
export interface ShippingMethodFragment { export interface ShippingMethodFragment {
__typename: "ShippingMethod"; __typename: "ShippingMethod";
id: string; id: string;
zipCodeRules: (ShippingMethodFragment_zipCodeRules | null)[] | null; postalCodeRules: (ShippingMethodFragment_postalCodeRules | null)[] | null;
metadata: (ShippingMethodFragment_metadata | null)[]; metadata: (ShippingMethodFragment_metadata | null)[];
privateMetadata: (ShippingMethodFragment_privateMetadata | null)[]; privateMetadata: (ShippingMethodFragment_privateMetadata | null)[];
minimumOrderWeight: ShippingMethodFragment_minimumOrderWeight | null; minimumOrderWeight: ShippingMethodFragment_minimumOrderWeight | null;

View file

@ -2,15 +2,16 @@
/* 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 { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingMethodWithExcludedProductsFragment // GraphQL fragment: ShippingMethodWithExcludedProductsFragment
// ==================================================== // ====================================================
export interface ShippingMethodWithExcludedProductsFragment_zipCodeRules { export interface ShippingMethodWithExcludedProductsFragment_postalCodeRules {
__typename: "ShippingMethodZipCodeRule"; __typename: "ShippingMethodPostalCodeRule";
id: string; id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null; start: string | null;
end: string | null; end: string | null;
} }
@ -107,7 +108,7 @@ export interface ShippingMethodWithExcludedProductsFragment_excludedProducts {
export interface ShippingMethodWithExcludedProductsFragment { export interface ShippingMethodWithExcludedProductsFragment {
__typename: "ShippingMethod"; __typename: "ShippingMethod";
id: string; id: string;
zipCodeRules: (ShippingMethodWithExcludedProductsFragment_zipCodeRules | null)[] | null; postalCodeRules: (ShippingMethodWithExcludedProductsFragment_postalCodeRules | null)[] | null;
metadata: (ShippingMethodWithExcludedProductsFragment_metadata | null)[]; metadata: (ShippingMethodWithExcludedProductsFragment_metadata | null)[];
privateMetadata: (ShippingMethodWithExcludedProductsFragment_privateMetadata | null)[]; privateMetadata: (ShippingMethodWithExcludedProductsFragment_privateMetadata | null)[];
minimumOrderWeight: ShippingMethodWithExcludedProductsFragment_minimumOrderWeight | null; minimumOrderWeight: ShippingMethodWithExcludedProductsFragment_minimumOrderWeight | null;

View file

@ -0,0 +1,23 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { PostalCodeRuleInclusionTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: ShippingMethodWithPostalCodesFragment
// ====================================================
export interface ShippingMethodWithPostalCodesFragment_postalCodeRules {
__typename: "ShippingMethodPostalCodeRule";
id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null;
end: string | null;
}
export interface ShippingMethodWithPostalCodesFragment {
__typename: "ShippingMethod";
id: string;
postalCodeRules: (ShippingMethodWithPostalCodesFragment_postalCodeRules | null)[] | null;
}

View file

@ -2,13 +2,16 @@
/* 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 { PostalCodeRuleInclusionTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingMethodWithZipCodesFragment // GraphQL fragment: ShippingMethodWithZipCodesFragment
// ==================================================== // ====================================================
export interface ShippingMethodWithZipCodesFragment_zipCodeRules { export interface ShippingMethodWithZipCodesFragment_postalCodeRules {
__typename: "ShippingMethodZipCodeRule"; __typename: "ShippingMethodPostalCodeRule";
id: string; id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null; start: string | null;
end: string | null; end: string | null;
} }
@ -16,5 +19,5 @@ export interface ShippingMethodWithZipCodesFragment_zipCodeRules {
export interface ShippingMethodWithZipCodesFragment { export interface ShippingMethodWithZipCodesFragment {
__typename: "ShippingMethod"; __typename: "ShippingMethod";
id: string; id: string;
zipCodeRules: (ShippingMethodWithZipCodesFragment_zipCodeRules | null)[] | null; postalCodeRules: (ShippingMethodWithZipCodesFragment_postalCodeRules | null)[] | 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 { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { PostalCodeRuleInclusionTypeEnum, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingZoneDetailsFragment // GraphQL fragment: ShippingZoneDetailsFragment
@ -26,9 +26,10 @@ export interface ShippingZoneDetailsFragment_countries {
country: string; country: string;
} }
export interface ShippingZoneDetailsFragment_shippingMethods_zipCodeRules { export interface ShippingZoneDetailsFragment_shippingMethods_postalCodeRules {
__typename: "ShippingMethodZipCodeRule"; __typename: "ShippingMethodPostalCodeRule";
id: string; id: string;
inclusionType: PostalCodeRuleInclusionTypeEnum | null;
start: string | null; start: string | null;
end: string | null; end: string | null;
} }
@ -94,7 +95,7 @@ export interface ShippingZoneDetailsFragment_shippingMethods_channelListings {
export interface ShippingZoneDetailsFragment_shippingMethods { export interface ShippingZoneDetailsFragment_shippingMethods {
__typename: "ShippingMethod"; __typename: "ShippingMethod";
id: string; id: string;
zipCodeRules: (ShippingZoneDetailsFragment_shippingMethods_zipCodeRules | null)[] | null; postalCodeRules: (ShippingZoneDetailsFragment_shippingMethods_postalCodeRules | null)[] | null;
metadata: (ShippingZoneDetailsFragment_shippingMethods_metadata | null)[]; metadata: (ShippingZoneDetailsFragment_shippingMethods_metadata | null)[];
privateMetadata: (ShippingZoneDetailsFragment_shippingMethods_privateMetadata | null)[]; privateMetadata: (ShippingZoneDetailsFragment_shippingMethods_privateMetadata | null)[];
minimumOrderWeight: ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight | null; minimumOrderWeight: ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight | null;

View file

@ -30,16 +30,17 @@ const useStyles = makeStyles(
interface HomeActivityCardProps { interface HomeActivityCardProps {
activities: Home_activities_edges_node[]; activities: Home_activities_edges_node[];
testId?: string;
} }
const HomeActivityCard: React.FC<HomeActivityCardProps> = props => { const HomeActivityCard: React.FC<HomeActivityCardProps> = props => {
const { activities } = props; const { activities, testId } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
return ( return (
<Card> <Card data-test-id={testId}>
<CardTitle <CardTitle
title={intl.formatMessage({ title={intl.formatMessage({
defaultMessage: "Activity", defaultMessage: "Activity",

View file

@ -53,19 +53,20 @@ const useStyles = makeStyles(
); );
interface HomeAnalyticsCardProps { interface HomeAnalyticsCardProps {
testId?: string;
icon: React.ReactElement<IconProps>; icon: React.ReactElement<IconProps>;
title: string; title: string;
children?: React.ReactNode; children?: React.ReactNode;
} }
const HomeAnalyticsCard: React.FC<HomeAnalyticsCardProps> = props => { const HomeAnalyticsCard: React.FC<HomeAnalyticsCardProps> = props => {
const { children, title, icon } = props; const { children, title, icon, testId } = props;
const classes = useStyles(props); const classes = useStyles(props);
return ( return (
<Card className={classes.cardSpacing}> <Card className={classes.cardSpacing}>
<CardContent className={classes.cardContent}> <CardContent className={classes.cardContent} data-test-id={testId}>
<div> <div>
<Typography className={classes.cardTitle} variant="subtitle1"> <Typography className={classes.cardTitle} variant="subtitle1">
{title} {title}

View file

@ -116,7 +116,7 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]} requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
> >
<TableRow hover={true} onClick={onOrdersToFulfillClick}> <TableRow hover={true} onClick={onOrdersToFulfillClick}>
<TableCell> <TableCell data-test-id="orders-to-fulfill">
{ordersToFulfill === undefined ? ( {ordersToFulfill === undefined ? (
<Skeleton /> <Skeleton />
) : ordersToFulfill === 0 ? ( ) : ordersToFulfill === 0 ? (
@ -136,7 +136,7 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow hover={true} onClick={onOrdersToCaptureClick}> <TableRow hover={true} onClick={onOrdersToCaptureClick}>
<TableCell> <TableCell data-test-id="orders-to-capture">
{ordersToCapture === undefined ? ( {ordersToCapture === undefined ? (
<Skeleton /> <Skeleton />
) : ordersToCapture === 0 ? ( ) : ordersToCapture === 0 ? (
@ -161,7 +161,7 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
requiredPermissions={[PermissionEnum.MANAGE_PRODUCTS]} requiredPermissions={[PermissionEnum.MANAGE_PRODUCTS]}
> >
<TableRow hover={true} onClick={onProductsOutOfStockClick}> <TableRow hover={true} onClick={onProductsOutOfStockClick}>
<TableCell> <TableCell data-test-id="products-out-of-stock">
{productsOutOfStock === undefined ? ( {productsOutOfStock === undefined ? (
<Skeleton /> <Skeleton />
) : productsOutOfStock === 0 ? ( ) : productsOutOfStock === 0 ? (

View file

@ -95,6 +95,7 @@ const HomePage: React.FC<HomePageProps> = props => {
<div className={classes.cardContainer}> <div className={classes.cardContainer}>
<HomeAnalyticsCard <HomeAnalyticsCard
title={"Sales"} title={"Sales"}
testId="sales-analytics"
icon={ icon={
<Sales <Sales
className={classes.icon} className={classes.icon}
@ -113,6 +114,7 @@ const HomePage: React.FC<HomePageProps> = props => {
</HomeAnalyticsCard> </HomeAnalyticsCard>
<HomeAnalyticsCard <HomeAnalyticsCard
title={"Orders"} title={"Orders"}
testId="orders-analytics"
icon={ icon={
<Orders <Orders
className={classes.icon} className={classes.icon}
@ -152,6 +154,7 @@ const HomePage: React.FC<HomePageProps> = props => {
]} ]}
> >
<HomeProductListCard <HomeProductListCard
testId="top-products"
onRowClick={onProductClick} onRowClick={onProductClick}
topProducts={topProducts} topProducts={topProducts}
/> />
@ -165,7 +168,10 @@ const HomePage: React.FC<HomePageProps> = props => {
userPermissions={userPermissions} userPermissions={userPermissions}
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]} requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
> >
<HomeActivityCard activities={activities} /> <HomeActivityCard
activities={activities}
testId="activity-card"
/>
</RequirePermissions> </RequirePermissions>
</div> </div>
)} )}

View file

@ -46,18 +46,19 @@ const useStyles = makeStyles(
); );
interface HomeProductListProps { interface HomeProductListProps {
testId?: string;
topProducts: Home_productTopToday_edges_node[]; topProducts: Home_productTopToday_edges_node[];
onRowClick: (productId: string, variantId: string) => void; onRowClick: (productId: string, variantId: string) => void;
} }
export const HomeProductList: React.FC<HomeProductListProps> = props => { export const HomeProductList: React.FC<HomeProductListProps> = props => {
const { topProducts, onRowClick } = props; const { topProducts, onRowClick, testId } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
return ( return (
<Card> <Card data-test-id={testId}>
<CardTitle <CardTitle
title={intl.formatMessage({ title={intl.formatMessage({
defaultMessage: "Top Products", defaultMessage: "Top Products",

View file

@ -167,8 +167,8 @@ const Routes: React.FC = () => {
dispatchAppState({ dispatchAppState({
payload: { payload: {
errorId, error: "unhandled",
error: "unhandled" errorId
}, },
type: "displayError" type: "displayError"
}); });

View file

@ -117,7 +117,7 @@ export interface PageCreate_pageCreate_page {
pageType: PageCreate_pageCreate_page_pageType; pageType: PageCreate_pageCreate_page_pageType;
metadata: (PageCreate_pageCreate_page_metadata | null)[]; metadata: (PageCreate_pageCreate_page_metadata | null)[];
privateMetadata: (PageCreate_pageCreate_page_privateMetadata | null)[]; privateMetadata: (PageCreate_pageCreate_page_privateMetadata | null)[];
content: any; content: any | null;
seoTitle: string | null; seoTitle: string | null;
seoDescription: string | null; seoDescription: string | null;
publicationDate: any | null; publicationDate: any | null;

View file

@ -109,7 +109,7 @@ export interface PageDetails_page {
pageType: PageDetails_page_pageType; pageType: PageDetails_page_pageType;
metadata: (PageDetails_page_metadata | null)[]; metadata: (PageDetails_page_metadata | null)[];
privateMetadata: (PageDetails_page_privateMetadata | null)[]; privateMetadata: (PageDetails_page_privateMetadata | null)[];
content: any; content: any | null;
seoTitle: string | null; seoTitle: string | null;
seoDescription: string | null; seoDescription: string | null;
publicationDate: any | null; publicationDate: any | null;

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