""")) { stringBuilder, line ->
+ .fold(StringBuilder("""""")
diff --git a/html-report/layout/log-container.html b/html-report/layout/log-container.html
index e34bcc2..52400c0 100644
--- a/html-report/layout/log-container.html
+++ b/html-report/layout/log-container.html
@@ -1,4 +1,4 @@
-
""")) { stringBuilder, line ->
stringBuilder.appendln(line)
}
.appendln("""
+
${log_entries}
diff --git a/html-report/src/components/LogContainer.js b/html-report/src/components/LogContainer.js
new file mode 100644
index 0000000..5b1317a
--- /dev/null
+++ b/html-report/src/components/LogContainer.js
@@ -0,0 +1,61 @@
+import React, { Component } from 'react';
+import cx from 'classnames';
+import LogFilter from './LogFilter'
+
+export default class LogContainer extends Component {
+
+ state = {
+ logs: [],
+ results: [],
+ loading: true,
+ hide: false
+ }
+
+ componentDidMount() {
+ document.addEventListener('DOMContentLoaded', (event) => {
+ this.loadStaticLogs()
+ })
+ }
+
+ loadStaticLogs() {
+ let logDivs = document.querySelectorAll('#static_logs > div > div')
+ if (!logDivs.length) {
+ this.setState({hide: true, loading: false})
+ return
+ }
+ let logs = []
+ for (var div of logDivs) {
+ logs.push(Object.assign({level: div.className, line: div.innerText, key: logs.length}))
+ }
+ this.setState({logs: logs, results: logs, loading: false})
+ window.document.getElementById("static_logs").remove()
+ }
+
+ getSearchResults(results) {
+ this.setState({ results: results });
+ }
+
+ render() {
+ return this.state.hide
+ ? ()
+ : (
+
+ this.getSearchResults(results) }
+ data={this.state.logs}
+ />
+ { !this.state.loading &&
+
+ );
+ }
+}
diff --git a/html-report/src/components/LogFilter.js b/html-report/src/components/LogFilter.js
new file mode 100644
index 0000000..97f33fa
--- /dev/null
+++ b/html-report/src/components/LogFilter.js
@@ -0,0 +1,37 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import cx from 'classnames';
+import SearchBar from './SearchBar';
+import LogFilterButton from './LogFilterButton';
+
+const SEARCH_FIELDS = ['level', 'line'];
+const SEARCH_REF = 'line';
+export default class LogFilter extends Component {
+ static propTypes = {
+ setSearchResults: PropTypes.func,
+ data: PropTypes.array
+ };
+
+ render() {
+ return (
+
+ { this.state.results.map((entry, i) => {
+ return (
+
}
+
+ { entry.line }
+
+ )
+ })
+ }
+
+ (this.performFilterSearch = callback) }
+ />
+
+ );
+ }
+}
diff --git a/html-report/src/components/LogFilterButton.js b/html-report/src/components/LogFilterButton.js
new file mode 100644
index 0000000..d368b47
--- /dev/null
+++ b/html-report/src/components/LogFilterButton.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+const LogFilterButton = ({text, disabled, onClick}) => (
+
+);
+
+export default LogFilterButton;
diff --git a/html-report/src/components/SearchBar.js b/html-report/src/components/SearchBar.js
index 2bc7375..6816ecf 100644
--- a/html-report/src/components/SearchBar.js
+++ b/html-report/src/components/SearchBar.js
@@ -4,16 +4,18 @@ import cx from 'classnames';
import elasticlunr from 'elasticlunr';
import convertTime from './../utils/convertTime'
-const SEARCH_FIELDS = ['package_name', 'class_name', 'name', 'id', 'status'];
-const SEARCH_REF = 'id';
const EL_SEARCH = elasticlunr();
export default class SearchBar extends Component {
static propTypes = {
- setSearchResults: PropTypes.func
+ setSearchResults: PropTypes.func,
+ setPerformFilterSearchCallback: PropTypes.func,
+ searchFields: PropTypes.array,
+ searchRef: PropTypes.string,
+ data: PropTypes.array,
};
state = {
- data: window.suite.tests,
+ data: this.props.data,
error: false,
searchLabel: null,
searchParams: null,
@@ -21,13 +23,28 @@ export default class SearchBar extends Component {
};
componentWillMount() {
- let { data } = this.state;
elasticlunr.clearStopWords();
- SEARCH_FIELDS.forEach(f => EL_SEARCH.addField(f))
- EL_SEARCH.setRef(SEARCH_REF);
- if (data.length) {
- data.forEach(item => EL_SEARCH.addDoc(item))
- }
+ this.props.searchFields.forEach(f => EL_SEARCH.addField(f))
+ EL_SEARCH.setRef(this.props.searchRef);
+ }
+
+ componentDidMount() {
+ this.props.setPerformFilterSearchCallback(this.performFilterSearch)
+ }
+
+ componentWillReceiveProps(props) {
+ let data = props.data;
+ if (data === this.state.data) {
+ return;
+ }
+ if (data.length) {
+ data.forEach(item => EL_SEARCH.addDoc(item))
+ }
+ this.setState({data: props.data});
+ }
+
+ componentWillUnmount() {
+ this.props.setPerformFilterSearchCallback(undefined)
}
mapResults(results) {
@@ -42,14 +59,14 @@ export default class SearchBar extends Component {
};
setTagSearch = (field, callback) => {
- if (SEARCH_FIELDS.indexOf(field) < 0) {
+ if (this.props.searchFields.indexOf(field) < 0) {
this.setState({ error: true });
return;
}
let params = {};
params.fields = {};
- SEARCH_FIELDS.forEach((f) => {
+ this.props.searchFields.forEach((f) => {
if (f === field) {
params.fields[f] = { boost: 1 }
} else {
@@ -97,43 +114,29 @@ export default class SearchBar extends Component {
render() {
let errorTextClasses = cx('form-item__error-text col-100', { visible: this.state.error });
let errorInputClasses = cx({ 'is-invalid-input': this.state.error });
- const data = window.suite;
return (
-
+ this.performFilterSearch("level:verbose") } text="Verbose" disabled={ !!!this.props.data.length } />
+ this.performFilterSearch("level:debug") } text="Debug" disabled={ !!!this.props.data.length } />
+ this.performFilterSearch("level:info") } text="Info" disabled={ !!!this.props.data.length } />
+ this.performFilterSearch("level:warning") } text="Warning" disabled={ !!!this.props.data.length } />
+ this.performFilterSearch("level:error") } text="Error" disabled={ !!!this.props.data.length } />
+ this.performFilterSearch("level:assert") } text="Assert" disabled={ !!!this.props.data.length } />
+
+ LOGS
+
-
-
- this.performFilterSearch('status:passed') }>
-
- Passed
- { data.passed_count }
- this.performFilterSearch('status:failed') }>
-
- Failed
- { data.failed_count }
- this.performFilterSearch('status:ignored') }>
-
- Ignored
- { data.ignored_count }
-
-
- Duration
- { convertTime(data.duration_millis) }
-
-
-
-
-
-
- { this.state.searchLabel &&
- { this.state.searchLabel }:
}
-
-
- No such key exists!
+
+
+
this.getSearchResults(results) } />
+ this.getSearchResults(results) } />
);
+
diff --git a/html-report/src/components/Suite.js b/html-report/src/components/Suite.js
index 1b86d84..28e7e95 100644
--- a/html-report/src/components/Suite.js
+++ b/html-report/src/components/Suite.js
@@ -3,7 +3,7 @@ import cx from 'classnames';
import randomColor from 'randomcolor';
import convertTime from './../utils/convertTime';
import paths from './../utils/paths';
-import SearchBar from './SearchBar';
+import SuiteFilter from './SuiteFilter';
export default class Suite extends Component {
state = {
@@ -38,7 +38,7 @@ export default class Suite extends Component {
+
+
+ { this.state.searchLabel &&
+ { this.state.searchLabel }:
}
+
+
No such key exists!
Suites list/ Suite {data.id}
-
diff --git a/html-report/src/components/SuiteFilter.js b/html-report/src/components/SuiteFilter.js
new file mode 100644
index 0000000..d4691c1
--- /dev/null
+++ b/html-report/src/components/SuiteFilter.js
@@ -0,0 +1,51 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import cx from 'classnames';
+import convertTime from './../utils/convertTime'
+import SearchBar from './SearchBar';
+
+const SEARCH_FIELDS = ['package_name', 'class_name', 'name', 'id', 'status'];
+const SEARCH_REF = 'id';
+export default class SuiteFilter extends Component {
+ static propTypes = {
+ setSearchResults: PropTypes.func
+ };
+
+ performFilterSearch = (query) => {
+ this.searchBar.performFilterSearch(query)
+ };
+
+ render() {
+ const data = window.suite;
+
+ return (
+
}
+
+
+ (this.performFilterSearch = callback) }
+ />
+
+ )
+ }
+}
diff --git a/html-report/src/components/TestItem.js b/html-report/src/components/TestItem.js
index cbff2ea..642f914 100644
--- a/html-report/src/components/TestItem.js
+++ b/html-report/src/components/TestItem.js
@@ -2,14 +2,21 @@ import React, { Component } from 'react';
import cx from 'classnames';
import convertTime from './../utils/convertTime'
import paths from './../utils/paths'
+import LogContainer from './LogContainer'
export default class TestItem extends Component {
+
+ state = {
+ data: window.test,
+ };
+
componentWillMount() {
document.title = `Test ${window.test.name}`;
}
render() {
- const data = window.test;
+ const data = this.state.data
+
let statusLabelClass = cx('label', 'margin-right-10', {
alert: data.status === 'failed',
success: data.status === 'passed'
@@ -65,6 +72,8 @@ export default class TestItem extends Component {
}) }
+
+ this.performFilterSearch('status:passed') }>
+
+ Passed
+ { data.passed_count }
+ this.performFilterSearch('status:failed') }>
+
+ Failed
+ { data.failed_count }
+ this.performFilterSearch('status:ignored') }>
+
+ Ignored
+ { data.ignored_count }
+
+
+ Duration
+ { convertTime(data.duration_millis) }
+