Skip to content

Commit 002b43d

Browse files
authored
fix(gui): reconnect to database on tests end event (#411)
* fix(gui): reconnect to database on tests end event * fix(details component): call handler with current state * feat: add ability to share html-reporter action names with its plugins
1 parent 99e2c8b commit 002b43d

File tree

11 files changed

+148
-50
lines changed

11 files changed

+148
-50
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ directory.
256256
The routes then can be called from the plugin React components defined in the `plugin.js`. For convenience the plugin name is always passed with options when function- or array-returning form is used to export plugin as the function options property `pluginName`:
257257

258258
```js
259-
export default ['react', 'axios', function(React, axios, {pluginName, pluginConfig, actions, selectors}) {
259+
export default ['react', 'axios', function(React, axios, {pluginName, pluginConfig, actions, actionNames, selectors}) {
260260
class PluginComponent extends React.Component {
261261
// ... somewhere inside the component ...
262262
const result = await axios.get(`/plugin-routes/${pluginName}/plugin-route`);
@@ -271,9 +271,11 @@ directory.
271271

272272
In the example you can also see another convenient properties:
273273
- `pluginName` - plugin name;
274+
- `pluginConfig` - plugin configuration;
274275
- `actions` - the html-reporter **Redux** actions;
275-
- `selectors` - the memoized html-reporter selectors which created using **reselect** library
276-
- `pluginConfig` - plugin configuration.
276+
- `actionNames` - the html-reporter action names, that used in **Redux** actions. To be able to subscribe on html-reporter events;
277+
- `selectors` - the memoized html-reporter selectors which created using **reselect** library.
278+
277279

278280
Available dependencies:
279281
- `react`

lib/static/components/details.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ export default class Details extends Component {
1616
state = {isOpened: false};
1717

1818
handleClick = () => {
19-
this.setState({isOpened: !this.state.isOpened});
19+
this.setState((state, props) => {
20+
const newState = {isOpened: !state.isOpened};
2021

21-
if (this.props.onClick) {
22-
this.props.onClick();
23-
}
22+
if (props.onClick) {
23+
props.onClick(newState);
24+
}
25+
26+
return newState;
27+
});
2428
}
2529

2630
render() {

lib/static/modules/action-names.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default {
1313
SUITE_BEGIN: 'SUITE_BEGIN',
1414
TEST_BEGIN: 'TEST_BEGIN',
1515
TEST_RESULT: 'TEST_RESULT',
16-
TESTS_END: 'TEST_END',
16+
TESTS_END: 'TESTS_END',
1717
ACCEPT_SCREENSHOT: 'ACCEPT_SCREENSHOT',
1818
ACCEPT_OPENED_SCREENSHOTS: 'ACCEPT_OPENED_SCREENSHOTS',
1919
CLOSE_SECTIONS: 'CLOSE_SECTIONS',

lib/static/modules/actions.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import StaticTestsTreeBuilder from '../../tests-tree-builder/static';
55
import actionNames from './action-names';
66
import {types as modalTypes} from '../components/modals';
77
import {QUEUED} from '../../constants/test-statuses';
8-
import {LOCAL_DATABASE_NAME} from '../../constants/file-names';
98
import {getHttpErrorMessage} from './utils';
10-
import {fetchDatabases, mergeDatabases, connectToDatabase} from './sqlite';
9+
import {fetchDatabases, mergeDatabases, connectToDatabase, getMainDatabaseUrl} from './sqlite';
1110
import {getSuitesTableRows} from './database-utils';
1211
import {setFilteredBrowsers} from './query-params';
1312
import plugins from './plugins';
@@ -23,7 +22,7 @@ export const initGuiReport = () => {
2322
try {
2423
const appState = await axios.get('/init');
2524

26-
const mainDatabaseUrl = new URL(LOCAL_DATABASE_NAME, window.location.href);
25+
const mainDatabaseUrl = getMainDatabaseUrl();
2726
const db = await connectToDatabase(mainDatabaseUrl.href);
2827

2928
await plugins.loadAll(appState.data.config);
@@ -148,6 +147,20 @@ export const stopTests = () => async dispatch => {
148147
}
149148
};
150149

150+
export const testsEnd = () => async dispatch => {
151+
try {
152+
const mainDatabaseUrl = getMainDatabaseUrl();
153+
const db = await connectToDatabase(mainDatabaseUrl.href);
154+
155+
dispatch({
156+
type: actionNames.TESTS_END,
157+
payload: {db}
158+
});
159+
} catch (e) {
160+
dispatch(createNotificationError('testsEnd', e));
161+
}
162+
};
163+
151164
export const suiteBegin = (suite) => ({type: actionNames.SUITE_BEGIN, payload: suite});
152165
export const testBegin = (test) => ({type: actionNames.TEST_BEGIN, payload: test});
153166
export const testResult = (result) => ({type: actionNames.TEST_RESULT, payload: result});
@@ -157,7 +170,6 @@ export const toggleLoading = (payload) => ({type: actionNames.TOGGLE_LOADING, pa
157170
export const closeSections = (payload) => ({type: actionNames.CLOSE_SECTIONS, payload});
158171
export const openModal = (payload) => ({type: actionNames.OPEN_MODAL, payload});
159172
export const closeModal = (payload) => ({type: actionNames.CLOSE_MODAL, payload});
160-
export const testsEnd = () => ({type: actionNames.TESTS_END});
161173
export const runFailed = () => ({type: actionNames.RUN_FAILED_TESTS});
162174
export const expandAll = () => ({type: actionNames.VIEW_EXPAND_ALL});
163175
export const expandErrors = () => ({type: actionNames.VIEW_EXPAND_ERRORS});

lib/static/modules/load-plugin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import immer from 'immer';
1111
import * as reselect from 'reselect';
1212
import axios from 'axios';
1313
import * as selectors from './selectors';
14+
import actionNames from './action-names';
1415
import Details from '../components/details';
1516

1617
const whitelistedDeps = {
@@ -85,7 +86,7 @@ async function initPlugin(plugin, pluginName, pluginConfig) {
8586
const depArgs = deps.map(dep => whitelistedDeps[dep]);
8687
// cyclic dep, resolve it dynamically
8788
const actions = await import('./actions');
88-
return plugin(...depArgs, {pluginName, pluginConfig, actions, selectors});
89+
return plugin(...depArgs, {pluginName, pluginConfig, actions, actionNames, selectors});
8990
}
9091

9192
return plugin;

lib/static/modules/reducers/db.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ export default (state, action) => {
66
case actionNames.INIT_STATIC_REPORT:
77
case actionNames.INIT_GUI_REPORT: {
88
const {db} = action.payload;
9+
10+
return {...state, db};
11+
}
12+
13+
case actionNames.TESTS_END: {
14+
closeDatabase(state.db); // close previous connection in order to free memory
15+
const {db} = action.payload;
16+
917
return {...state, db};
1018
}
1119

lib/static/modules/sqlite.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import axios from 'axios';
44
import {flattenDeep} from 'lodash';
55
import {mergeTables} from '../../common-utils';
6+
import {LOCAL_DATABASE_NAME} from '../../constants/file-names';
67

78
function isRelativeUrl(url) {
89
try {
@@ -139,8 +140,13 @@ async function connectToDatabase(dbUrl) {
139140
return new SQL.Database(new Uint8Array(data));
140141
}
141142

143+
function getMainDatabaseUrl() {
144+
return new URL(LOCAL_DATABASE_NAME, window.location.href);
145+
}
146+
142147
module.exports = {
143148
fetchDatabases,
144149
mergeDatabases,
145-
connectToDatabase
150+
connectToDatabase,
151+
getMainDatabaseUrl
146152
};

test/unit/lib/static/components/details.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,34 @@ describe('<Details />', () => {
3030
assert.equal(text, 'some-title');
3131
});
3232

33-
it('should call "onClick" handler on click in title', () => {
34-
const props = {
35-
title: 'some-title',
36-
content: 'foo bar',
37-
onClick: sinon.stub()
38-
};
33+
describe('"onClick" handler', () => {
34+
let props;
3935

40-
const component = mount(<Details {...props} />);
41-
component.find('.details__summary').simulate('click');
36+
beforeEach(() => {
37+
props = {
38+
title: 'some-title',
39+
content: 'foo bar',
40+
onClick: sinon.stub()
41+
};
42+
});
43+
44+
it('should call on click in title', () => {
45+
const component = mount(<Details {...props} />);
46+
47+
component.find('.details__summary').simulate('click');
48+
49+
assert.calledOnce(props.onClick);
50+
});
51+
52+
it('should call with changed state on each call', () => {
53+
const component = mount(<Details {...props} />);
54+
55+
component.find('.details__summary')
56+
.simulate('click')
57+
.simulate('click');
4258

43-
assert.calledOnceWith(props.onClick);
59+
assert.calledWith(props.onClick.firstCall, {isOpened: true});
60+
assert.calledWith(props.onClick.secondCall, {isOpened: false});
61+
});
4462
});
4563
});

test/unit/lib/static/modules/actions.js

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import {LOCAL_DATABASE_NAME} from 'lib/constants/file-names';
77

88
describe('lib/static/modules/actions', () => {
99
const sandbox = sinon.sandbox.create();
10-
let dispatch, actions, addNotification, getSuitesTableRows, connectToDatabaseStub, pluginsStub;
10+
let dispatch, actions, addNotification, getSuitesTableRows, getMainDatabaseUrl, connectToDatabaseStub, pluginsStub;
1111

1212
beforeEach(() => {
1313
dispatch = sandbox.stub();
1414
sandbox.stub(axios, 'post').resolves({data: {}});
1515
addNotification = sandbox.stub();
1616
getSuitesTableRows = sandbox.stub();
17+
getMainDatabaseUrl = sandbox.stub().returns({href: 'http://localhost/default/sqlite.db'});
1718
connectToDatabaseStub = sandbox.stub().resolves({});
1819
pluginsStub = {loadAll: sandbox.stub()};
1920

@@ -23,7 +24,7 @@ describe('lib/static/modules/actions', () => {
2324
actions = proxyquire('lib/static/modules/actions', {
2425
'reapop': {addNotification},
2526
'./database-utils': {getSuitesTableRows},
26-
'./sqlite': {connectToDatabase: connectToDatabaseStub},
27+
'./sqlite': {getMainDatabaseUrl, connectToDatabase: connectToDatabaseStub},
2728
'./plugins': pluginsStub
2829
});
2930
});
@@ -33,16 +34,6 @@ describe('lib/static/modules/actions', () => {
3334
describe('initGuiReport', () => {
3435
beforeEach(() => {
3536
sandbox.stub(axios, 'get').resolves({data: {}});
36-
37-
global.window = {
38-
location: {
39-
href: 'http://localhost/random/path.html'
40-
}
41-
};
42-
});
43-
44-
afterEach(() => {
45-
global.window = undefined;
4637
});
4738

4839
it('should run init action on server', async () => {
@@ -52,7 +43,8 @@ describe('lib/static/modules/actions', () => {
5243
});
5344

5445
it('should fetch database from default html page', async () => {
55-
global.window.location.href = 'http://127.0.0.1:8080/';
46+
const href = 'http://127.0.0.1:8080/sqlite.db';
47+
getMainDatabaseUrl.returns({href});
5648

5749
await actions.initGuiReport()(dispatch);
5850

@@ -330,4 +322,43 @@ describe('lib/static/modules/actions', () => {
330322
assert.deepEqual(actions.closeModal(modal), {type: actionNames.CLOSE_MODAL, payload: modal});
331323
});
332324
});
325+
326+
describe('testsEnd', () => {
327+
it('should connect to database', async () => {
328+
const href = 'http://127.0.0.1:8080/sqlite.db';
329+
getMainDatabaseUrl.returns({href});
330+
331+
await actions.testsEnd()(dispatch);
332+
333+
assert.calledOnceWith(connectToDatabaseStub, href);
334+
});
335+
336+
it('should dispatch "TESTS_END" action with db connection', async () => {
337+
const db = {};
338+
connectToDatabaseStub.resolves(db);
339+
340+
await actions.testsEnd()(dispatch);
341+
342+
assert.calledOnceWith(dispatch, {
343+
type: actionNames.TESTS_END,
344+
payload: {db}
345+
});
346+
});
347+
348+
it('should show notification if error appears', async () => {
349+
connectToDatabaseStub.rejects(new Error('failed to connect to database'));
350+
351+
await actions.testsEnd()(dispatch);
352+
353+
assert.calledOnceWith(
354+
addNotification,
355+
{
356+
dismissAfter: 0,
357+
id: 'testsEnd',
358+
message: 'failed to connect to database',
359+
status: 'error'
360+
}
361+
);
362+
});
363+
});
333364
});

test/unit/lib/static/modules/load-plugin.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import axios from 'axios';
44
import loadPlugin from 'lib/static/modules/load-plugin';
5+
import actionNames from 'lib/static/modules/action-names';
56
import * as actions from 'lib/static/modules/actions';
67
import * as selectors from 'lib/static/modules/selectors';
78

@@ -32,7 +33,7 @@ describe('static/modules/load-plugin', () => {
3233
await loadPlugin('plugin-a');
3334

3435
assert.deepStrictEqual(plugin.args, [
35-
[{actions, selectors, pluginName: 'plugin-a', pluginConfig: undefined}]
36+
[{actions, actionNames, selectors, pluginName: 'plugin-a', pluginConfig: undefined}]
3637
]);
3738
});
3839

@@ -41,7 +42,7 @@ describe('static/modules/load-plugin', () => {
4142
await loadPlugin('plugin-b', config);
4243

4344
assert.deepStrictEqual(plugin.args, [
44-
[{actions, selectors, pluginName: 'plugin-b', pluginConfig: config}]
45+
[{actions, actionNames, selectors, pluginName: 'plugin-b', pluginConfig: config}]
4546
]);
4647
});
4748

@@ -50,7 +51,7 @@ describe('static/modules/load-plugin', () => {
5051
await loadPlugin('plugin-c');
5152

5253
assert.deepStrictEqual(plugin.args, [
53-
[axios, {actions, selectors, pluginName: 'plugin-c', pluginConfig: undefined}]
54+
[axios, {actions, actionNames, selectors, pluginName: 'plugin-c', pluginConfig: undefined}]
5455
]);
5556
});
5657

@@ -59,7 +60,7 @@ describe('static/modules/load-plugin', () => {
5960
await loadPlugin('plugin-d');
6061

6162
assert.deepStrictEqual(plugin.args, [
62-
[undefined, {actions, selectors, pluginName: 'plugin-d', pluginConfig: undefined}]
63+
[undefined, {actions, actionNames, selectors, pluginName: 'plugin-d', pluginConfig: undefined}]
6364
]);
6465
});
6566
});

0 commit comments

Comments
 (0)