merge changes

This commit is contained in:
Karolina Rakoczy 2021-02-11 15:17:00 +01:00
commit 221756a70e
23 changed files with 1265 additions and 212 deletions

View file

@ -90,11 +90,12 @@ jobs:
env:
API_URI: ${{ steps.api_uri.outputs.custom_api_uri || secrets.API_URI }}
APP_MOUNT_URI: ${{ secrets.APP_MOUNT_URI }}
CYPRESS_baseUrl: ${{ secrets.CYPRESS_BASEURL }}
CYPRESS_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }}
CYPRESS_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }}
with:
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-timeout: 120
- uses: actions/upload-artifact@v1

View file

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

View file

@ -34,12 +34,9 @@ class Channels {
`;
cy.sendRequestWithQuery(getChannelsInfoQuery).then(resp => {
const channels = new Set(resp.body.data.channels);
if (channels) {
channels.forEach(element => {
if (element.name.startsWith(nameStartsWith)) {
const targetChannels = Array.from(channels).filter(function(
channel
) {
const targetChannels = Array.from(channels).filter(function(channel) {
return (
element.currencyCode === channel.currencyCode &&
element.id !== channel.id
@ -51,14 +48,13 @@ class Channels {
}
}
});
}
});
}
deleteChannel(channelId, targetChannelId) {
deleteChannel(channelId, targetChennelId) {
const deleteChannelMutation = `mutation{
channelDelete(id: "${channelId}", input:{
targetChannel: "${targetChannelId}"
targetChannel: "${targetChennelId}"
}){
channel{
name

View file

@ -1,4 +1,7 @@
/* eslint-disable sort-keys */
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 */
export const PRODUCTS_SELECTORS = {
productsList: "[data-test-id][data-test='id']",
products: "[data-test='submenu-item-label'][data-test-id='products']",
createProductBtn: "[data-test='add-product']",
productNameInput: "[name='name']",
@ -11,5 +12,10 @@ export const PRODUCTS_SELECTORS = {
visibleRadioBtn: "[name='isPublished']",
saveBtn: "[data-test='button-bar-confirm']",
confirmationMsg: "[data-test='notification-success']",
channelAvailabilityItem: "[data-test='channel-availability-item']"
channelAvailabilityItem: "[data-test='channel-availability-item']",
availableManageButton:
"[data-test-id='channels-availiability-manage-button']",
channelsAvailabilityForm:
"[data-test-id='manage-products-channels-availiability-list']",
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,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,139 @@
// <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";
describe("Channels", () => {
const channelStartsWith = "Cypress:";
const currency = "PLN";
const channels = new Channels();
const channelsSteps = new ChannelsSteps();
before(() => {
cy.clearSessionData().loginUserViaRequest();
channels.deleteTestChannels(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.visit(urlList.channels).waitForGraph("Channels");
channelsSteps.createChannelByView(randomChannel, currency);
// New channel should be visible in channels list
cy.waitForGraph("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.visit(urlList.products).waitForGraph("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.visit(urlList.channels).waitForGraph("Channels");
cy.get(CHANNELS_SELECTORS.channelName)
.contains(randomChannelToDelete)
.parentsUntil(CHANNELS_SELECTORS.channelsTable)
.find("button")
.click()
.get(BUTTON_SELECTORS.submit)
.click()
.waitForGraph("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

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

View file

@ -1,18 +1,25 @@
// <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";
// <reference types="cypress" />
describe("Products", () => {
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
});
it("should add new visible product", () => {
cy.visit("/")
it("should navigate to channels page", () => {
cy.visit(urlList.homePage)
.get(LEFT_MENU_SELECTORS.catalog)
.click()
.get(PRODUCTS_SELECTORS.products)
.get(LEFT_MENU_SELECTORS.products)
.click()
.location("pathname")
.should("contain", "/products");
});
it("should add new visible product", () => {
cy.visit(urlList.products)
.get(PRODUCTS_SELECTORS.createProductBtn)
.click()
.get(PRODUCTS_SELECTORS.productNameInput)

View file

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

View file

@ -16,7 +16,13 @@
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// make env variables visible for cypress
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;
};

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

@ -1,35 +1,55 @@
import "./user";
import "./softAsserations";
import { urlList } from "../url/urlList";
Cypress.Commands.add("clearSessionData", () => {
// Because of known cypress bug, not all local storage data are cleared.
// Here is workaround to ensure tests have no side effects.
// Suggested usage:
// beforeEach(() => {
// cy.clearSessionData();
// });
cy.clearCookies();
cy.clearLocalStorage();
cy.visit("/", {
cy.visit(urlList.homePage, {
onBeforeLoad: win => {
win.sessionStorage.clear();
}
});
});
Cypress.Commands.add("waitForGraph", 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;
}
}
});
cy.wait(`@${operationName}`);
});
Cypress.Commands.add("sendRequestWithQuery", query =>
cy.request({
method: "POST",
body: {
method: "POST",
url: Cypress.env("API_URI"),
query
query,
url: urlList.apiUri
},
headers: {
Authorization: `JWT ${window.sessionStorage.getItem("auth")}`
},
url: Cypress.env("API_URI")
method: "POST",
url: urlList.apiUri
})
);

View file

@ -1,5 +1,5 @@
/* eslint-disable sort-keys */
import { LOGIN_SELECTORS } from "../../elements/account/login-selectors";
import { urlList } from "../../url/urlList";
Cypress.Commands.add("loginUser", () =>
cy
@ -31,16 +31,16 @@ Cypress.Commands.add("loginUserViaRequest", () => {
return cy
.request({
method: "POST",
url: Cypress.env("API_URI"),
body: {
operationName: "TokenAuth",
query: logInMutationQuery,
variables: {
email: Cypress.env("USER_NAME"),
password: Cypress.env("USER_PASSWORD")
},
query: logInMutationQuery
}
},
method: "POST",
url: urlList.apiUri
})
.then(resp => {
window.sessionStorage.setItem("auth", resp.body.data.tokenCreate.token);

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

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

4
lws.config.js Normal file
View file

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

1087
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "saleor-dashboard",
"version": "3.0.0-a.0",
"version": "3.0.0-a.5",
"main": "src/index.tsx",
"repository": {
"type": "git",
@ -43,6 +43,7 @@
"downshift": "^1.31.16",
"editorjs-inline-tool": "^0.4.0",
"editorjs-undo": "^0.1.4",
"faker": "^5.1.0",
"fast-array-diff": "^0.2.0",
"fsevents": "^1.2.9",
"fuzzaldrin": "^2.1.0",
@ -132,7 +133,7 @@
"codecov": "^3.7.1",
"core-js": "^3.7.0",
"cross-env": "^6.0.3",
"cypress": "^4.9.0",
"cypress": "^6.4.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"enzyme-to-json": "^3.6.1",