Skip to content

Commit 686e50a

Browse files
Hypnosphimb1te
authored andcommitted
feat: add an ability to stop tests directly from GUI
1 parent ba72d69 commit 686e50a

File tree

8 files changed

+89
-3
lines changed

8 files changed

+89
-3
lines changed

lib/gui/server.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ exports.start = async ({paths, hermione, guiApi, configs}) => {
110110
logger.log('server shutting down');
111111
});
112112

113+
server.post('/stop', (req, res) => {
114+
try {
115+
// pass 0 to prevent terminating hermione process
116+
hermione.halt('Tests were stopped by the user', 0);
117+
res.sendStatus(OK);
118+
} catch (e) {
119+
res.status(INTERNAL_SERVER_ERROR).send(`Error while stopping tests: ${e.message}`);
120+
}
121+
});
122+
113123
await app.initialize();
114124

115125
const {port, hostname} = options;

lib/static/components/controls/gui-controls.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class GuiControls extends Component {
1919
// from store
2020
running: PropTypes.bool.isRequired,
2121
processing: PropTypes.bool.isRequired,
22+
stopping: PropTypes.bool.isRequired,
2223
autoRun: PropTypes.bool.isRequired,
2324
allRootSuiteIds: PropTypes.arrayOf(PropTypes.string).isRequired,
2425
failedRootSuiteIds: PropTypes.arrayOf(PropTypes.string).isRequired,
@@ -35,8 +36,7 @@ class GuiControls extends Component {
3536
}
3637

3738
render() {
38-
const {actions, allRootSuiteIds, failedRootSuiteIds, running, autoRun, processing} = this.props;
39-
39+
const {actions, allRootSuiteIds, failedRootSuiteIds, running, autoRun, processing, stopping} = this.props;
4040
return (
4141
<div className="main-menu container">
4242
<CustomGuiControls />
@@ -52,6 +52,11 @@ class GuiControls extends Component {
5252
isDisabled={!failedRootSuiteIds.length || processing}
5353
handler={this._runFailedTests}
5454
/>
55+
<ControlButton
56+
label="Stop tests"
57+
isDisabled={!running || stopping}
58+
handler={actions.stopTests}
59+
/>
5560
<AcceptOpenedButton />
5661
<CommonControls/>
5762
</div>
@@ -66,6 +71,7 @@ export default connect(
6671
return {
6772
running: state.running,
6873
processing: state.processing,
74+
stopping: state.stopping,
6975
autoRun: state.autoRun,
7076
allRootSuiteIds: state.tree.suites.allRootIds,
7177
failedRootSuiteIds: state.tree.suites.failedRootIds,

lib/static/modules/action-names.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default {
77
FIN_STATIC_REPORT: 'FIN_STATIC_REPORT',
88
RUN_ALL_TESTS: 'RUN_ALL_TESTS',
99
RUN_FAILED_TESTS: 'RUN_FAILED_TESTS',
10+
STOP_TESTS: 'STOP_TESTS',
1011
RETRY_SUITE: 'RETRY_SUITE',
1112
RETRY_TEST: 'RETRY_TEST',
1213
SUITE_BEGIN: 'SUITE_BEGIN',

lib/static/modules/actions.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ export const acceptTest = (imageId) => {
139139
return acceptOpened([imageId], actionNames.ACCEPT_SCREENSHOT);
140140
};
141141

142+
export const stopTests = () => async dispatch => {
143+
try {
144+
await axios.post('/stop');
145+
dispatch({type: actionNames.STOP_TESTS});
146+
} catch (e) {
147+
console.error(`Error while stopping tests: {e}`);
148+
}
149+
};
150+
142151
export const suiteBegin = (suite) => ({type: actionNames.SUITE_BEGIN, payload: suite});
143152
export const testBegin = (test) => ({type: actionNames.TEST_BEGIN, payload: test});
144153
export const testResult = (result) => ({type: actionNames.TEST_RESULT, payload: result});

lib/static/modules/default-state.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export default Object.assign(defaults, {
88
gui: true,
99
running: false,
1010
processing: false,
11+
stopping: false,
1112
autoRun: false,
1213
skips: [],
1314
browsers: [],

lib/static/modules/reducers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import skips from './skips';
2222
import view from './view';
2323
import plugins from './plugins';
2424
import groupedErrors from './grouped-errors';
25+
import stopping from './stopping';
2526

2627
// The order of specifying reducers is important.
2728
// At the top specify reducers that does not depend on other state fields.
@@ -31,6 +32,7 @@ export default reduceReducers(
3132
notifications,
3233
running,
3334
processing,
35+
stopping,
3436
loading,
3537
gui,
3638
date,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import actionNames from '../action-names';
2+
3+
export default (state = {}, action) => {
4+
switch (action.type) {
5+
case actionNames.STOP_TESTS: {
6+
return {...state, stopping: true};
7+
}
8+
9+
case actionNames.TESTS_END: {
10+
return {...state, stopping: false};
11+
}
12+
13+
default:
14+
return state;
15+
}
16+
};

test/unit/lib/static/components/controls/gui-controls.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ describe('<GuiControls />', () => {
1212
AcceptOpenedButton = sandbox.stub().returns(null);
1313
actionsStub = {
1414
runAllTests: sandbox.stub().returns({type: 'some-type'}),
15-
runFailedTests: sandbox.stub().returns({type: 'some-type'})
15+
runFailedTests: sandbox.stub().returns({type: 'some-type'}),
16+
stopTests: sandbox.stub().returns({type: 'some-type'})
1617
};
1718
selectors = {
1819
getFailedTests: sandbox.stub().returns([])
@@ -116,4 +117,44 @@ describe('<GuiControls />', () => {
116117
assert.calledOnce(AcceptOpenedButton);
117118
});
118119
});
120+
121+
describe('"Stop tests" button', () => {
122+
it('should be disabled when tests are not running', () => {
123+
const component = mkConnectedComponent(<GuiControls />, {
124+
initialState: {running: false, stopping: false}
125+
});
126+
127+
const stop = component.find('[label="Stop tests"]');
128+
assert.isTrue(stop.prop('isDisabled'));
129+
});
130+
131+
describe ('should be disabled when tests are', () => {
132+
it('running', () => {
133+
const component = mkConnectedComponent(<GuiControls />, {
134+
initialState: {running: true, stopping: false}
135+
});
136+
137+
const stop = component.find('[label="Stop tests"]');
138+
assert.isFalse(stop.prop('isDisabled'));
139+
});
140+
141+
it('stopping', () => {
142+
const component = mkConnectedComponent(<GuiControls />, {
143+
initialState: {running: true, stopping: true}
144+
});
145+
146+
const stop = component.find('[label="Stop tests"]');
147+
assert.isTrue(stop.prop('isDisabled'));
148+
});
149+
});
150+
151+
it('should call "stopTests" action on click', () => {
152+
const component = mkConnectedComponent(<GuiControls />, {
153+
initialState: {running: true}
154+
});
155+
156+
component.find('[label="Stop tests"]').simulate('click');
157+
assert.calledOnce(actionsStub.stopTests);
158+
});
159+
});
119160
});

0 commit comments

Comments
 (0)