Skip to content

Commit

Permalink
feat: ability to add custom scripts on report page
Browse files Browse the repository at this point in the history
  • Loading branch information
sipayRT committed Feb 10, 2020
1 parent e80738e commit 9fe6cb8
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 6 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ directory.
]
}
```
* **customScripts** (optional) `function[]` - allows to add any scripts on the report html-page. Script will be executed immediately on page render. It can be helpful for adding some metrics or own extra functionality.

```js
customScripts: [
function() {console.log('something')},
() => {
const div = document.createElement('div');
div.innerHTML = 'hello';
document.body.prepend(div);
}
]
```

Also there is ability to override plugin parameters by CLI options or environment variables
(see [configparser](https://github.com/gemini-testing/configparser)).
Expand Down
17 changes: 17 additions & 0 deletions lib/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ const assertMetaInfoBaseUrls = (metaInfoBaseUrls) => {
}
};

const assertArrayOfFunctions = (name) => {
return (value) => {
if (!_.isArray(value)) {
throw new Error(`"${name}" option must be an array, but got ${typeof value}`);
}
for (const item of value) {
if (!_.isFunction(item)) {
throw new Error(`"${name}" option must be an array of functions but got ${typeof item} for one of items`);
}
}
};
};

const mapErrorPatterns = (errorPatterns) => {
return errorPatterns.map(patternInfo => {
return _.isString(patternInfo)
Expand Down Expand Up @@ -130,6 +143,10 @@ const getParser = () => {
customGui: option({
defaultValue: configDefaults.customGui,
validate: assertCustomGui
}),
customScripts: option({
defaultValue: configDefaults.customScripts,
validate: assertArrayOfFunctions('customScripts')
})
}), {envPrefix: ENV_PREFIX, cliPrefix: CLI_PREFIX});
};
Expand Down
3 changes: 2 additions & 1 deletion lib/constants/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
lazyLoadOffset: 800,
errorPatterns: [],
metaInfoBaseUrls: {},
customGui: {}
customGui: {},
customScripts: []
}
};
6 changes: 4 additions & 2 deletions lib/report-builder/report-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,17 @@ module.exports = class ReportBuilder {

getResult() {
const {
defaultView, baseHost, scaleImages, lazyLoadOffset, errorPatterns, metaInfoBaseUrls, customGui
defaultView, baseHost, scaleImages, lazyLoadOffset,
errorPatterns, metaInfoBaseUrls, customGui, customScripts
} = this._pluginConfig;

this._sortTree();
return _.extend({
skips: _.uniq(this._skips, JSON.stringify),
suites: this._tree.children,
config: {
defaultView, baseHost, scaleImages, lazyLoadOffset, errorPatterns, metaInfoBaseUrls, customGui
defaultView, baseHost, scaleImages, lazyLoadOffset,
errorPatterns, metaInfoBaseUrls, customGui, customScripts
},
apiValues: this._apiValues,
date: new Date().toString(),
Expand Down
25 changes: 25 additions & 0 deletions lib/static/components/custom-scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

import React, {useEffect, useRef} from 'react';
import {isEmpty} from 'lodash';

export default function CustomScripts(props) {
const {scripts} = props;

if (isEmpty(scripts)) {
return null;
}

const ref = useRef(null);

useEffect(() => {
scripts.forEach((script) => {
const s = document.createElement('script');
s.type = 'text/javascript';
s.innerHTML = `(${script})();`;
ref.current.appendChild(s);
});
}, [scripts]);

return <div className="custom-scripts" ref={ref}></div>;
}
8 changes: 6 additions & 2 deletions lib/static/components/gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import SkippedList from './skipped-list';
import Loading from './loading';
import ModalContainer from '../containers/modal';
import MainTree from './main-tree';
import CustomScripts from './custom-scripts';

class Gui extends Component {
componentDidMount() {
this.props.gui && this.props.initial();
}

render() {
const {loading} = this.props;
const {loading, customScripts} = this.props;

return (
<Fragment>
<CustomScripts scripts={customScripts}/>
<Notifications theme={wybo}/>
<ControlButtons />
<SkippedList />
Expand All @@ -33,4 +35,6 @@ class Gui extends Component {
}
}

export default connect(({reporter: {gui, loading}}) => ({gui, loading}), {initial})(Gui);
export default connect(({reporter: {gui, loading, config: {customScripts}}}) => {
return {gui, loading, customScripts};
}, {initial})(Gui);
7 changes: 6 additions & 1 deletion lib/static/components/report.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
'use strict';

import React, {Component, Fragment} from 'react';
import {connect} from 'react-redux';
import Header from './header';
import ControlButtons from './controls/report-controls';
import SkippedList from './skipped-list';
import MainTree from './main-tree';
import CustomScripts from './custom-scripts';

export default class Report extends Component {
class Report extends Component {
render() {
return (
<Fragment>
<CustomScripts scripts={this.props.customScripts}/>
<Header/>
<ControlButtons/>
<SkippedList/>
Expand All @@ -18,3 +21,5 @@ export default class Report extends Component {
);
}
}

export default connect(({reporter: {config}}) => ({customScripts: config.customScripts}))(Report);
28 changes: 28 additions & 0 deletions test/unit/lib/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,32 @@ describe('config', () => {
));
});
});

describe('customScripts', () => {
it('should have default value', () => {
assert.deepEqual(parseConfig({}).customScripts, configDefaults.customScripts);
});

it('should validate for Array type', () => {
assert.throws(
() => parseConfig({customScripts: 'foo'}),
/"customScripts" option must be an array, but got string/
);
});

it('should validate for Array items', () => {
assert.throws(
() => parseConfig({customScripts: ['foo']}),
/"customScripts" option must be an array of functions but got string for one of items/
);
});

it('should not throw with correct values', () => {
const scripts = [function() {}];

const config = parseConfig({customScripts: scripts});

assert.deepEqual(config.customScripts, scripts);
});
});
});
51 changes: 51 additions & 0 deletions test/unit/lib/static/components/custom-scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import CustomScripts from 'lib/static/components/custom-scripts';

describe('<CustomScripts />', () => {
it('should render component for scripts', () => {
const props = {
scripts: [function() {}]
};

const component = mount(<CustomScripts {...props} />);
const node = component.find('.custom-scripts');

assert.lengthOf(node, 1);
});

it('should not render component if no scripts to inject', () => {
const props = {
scripts: []
};

const component = mount(<CustomScripts {...props} />);
const node = component.find('.custom-scripts');

assert.lengthOf(node, 0);
});

it('should wrap function with IIFE', () => {
const props = {
scripts: [function foo() {}]
};

const component = mount(<CustomScripts {...props} />);
const script = component.text();

assert.equal(script, '(function foo() {})();');
});

it('should split each function with ";"', () => {
const props = {
scripts: [
function foo() {},
function bar() {}
]
};

const component = mount(<CustomScripts {...props} />);
const script = component.text();

assert.equal(script, '(function foo() {})();(function bar() {})();');
});
});

0 comments on commit 9fe6cb8

Please sign in to comment.