Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new 'Run all' button to interactive toolbar on the web interface #981

Merged
merged 14 commits into from
Aug 24, 2023
1 change: 1 addition & 0 deletions doc/newsfragments/2404_new.run_all_button.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a new "Run all Multitests" button to interactive toolbar on the web interface.
rnemes marked this conversation as resolved.
Show resolved Hide resolved
25 changes: 25 additions & 0 deletions testplan/web_ui/testing/src/Report/InteractiveReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ReloadButton,
ResetButton,
AbortButton,
RunAllButton,
SaveButton,
} from "../Toolbar/InteractiveButtons";
import NavBreadcrumbs from "../Nav/NavBreadcrumbs";
Expand Down Expand Up @@ -63,6 +64,7 @@ class InteractiveReportComponent extends BaseReport {
this.resetAssertionStatus = this.resetAssertionStatus.bind(this);
this.resetReport = this.resetReport.bind(this);
this.abortTestplan = this.abortTestplan.bind(this);
this.runAll = this.runAll.bind(this);
this.reloadCode = this.reloadCode.bind(this);
this.envCtrlCallback = this.envCtrlCallback.bind(this);
this.handleClick = this.handleClick.bind(this);
Expand All @@ -72,6 +74,7 @@ class InteractiveReportComponent extends BaseReport {
navWidth: `${INTERACTIVE_COL_WIDTH}em`,
resetting: false,
reloading: false,
running: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I looked at this new status and wonder two things:

  • Can we do without it?
  • If not, then we need to block certain actions when it is True. Right now I think we can trigger it twice without problem, send a reload during it, reset, etc... See for instance, how the reloadCode in InteractiveReport is handling it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do without it?

We only need that for logging the state of the progress. However, we could use another variable eg.: runtime_status which could replace resetting, reloading, aborting as well, but in that case need to refactor the related functionalities.

If not, then we need to block certain actions when it is True. Right now I think we can trigger it twice without problem, send a reload during it, reset, etc... See for instance, how the reloadCode in InteractiveReport is handling it.

That makes sense. I will update the code.

aborting: false,
assertionStatus: defaultAssertionStatus,
};
Expand Down Expand Up @@ -123,6 +126,7 @@ class InteractiveReportComponent extends BaseReport {
response.data.runtime_status === "not_run"
) {
this.setState({ resetting: false });
this.setState({ running: false });
}
if (
!this.state.report ||
Expand Down Expand Up @@ -497,6 +501,22 @@ class InteractiveReportComponent extends BaseReport {
return shallowEntry;
}

/**
* Send request of start all tests to server.
*/
runAll() {
if (this.state.running) {
return;
} else {
const updatedReportEntry = {
...this.shallowReportEntry(this.state.report),
runtime_status: "running",
};
this.putUpdatedReportEntry(updatedReportEntry);
this.setState({ running: true });
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the fixes for the active filter issues are in, we probably need to see if it is working with a filter expression. If not, we will need to extend support, I do not think that piece of the API is handling the filtering.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't handle filtering, it starts everything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned offline, this will be needed.


/**
* Reset the report state to "resetting" and request the change to server.
*/
Expand Down Expand Up @@ -664,6 +684,11 @@ class InteractiveReportComponent extends BaseReport {
updateEmptyDisplayFunc={noop}
updateTagsDisplayFunc={noop}
extraButtons={[
<RunAllButton
key="runall-button"
running={this.state.running}
runAllCbk={this.runAll}
/>,
<ReloadButton
key="reload-button"
reloading={this.state.reloading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,4 +683,61 @@ describe("InteractiveReport", () => {
});
});
});

it("Run all tests", (done) => {
const interactiveReport = renderInteractiveReport();

const report = initialReport();
const multitest = report.entries[0];
expect(multitest.category).toBe("multitest");
multitest.env_status = "STARTED";

const testcase = report.entries[0].entries[0].entries[0];
expect(testcase.category).toBe("testcase");

// Add an assertion entry.
testcase.entries = [
{
machine_time: "2020-01-28T17:27:46.134440+00:00",
second: "foo",
description: null,
passed: false,
meta_type: "assertion",
type: "Equal",
category: "DEFAULT",
utc_time: "2020-01-28T17:27:46.134429+00:00",
line_no: 24,
label: "==",
first: "foo",
},
];

interactiveReport.setState({
report: report,
});
interactiveReport.update();
interactiveReport.instance().runAll();
moxios.wait(() => {
const request = moxios.requests.mostRecent();
expect(request.url).toBe("/api/v1/interactive/report");
expect(request.config.method).toBe("put");
const putData = JSON.parse(request.config.data);

request
.respondWith({
status: 200,
response: putData,
})
.then(() => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
expect(request.url).toBe("/api/v1/interactive/report");
expect(request.config.method).toBe("put");
const putData = JSON.parse(request.config.data);
expect(putData.runtime_status).toBe("running");
done();
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ exports[`InteractiveReport Handles environment being started 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -92,6 +96,10 @@ exports[`InteractiveReport Parially refreshes the report on update. 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -174,6 +182,10 @@ exports[`InteractiveReport Updates testcase state 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -653,6 +665,10 @@ exports[`InteractiveReport handles individual parametrizations being run 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -735,6 +751,10 @@ exports[`InteractiveReport handles individual test suites being run 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -817,6 +837,10 @@ exports[`InteractiveReport handles individual testcases being run 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down Expand Up @@ -899,6 +923,10 @@ exports[`InteractiveReport handles tests being run 1`] = `
expandStatus="default"
extraButtons={
Array [
<RunAllButton
runAllCbk={[Function]}
running={false}
/>,
<ReloadButton
reloadCbk={[Function]}
reloading={false}
Expand Down
38 changes: 38 additions & 0 deletions testplan/web_ui/testing/src/Toolbar/InteractiveButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
faFastBackward,
faTimes,
faSave,
faPlay,
} from "@fortawesome/free-solid-svg-icons";
import { format as dateFormat } from "date-fns";
import { css } from "aphrodite";
Expand Down Expand Up @@ -141,6 +142,43 @@ export const AbortButton = (props) => {
}
};

/**
* Render a button to Run all Multitests.
*
* If the RunAll action is currently in progress, display an inactive icon
* instead.
*/
export const RunAllButton = (props) => {
if (props.running) {
return (
<NavItem key="runall-button">
<div className={css(styles.buttonsBar)}>
<FontAwesomeIcon
key="toolbar-runall"
className={css(styles.toolbarButton, styles.toolbarInactive)}
icon={faPlay}
title="Running..."
/>
</div>
</NavItem>
);
} else {
return (
<NavItem key="runall-button">
<div className={css(styles.buttonsBar)}>
<FontAwesomeIcon
key="toolbar-runall"
className={css(styles.toolbarButton)}
icon={faPlay}
title="Run all Multitests"
onClick={props.runAllCbk}
/>
</div>
</NavItem>
);
}
};

const getHistoryTable = (historyExporters) => {
if (Array.isArray(historyExporters) && historyExporters.length > 0) {
const resultList = historyExporters.map((item, i) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from "react";
import { shallow } from "enzyme";
import { StyleSheetTestUtils } from "aphrodite";

import { ReloadButton, ResetButton, AbortButton } from "../InteractiveButtons";
import { ReloadButton, ResetButton, AbortButton, RunAllButton } from "../InteractiveButtons";

describe("ReloadButton", () => {
beforeEach(() => {
Expand Down Expand Up @@ -94,3 +94,33 @@ describe("AbortButton", () => {
expect(button).toMatchSnapshot();
});
});

describe("RunAllButton", () => {
beforeEach(() => {
// Stop Aphrodite from injecting styles, this crashes the tests.
StyleSheetTestUtils.suppressStyleInjection();
});

afterEach(() => {
// Resume style injection once test is finished.
StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
});

it("Renders a clickable button", () => {
const runAllCbk = jest.fn();
const button = shallow(
<RunAllButton runAllCbk={runAllCbk} running={false} />
);
expect(button).toMatchSnapshot();
button.find({ title: "Run all Multitests" }).simulate("click");
expect(runAllCbk.mock.calls.length).toBe(1);
});

it("Renders an inactive icon when running is in-progress", () => {
const runAllCbk = jest.fn();
const button = shallow(
<RunAllButton runAllCbk={runAllCbk} running={true} />
);
expect(button).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TOOLBAR_BUTTONS_BATCH,
TOOLBAR_BUTTONS_INTERACTIVE,
} from "../../Common/defaults";
import { ReloadButton, ResetButton, AbortButton } from "../InteractiveButtons";
import { ReloadButton, ResetButton, AbortButton, RunAllButton } from "../InteractiveButtons";

function defaultProps() {
return {
Expand Down Expand Up @@ -74,6 +74,7 @@ describe("Toolbar", () => {
it("inserts extra buttons into the toolbar", () => {
const resetCbk = jest.fn();
props.extraButtons = [
RunAllButton({ running: false, runAllCbk: jest.fn() }),
ReloadButton({ reloading: false, reloadCbk: jest.fn() }),
ResetButton({ resetting: false, resetStateCbk: jest.fn() }),
AbortButton({ aborting: false, abortCbk: jest.fn() }),
Expand Down
Loading
Loading