Skip to content

Commit

Permalink
fixed backend unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewChubatiuk committed Apr 19, 2024
1 parent d4f8b20 commit 2881606
Show file tree
Hide file tree
Showing 38 changed files with 570 additions and 1,082 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ 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 install

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
10 changes: 6 additions & 4 deletions Dockerfile.cypress
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
FROM cypress/browsers:node18.12.0-chrome106-ff106

ENV APP /usr/src/app
ENV APP /app
WORKDIR $APP

RUN \
npm install yarn@1.22.22 -g
COPY package.json yarn.lock .yarnrc $APP/
COPY viz-lib $APP/viz-lib
RUN npm install yarn@1.22.22 -g && yarn --frozen-lockfile --network-concurrency 1 > /dev/null
COPY viz-lib/package.json $APP/viz-lib/
RUN COMPOSE_BUILD=true yarn install

COPY . $APP
COPY client/cypress $APP/client/cypress

RUN ./node_modules/.bin/cypress verify
20 changes: 20 additions & 0 deletions Dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
client/.tmp/
client/cypress/
node_modules/
viz-lib/node_modules/
.tmp/
.venv/
venv/
.git/
/.codeclimate.yml
/.coverage
/coverage.xml
/.circleci/
/.github/
/netlify.toml
/setup/
.github/
compose.*
*.md
LICENSE*
Dockerfile*
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import PlainButton from "@/components/PlainButton";
import ExpandedWidgetDialog from "@/components/dashboards/ExpandedWidgetDialog";
import EditParameterMappingsDialog from "@/components/dashboards/EditParameterMappingsDialog";
import VisualizationRenderer from "@/components/visualizations/VisualizationRenderer";
import { ExecutionStatus } from "@/services/query-result";

import Widget from "./Widget";

Expand Down Expand Up @@ -278,7 +279,7 @@ class VisualizationWidget extends React.Component {
const widgetQueryResult = widget.getQueryResult();
const widgetStatus = widgetQueryResult && widgetQueryResult.getStatus();
switch (widgetStatus) {
case "failed":
case ExecutionStatus.FAILED:
return (
<div className="body-row-auto scrollbox">
{widgetQueryResult.getError() && (
Expand All @@ -288,7 +289,7 @@ class VisualizationWidget extends React.Component {
)}
</div>
);
case "done":
case ExecutionStatus.FINISHED:
return (
<div className="body-row-auto scrollbox">
<VisualizationRenderer
Expand Down
File renamed without changes.
4 changes: 3 additions & 1 deletion client/app/pages/queries/QuerySource.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,9 @@ function QuerySource(props) {
<QueryVisualizationTabs
queryResult={queryResult}
visualizations={query.visualizations}
showNewVisualizationButton={queryFlags.canEdit && queryResultData.status === ExecutionStatus.DONE}
showNewVisualizationButton={
queryFlags.canEdit && queryResultData.status === ExecutionStatus.FINISHED
}
canDeleteVisualizations={queryFlags.canEdit}
selectedTab={selectedVisualization}
onChangeTab={setSelectedVisualization}
Expand Down
2 changes: 1 addition & 1 deletion client/app/pages/queries/QueryView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function QueryView(props) {
<QueryVisualizationTabs
queryResult={queryResult}
visualizations={query.visualizations}
showNewVisualizationButton={queryFlags.canEdit && queryResultData.status === ExecutionStatus.DONE}
showNewVisualizationButton={queryFlags.canEdit && queryResultData.status === ExecutionStatus.FINISHED}
canDeleteVisualizations={queryFlags.canEdit}
selectedTab={selectedVisualization}
onChangeTab={setSelectedVisualization}
Expand Down
20 changes: 12 additions & 8 deletions client/app/pages/queries/components/QueryExecutionStatus.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,38 @@ import PropTypes from "prop-types";
import Alert from "antd/lib/alert";
import Button from "antd/lib/button";
import Timer from "@/components/Timer";
import { ExecutionStatus } from "@/services/query-result";

export default function QueryExecutionStatus({ status, updatedAt, error, isCancelling, onCancel }) {
const alertType = status === "failed" ? "error" : "info";
const showTimer = status !== "failed" && updatedAt;
const isCancelButtonAvailable = includes(["waiting", "processing"], status);
const alertType = status === ExecutionStatus.FAILED ? "error" : "info";
const showTimer = status !== ExecutionStatus.FAILED && updatedAt;
const isCancelButtonAvailable = includes([ExecutionStatus.QUEUED, ExecutionStatus.STARTED], status);
let message = isCancelling ? <React.Fragment>Cancelling&hellip;</React.Fragment> : null;

switch (status) {
case "waiting":
case ExecutionStatus.QUEUED:
if (!isCancelling) {
message = <React.Fragment>Query in queue&hellip;</React.Fragment>;
}
break;
case "processing":
case ExecutionStatus.STARTED:
if (!isCancelling) {
message = <React.Fragment>Executing query&hellip;</React.Fragment>;
}
break;
case "loading-result":
case ExecutionStatus.LOADING_RESULT:
message = <React.Fragment>Loading results&hellip;</React.Fragment>;
break;
case "failed":
case ExecutionStatus.FAILED:
message = (
<React.Fragment>
Error running query: <strong>{error}</strong>
</React.Fragment>
);
break;
case ExecutionStatus.CANCELED:
message = <React.Fragment>Query was canceled</React.Fragment>;
break;
// no default
}

Expand Down Expand Up @@ -66,7 +70,7 @@ QueryExecutionStatus.propTypes = {
};

QueryExecutionStatus.defaultProps = {
status: "waiting",
status: ExecutionStatus.QUEUED,
updatedAt: null,
error: null,
isCancelling: true,
Expand Down
61 changes: 33 additions & 28 deletions client/app/services/query-result.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,14 @@ const QueryResultResource = {
};

export const ExecutionStatus = {
WAITING: "waiting",
PROCESSING: "processing",
DONE: "done",
QUEUED: "queued",
STARTED: "started",
FINISHED: "finished",
FAILED: "failed",
LOADING_RESULT: "loading-result",
};

const statuses = {
1: ExecutionStatus.WAITING,
2: ExecutionStatus.PROCESSING,
3: ExecutionStatus.DONE,
4: ExecutionStatus.FAILED,
CANCELED: "canceled",
DEFERRED: "deferred",
SCHEDULED: "scheduled",
};

function handleErrorResponse(queryResult, error) {
Expand All @@ -80,7 +76,7 @@ function handleErrorResponse(queryResult, error) {
queryResult.update({
job: {
error: "cached query result unavailable, please execute again.",
status: 4,
status: ExecutionStatus.FAILED,
},
});
return;
Expand All @@ -91,7 +87,7 @@ function handleErrorResponse(queryResult, error) {
queryResult.update({
job: {
error: get(error, "response.data.message", "Unknown error occurred. Please try again later."),
status: 4,
status: ExecutionStatus.FAILED,
},
});
}
Expand All @@ -102,11 +98,17 @@ function sleep(ms) {

export function fetchDataFromJob(jobId, interval = 1000) {
return axios.get(`api/jobs/${jobId}`).then(data => {
const status = statuses[data.job.status];
if (status === ExecutionStatus.WAITING || status === ExecutionStatus.PROCESSING) {
const status = data.job.status;
if (
[ExecutionStatus.QUEUED, ExecutionStatus.STARTED, ExecutionStatus.SCHEDULED, ExecutionStatus.DEFERRED].includes(
status
)
) {
return sleep(interval).then(() => fetchDataFromJob(data.job.id));
} else if (status === ExecutionStatus.DONE) {
} else if (status === ExecutionStatus.FINISHED) {
return data.job.result;
} else if (status === ExecutionStatus.CANCELED) {
return Promise.reject("Job was canceled");
} else if (status === ExecutionStatus.FAILED) {
return Promise.reject(data.job.error);
}
Expand All @@ -122,7 +124,7 @@ class QueryResult {
this.deferred = defer();
this.job = {};
this.query_result = {};
this.status = "waiting";
this.status = ExecutionStatus.QUEUED;

this.updatedAt = moment();

Expand All @@ -138,8 +140,8 @@ class QueryResult {
extend(this, props);

if ("query_result" in props) {
this.status = ExecutionStatus.DONE;
this.deferred.onStatusChange(ExecutionStatus.DONE);
this.status = ExecutionStatus.FINISHED;
this.deferred.onStatusChange(ExecutionStatus.FINISHED);

const columnTypes = {};

Expand Down Expand Up @@ -183,11 +185,10 @@ class QueryResult {
});

this.deferred.resolve(this);
} else if (this.job.status === 3 || this.job.status === 2) {
this.deferred.onStatusChange(ExecutionStatus.PROCESSING);
this.status = "processing";
} else if (this.job.status === 4) {
this.status = statuses[this.job.status];
} else if (this.job.status === ExecutionStatus.STARTED || this.job.status === ExecutionStatus.FINISHED) {
this.status = ExecutionStatus.STARTED;
} else if (this.job.status === ExecutionStatus.FAILED) {
this.status = this.job.status;
this.deferred.reject(new QueryResultError(this.job.error));
} else {
this.deferred.onStatusChange(undefined);
Expand All @@ -211,7 +212,7 @@ class QueryResult {
if (this.isLoadingResult) {
return ExecutionStatus.LOADING_RESULT;
}
return this.status || statuses[this.job.status];
return this.status || this.job.status;
}

getError() {
Expand Down Expand Up @@ -389,7 +390,7 @@ class QueryResult {
this.update({
job: {
error: "failed communicating with server. Please check your Internet connection and try again.",
status: 4,
status: ExecutionStatus.FAILED,
},
});
this.isLoadingResult = false;
Expand All @@ -413,9 +414,13 @@ class QueryResult {
.then(jobResponse => {
this.update(jobResponse);

if (this.getStatus() === "processing" && this.job.query_result_id && this.job.query_result_id !== "None") {
if (
this.getStatus() === ExecutionStatus.STARTED &&
this.job.query_result_id &&
this.job.query_result_id !== "None"
) {
loadResult();
} else if (this.getStatus() !== "failed") {
} else if (this.getStatus() !== ExecutionStatus.FAILED) {
const waitTime = tryNumber > 10 ? 3000 : 500;
setTimeout(() => {
this.refreshStatus(query, parameters, tryNumber + 1);
Expand All @@ -428,7 +433,7 @@ class QueryResult {
this.update({
job: {
error: "failed communicating with server. Please check your Internet connection and try again.",
status: 4,
status: ExecutionStatus.FAILED,
},
});
});
Expand Down
5 changes: 3 additions & 2 deletions client/app/services/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import moment from "moment";
import debug from "debug";
import Mustache from "mustache";
import { axios } from "@/services/axios";
import { ExecutionStatus } from "@/services/query-result";
import {
zipObject,
isEmpty,
Expand Down Expand Up @@ -103,7 +104,7 @@ export class Query {
return new QueryResult({
job: {
error: `missing ${valuesWord} for ${missingParams.join(", ")} ${paramsWord}.`,
status: 4,
status: ExecutionStatus.FAILED,
},
});
}
Expand Down Expand Up @@ -360,7 +361,7 @@ export class QueryResultError {

// eslint-disable-next-line class-methods-use-this
getStatus() {
return "failed";
return ExecutionStatus.FAILED;
}

// eslint-disable-next-line class-methods-use-this
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

0 comments on commit 2881606

Please sign in to comment.