Skip to content

Commit

Permalink
cypress: v11.x -> v13.x
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewChubatiuk committed Apr 22, 2024
1 parent 6c68b48 commit 892699f
Show file tree
Hide file tree
Showing 28 changed files with 548 additions and 1,087 deletions.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
client/.tmp/
client/cypress/
node_modules/
viz-lib/node_modules/
.tmp/
Expand All @@ -12,3 +13,8 @@ venv/
/.github/
/netlify.toml
/setup/
.github/
compose.*
*.md
LICENSE*
Dockerfile*
8 changes: 3 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- master
env:
CYPRESS_COVERAGE: "true"
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/node_modules/.cache/Cypress
NODE_VERSION: 18
YARN_VERSION: 1.22.22
REDASH_COOKIE_SECRET: 2H9gNG9obnAQ9qnR9BDTQUph6CbXKCzF
Expand Down Expand Up @@ -148,10 +149,7 @@ jobs:
runs-on: ubuntu-22.04
needs: frontend-lint
env:
CYPRESS_INSTALL_BINARY: 0
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1
INSTALL_GROUPS: main
COMPOSE_PROFILES: e2e
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Expand All @@ -177,8 +175,8 @@ jobs:
set -x
yarn build
yarn cypress build
yarn cypress start -- --skip-db-seed
docker compose run cypress yarn cypress db-seed
yarn cypress start --skip-db-seed
yarn cypress db-seed
- name: Execute Cypress Tests
run: yarn cypress run-ci
- name: "Failure: output container logs to console"
Expand Down
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
RUN useradd -m -d /frontend redash
USER redash
WORKDIR /frontend
COPY --chown=redash package.json yarn.lock .yarnrc /frontend/
COPY --chown=redash package.json yarn.lock /frontend/
COPY --chown=redash viz-lib /frontend/viz-lib
COPY --chown=redash scripts /frontend/scripts

RUN yarn --frozen-lockfile --network-concurrency 1;
RUN yarn --frozen-lockfile --network-concurrency 1
COPY --chown=redash client /frontend/client
COPY --chown=redash webpack.config.js /frontend/
RUN yarn build
Expand Down
12 changes: 0 additions & 12 deletions Dockerfile.cypress

This file was deleted.

1 change: 1 addition & 0 deletions client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
},
],
"jsx-a11y/no-redundant-roles": "error",
"cypress/unsafe-to-chain-command": "off",
"jsx-a11y/no-autofocus": "off",
"jsx-a11y/click-events-have-key-events": "off", // TMP
"jsx-a11y/no-static-element-interactions": "off", // TMP
Expand Down
5 changes: 4 additions & 1 deletion client/app/services/axios.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import qs from "query-string";
import { restoreSession } from "@/services/restoreSession";

export const axios = axiosLib.create({
paramsSerializer: params => qs.stringify(params),
paramsSerializer: {
encode: value => value,
serialize: params => qs.stringify(params),
},
xsrfCookieName: "csrf_token",
xsrfHeaderName: "X-CSRF-TOKEN",
});
Expand Down
69 changes: 43 additions & 26 deletions client/cypress/cypress.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/* eslint-disable import/no-extraneous-dependencies, no-console */
const { find } = require("lodash");
const { execSync } = require("child_process");
const { get, post } = require("request").defaults({ jar: true });
const { seedData } = require("./seed-data");
const fs = require("fs");
var Cookie = require("request-cookies").Cookie;
const axios = require("axios");

let cypressConfigBaseUrl;
try {
Expand All @@ -14,31 +12,49 @@ try {

const baseUrl = process.env.CYPRESS_baseUrl || cypressConfigBaseUrl || "http://localhost:5001";

function seedDatabase(seedValues) {
get(baseUrl + "/login", (_, { headers }) => {
const request = seedValues.shift();
const data = request.type === "form" ? { formData: request.data } : { json: request.data };
const instance = axios.create({
baseURL: baseUrl,
xsrfCookieName: "csrf_token",
xsrfHeaderName: "X-CSRF-TOKEN",
maxRedirects: 0,
validateStatus: status => {
return status >= 200 && status < 303;
},
});

if (headers["set-cookie"]) {
const cookies = headers["set-cookie"].map(cookie => new Cookie(cookie));
const csrfCookie = find(cookies, { key: "csrf_token" });
if (csrfCookie) {
if (request.type === "form") {
data["formData"] = { ...data["formData"], csrf_token: csrfCookie.value };
} else {
data["headers"] = { "X-CSRFToken": csrfCookie.value };
}
}
}
let config = {
headers: {},
};

let csrfToken;

post(baseUrl + request.route, data, (err, response) => {
const result = response ? response.statusCode : err;
console.log("POST " + request.route + " - " + result);
function seedDatabase(seedValues) {
const request = seedValues.shift();
const headers = Object.assign({}, config.headers, request.config.headers);
const reqConfig = Object.assign({}, config, request.config, { headers });
if (csrfToken) {
const csrfValue = csrfToken.substring(csrfToken.indexOf("=") + 1, csrfToken.indexOf(";"));
if (request.type === "form") {
reqConfig["data"]["csrf_token"] = csrfValue;
} else {
reqConfig["headers"]["X-CSRFToken"] = csrfValue;
}
}
instance
.request(reqConfig)
.then(res => {
if (res.headers["set-cookie"]) {
csrfToken = res.headers["set-cookie"].find(cookie => cookie.startsWith("csrf_token="));
config["headers"]["Cookie"] = res.headers["set-cookie"].join(";");
}
console.log(`${reqConfig.method} ${reqConfig.url} - ${res.status}`);
if (seedValues.length) {
seedDatabase(seedValues);
}
})
.catch(err => {
console.log(`${reqConfig.method} ${reqConfig.url} - ${err}`);
});
});
}

function buildServer() {
Expand Down Expand Up @@ -66,11 +82,12 @@ function runCypressCI() {
if (GITHUB_REPOSITORY === "getredash/redash") {
process.env.CYPRESS_OPTIONS = "--record";
}
// Remove four lines below after it's merged
delete process.env.CYPRESS_INSTALL_BINARY;
execSync("rm -r node_modules", { stdio: "inherit" });
execSync("yarn install", { stdio: "inherit" });

execSync(
"docker compose run --name cypress cypress ./node_modules/.bin/percy exec -t 300 -- ./node_modules/.bin/cypress run $CYPRESS_OPTIONS",
{ stdio: "inherit" }
);
execSync("yarn percy cypress run $CYPRESS_OPTIONS", { stdio: "inherit" });
}

const command = process.argv[2] || "all";
Expand Down
11 changes: 4 additions & 7 deletions client/cypress/integration/alert/view_alert_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ describe("View Alert", () => {
.contains("Test Email Destination")
.should("not.exist");

cy.server();
cy.route("GET", "**/api/destinations").as("Destinations");
cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
cy.intercept("GET", "**/api/destinations").as("Destinations");
cy.intercept("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");

cy.visit(this.alertUrl);

Expand All @@ -49,8 +48,7 @@ describe("View Alert", () => {
});

it("hides remove button from non-author", function() {
cy.server();
cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
cy.intercept("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");

cy.logout()
.then(() => cy.login()) // as admin
Expand Down Expand Up @@ -81,8 +79,7 @@ describe("View Alert", () => {
});

it("shows remove button for non-author admin", function() {
cy.server();
cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
cy.intercept("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");

cy.logout()
.then(() => cy.login("user@redash.io", "password"))
Expand Down
6 changes: 2 additions & 4 deletions client/cypress/integration/dashboard/dashboard_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ describe("Dashboard", () => {
cy.getByTestId("CreateButton").click();
cy.getByTestId("CreateDashboardMenuItem").click();

cy.server();
cy.route("POST", "**/api/dashboards").as("NewDashboard");
cy.intercept("POST", "**/api/dashboards").as("NewDashboard");

cy.getByTestId("CreateDashboardDialog").within(() => {
cy.getByTestId("DashboardSaveButton").should("be.disabled");
Expand Down Expand Up @@ -57,8 +56,7 @@ describe("Dashboard", () => {
});

it("is accessible through multiple urls", () => {
cy.server();
cy.route("GET", "**/api/dashboards/*").as("LoadDashboard");
cy.intercept("GET", "**/api/dashboards/*").as("LoadDashboard");
cy.createDashboard("Dashboard multiple urls").then(({ id, slug }) => {
[`/dashboards/${id}`, `/dashboards/${id}-anything-here`, `/dashboard/${slug}`].forEach(url => {
cy.visit(url);
Expand Down
3 changes: 1 addition & 2 deletions client/cypress/integration/dashboard/dashboard_tags_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ describe("Dashboard Tags", () => {
});

it("is possible to add and edit tags", () => {
cy.server();
cy.route("POST", "**/api/dashboards/*").as("DashboardSave");
cy.intercept("POST", "**/api/dashboards/*").as("DashboardSave");

cy.getByTestId("TagsControl").contains(".label", "Unpublished");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ describe("Grid compliant widgets", () => {
});

it("auto saves after drag", () => {
cy.server();
cy.route("POST", "**/api/widgets/*").as("WidgetSave");
cy.intercept("POST", "**/api/widgets/*").as("WidgetSave");

editDashboard();
cy.get("@textboxEl").dragBy(330);
Expand Down Expand Up @@ -116,8 +115,7 @@ describe("Grid compliant widgets", () => {
});

it("auto saves after resize", () => {
cy.server();
cy.route("POST", "**/api/widgets/*").as("WidgetSave");
cy.intercept("POST", "**/api/widgets/*").as("WidgetSave");

editDashboard();
resizeBy(cy.get("@textboxEl"), 200);
Expand Down
1 change: 1 addition & 0 deletions client/cypress/integration/dashboard/parameter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe("Dashboard Parameters", () => {
.then(() => {
if (closeMappingMenu) {
cy.contains("button", "OK").click();
return cy.get("@Popover").should("not.exist");
}
return cy.get("@Popover").should("not.be.visible");
});
Expand Down
2 changes: 1 addition & 1 deletion client/cypress/integration/dashboard/textbox_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ describe("Textbox", () => {
})
.should($el => {
const { top, left } = $el.offset();
expect(top).to.be.oneOf([162, 162.015625]);
expect(top).to.be.oneOf([162.0078125]);
expect(left).to.eq(282);
expect($el.width()).to.eq(545);
expect($el.height()).to.eq(185);
Expand Down
6 changes: 2 additions & 4 deletions client/cypress/integration/dashboard/widget_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ describe("Widget", () => {

it("grows when dynamically adding table rows", () => {
// listen to results
cy.server();
cy.route("GET", "**/api/query_results/*").as("FreshResults");
cy.intercept("GET", "**/api/query_results/*").as("FreshResults");

// start with 1 table row
cy.get("@paramInput")
Expand All @@ -131,8 +130,7 @@ describe("Widget", () => {

it("revokes auto height after manual height adjustment", () => {
// listen to results
cy.server();
cy.route("GET", "**/api/query_results/*").as("FreshResults");
cy.intercept("GET", "**/api/query_results/*").as("FreshResults");

editDashboard();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ describe("Create Data Source", () => {

it("opens the creation dialog when clicking in the create link or button", () => {
cy.visit("/data_sources");
cy.server();
cy.route("**/api/data_sources", []); // force an empty response
cy.intercept("**/api/data_sources", []); // force an empty response

["CreateDataSourceButton", "CreateDataSourceLink"].forEach(createElementTestId => {
cy.getByTestId(createElementTestId).click();
Expand All @@ -18,8 +17,7 @@ describe("Create Data Source", () => {

it("renders the page and takes a screenshot", function() {
cy.visit("/data_sources/new");
cy.server();
cy.route("**/api/data_sources/types").as("DataSourceTypesRequest");
cy.intercept("**/api/data_sources/types").as("DataSourceTypesRequest");

cy.wait("@DataSourceTypesRequest")
.then(({ response }) => response.body.filter(type => type.deprecated))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ describe("Create Destination", () => {

it("renders the page and takes a screenshot", function() {
cy.visit("/destinations/new");
cy.server();
cy.route("**/api/destinations/types").as("DestinationTypesRequest");
cy.intercept("**/api/destinations/types").as("DestinationTypesRequest");

cy.wait("@DestinationTypesRequest")
.then(({ response }) => response.body.filter(type => type.deprecated))
Expand Down
9 changes: 3 additions & 6 deletions client/cypress/integration/query/parameter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,7 @@ describe("Parameter", () => {

cy.location("search").should("not.contain", "Redash");

cy.server();
cy.route("POST", "**/api/queries/*/results").as("Results");
cy.intecept("POST", "**/api/queries/*/results").as("Results");

apply(cy.get("@Input"));

Expand All @@ -512,8 +511,7 @@ describe("Parameter", () => {
},
};

cy.server();
cy.route("GET", "**/api/data_sources/*/schema").as("Schema");
cy.intercept("GET", "**/api/data_sources/*/schema").as("Schema");

cy.createQuery(queryData, false)
.then(({ id }) => cy.visit(`/queries/${id}/source`))
Expand Down Expand Up @@ -603,8 +601,7 @@ describe("Parameter", () => {
});

it("is possible to rearrange parameters", function() {
cy.server();
cy.route("POST", "**/api/queries/*").as("QuerySave");
cy.intercept("POST", "**/api/queries/*").as("QuerySave");

dragParam("param1", this.paramWidth, 1);
cy.wait("@QuerySave");
Expand Down
3 changes: 1 addition & 2 deletions client/cypress/integration/query/query_tags_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ describe("Query Tags", () => {
});

it("is possible to add and edit tags", () => {
cy.server();
cy.route("POST", "**/api/queries/*").as("QuerySave");
cy.intercept("POST", "**/api/queries/*").as("QuerySave");

cy.getByTestId("TagsControl").contains(".label", "Unpublished");

Expand Down
3 changes: 1 addition & 2 deletions client/cypress/integration/visualizations/pivot_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ describe("Pivot", () => {

const visualizationName = "Pivot";

cy.server();
cy.route("POST", "**/api/visualizations").as("SaveVisualization");
cy.intercept("POST", "**/api/visualizations").as("SaveVisualization");

createPivotThroughUI(visualizationName, { hideControls: true });

Expand Down

0 comments on commit 892699f

Please sign in to comment.