diff --git a/TestResultSummaryService/routes/getTestBySearch.js b/TestResultSummaryService/routes/getTestBySearch.js
index f8556cb9..d8287265 100644
--- a/TestResultSummaryService/routes/getTestBySearch.js
+++ b/TestResultSummaryService/routes/getTestBySearch.js
@@ -103,6 +103,7 @@ searchOutputId = async (build) => {
testResult: test.testResult,
duration: test.duration,
machine: build.machine,
+ timestamp: build.timestamp,
};
});
}
diff --git a/TestResultSummaryService/routes/getTestByTestName.js b/TestResultSummaryService/routes/getTestByTestName.js
new file mode 100644
index 00000000..482c9450
--- /dev/null
+++ b/TestResultSummaryService/routes/getTestByTestName.js
@@ -0,0 +1,48 @@
+const { TestResultsDB, ObjectID } = require('../Database');
+module.exports = async (req, res) => {
+ const { testName, buildNames, beforeTimestamp } = req.query;
+ const db = new TestResultsDB();
+ let query = {};
+ if (buildNames) {
+ const buildNameArray = buildNames.split(',');
+ const rootBuildIds = [];
+ await Promise.all(
+ buildNameArray.map(async (buildName) => {
+ const buildData = await db.getData({ buildName }).toArray();
+ buildData.forEach((build) => {
+ rootBuildIds.push(new ObjectID(build._id));
+ });
+ })
+ );
+ query.rootBuildId = { $in: rootBuildIds };
+ }
+ if (beforeTimestamp) {
+ query.timestamp = { $lt: parseInt(beforeTimestamp) };
+ }
+ const history = await db.aggregate([
+ {
+ $match: query,
+ },
+ { $unwind: '$tests' },
+ {
+ $match: {
+ 'tests.testName': testName,
+ },
+ },
+ {
+ $project: {
+ parentId: 1,
+ buildName: 1,
+ buildNum: 1,
+ machine: 1,
+ buildUrl: 1,
+ tests: 1,
+ timestamp: 1,
+ javaVersion: 1,
+ },
+ },
+ { $sort: { timestamp: -1 } },
+ ]);
+
+ res.send(history);
+};
diff --git a/TestResultSummaryService/routes/getTestNames.js b/TestResultSummaryService/routes/getTestNames.js
new file mode 100644
index 00000000..a957cdba
--- /dev/null
+++ b/TestResultSummaryService/routes/getTestNames.js
@@ -0,0 +1,13 @@
+const { TestResultsDB, ObjectID } = require('../Database');
+module.exports = async (req, res) => {
+ const testResultsDB = new TestResultsDB();
+ const data = await testResultsDB.aggregate([
+ { $group: { _id: '$tests.testName' } },
+ { $unwind: '$_id' },
+ { $group: { _id: '$_id' } },
+ { $sort: { _id: 1 } },
+ ]);
+ testNames = data.map(({ _id }) => _id);
+
+ res.send(testNames);
+};
diff --git a/TestResultSummaryService/routes/index.js b/TestResultSummaryService/routes/index.js
index 9c01bbbe..22d541c6 100644
--- a/TestResultSummaryService/routes/index.js
+++ b/TestResultSummaryService/routes/index.js
@@ -42,6 +42,8 @@ app.get('/getTestBuildsByMachine', wrap(require('./getTestBuildsByMachine')));
app.get('/getTestById', wrap(require('./getTestById')));
app.get('/getTestBySearch', wrap(require('./getTestBySearch')));
app.get('/getTestByVersionInfo', wrap(require('./getTestByVersionInfo')));
+app.get('/getTestNames', wrap(require('./getTestNames')));
+app.get('/getTestByTestName', wrap(require('./getTestByTestName')));
app.get('/getTestPerPlatform', wrap(require('./getTestPerPlatform')));
app.get('/getTopLevelBuildNames', wrap(require('./getTopLevelBuildNames')));
app.get('/getTotals', wrap(require('./getTotals')));
diff --git a/test-result-summary-client/src/AdvancedSearch/AdvancedSearch.jsx b/test-result-summary-client/src/AdvancedSearch/AdvancedSearch.jsx
new file mode 100644
index 00000000..3099c329
--- /dev/null
+++ b/test-result-summary-client/src/AdvancedSearch/AdvancedSearch.jsx
@@ -0,0 +1,85 @@
+import React, { Component } from 'react';
+import { Space, Button } from 'antd';
+import { fetchData } from '../utils/Utils';
+import InputAutoComplete from './InputAutoComplete';
+import InputSelect from './InputSelect';
+import SearchTestResult from '../Search/SearchTestResult';
+
+export default class AdvancedSearch extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ testName: '',
+ testNameOptions: [],
+ buildNameOptions: [],
+ };
+ }
+ async componentDidMount() {
+ await this.updateData();
+ }
+
+ async updateData() {
+ const data = await fetchData(`/api/getTopLevelBuildNames?type=Test`);
+ let buildNames = [];
+ if (data) {
+ buildNames = data.map((value) => {
+ return value._id.buildName;
+ });
+ }
+ const testNames = await fetchData(`/api/getTestNames`);
+
+ this.setState({
+ buildNameOptions: buildNames.sort(),
+ testNameOptions: testNames,
+ });
+ }
+ handleSubmit = async () => {
+ const { testName, buildNames } = this.state;
+ const buildNamesStr = buildNames.join(',');
+ const data = await fetchData(
+ `/api/getTestByTestName?testName=${testName}&buildNames=${buildNamesStr}`
+ );
+
+ const result = data.map((element) => ({
+ ...element,
+ ...element.tests,
+ }));
+
+ this.setState({ result });
+ };
+ onBuildNameChange = (value) => {
+ this.setState({ buildNames: value });
+ };
+
+ onTestNameChange = (value) => {
+ this.setState({ testName: value });
+ };
+ onSelect = (value) => {
+ this.setState({ value });
+ };
+ render() {
+ const { buildNameOptions, testNameOptions, testName, result } =
+ this.state;
+ return (
+
+
+
+
+
+ {result ? : ''}
+
+ );
+ }
+}
diff --git a/test-result-summary-client/src/AdvancedSearch/InputAutoComplete.jsx b/test-result-summary-client/src/AdvancedSearch/InputAutoComplete.jsx
new file mode 100644
index 00000000..0990e6e2
--- /dev/null
+++ b/test-result-summary-client/src/AdvancedSearch/InputAutoComplete.jsx
@@ -0,0 +1,24 @@
+import React, { Component } from 'react';
+import { AutoComplete } from 'antd';
+
+export default class InputAutoComplete extends Component {
+ render() {
+ return (
+ ({ value }))}
+ style={{
+ width: 600,
+ }}
+ onSelect={this.props.onSelect}
+ onChange={this.props.onChange}
+ placeholder={this.props.message}
+ filterOption={(inputValue, option) =>
+ option.value
+ .toUpperCase()
+ .includes(inputValue.toUpperCase())
+ }
+ />
+ );
+ }
+}
diff --git a/test-result-summary-client/src/AdvancedSearch/InputSelect.jsx b/test-result-summary-client/src/AdvancedSearch/InputSelect.jsx
new file mode 100644
index 00000000..88002094
--- /dev/null
+++ b/test-result-summary-client/src/AdvancedSearch/InputSelect.jsx
@@ -0,0 +1,20 @@
+import React, { Component } from 'react';
+import { Select } from 'antd';
+
+export default class InoutSelect extends Component {
+ render() {
+ const options = this.props.options.map((value) => ({ value }));
+ return (
+
+ );
+ }
+}
diff --git a/test-result-summary-client/src/AdvancedSearch/index.js b/test-result-summary-client/src/AdvancedSearch/index.js
new file mode 100644
index 00000000..a15faf92
--- /dev/null
+++ b/test-result-summary-client/src/AdvancedSearch/index.js
@@ -0,0 +1 @@
+export { default as AdvancedSearch } from './AdvancedSearch';
diff --git a/test-result-summary-client/src/App.jsx b/test-result-summary-client/src/App.jsx
index 963a0c4e..913abc22 100644
--- a/test-result-summary-client/src/App.jsx
+++ b/test-result-summary-client/src/App.jsx
@@ -11,6 +11,8 @@ import { TestCompare } from './TestCompare/';
import { ThirdPartyAppView } from './ThirdPartyAppView/';
import { PerfCompare } from './PerfCompare/';
import { TabularView } from './TabularView/';
+import { AdvancedSearch } from './AdvancedSearch/';
+
import {
AllTestsInfo,
BuildDetail,
@@ -96,7 +98,7 @@ export default class App extends Component {
),
},
{
- key: '6',
+ key: '2',
label: (
AQAvit Verification
@@ -104,7 +106,7 @@ export default class App extends Component {
),
},
{
- key: '2',
+ key: '3',
label: (
Test Compare
@@ -112,7 +114,7 @@ export default class App extends Component {
),
},
{
- key: '3',
+ key: '4',
label: 'Perf Related',
children: [
{
@@ -142,7 +144,7 @@ export default class App extends Component {
],
},
{
- key: '4',
+ key: '5',
label: (
Dashboard
@@ -150,13 +152,21 @@ export default class App extends Component {
),
},
{
- key: '5',
+ key: '6',
label: (
Third Party Applications
),
},
+ {
+ key: '7',
+ label: (
+
+ Advanced Search
+
+ ),
+ },
]}
/>
@@ -171,26 +181,90 @@ export default class App extends Component {
}}
>
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
diff --git a/test-result-summary-client/src/Build/AllTestsInfo.jsx b/test-result-summary-client/src/Build/AllTestsInfo.jsx
index 22dd2431..e7a90111 100644
--- a/test-result-summary-client/src/Build/AllTestsInfo.jsx
+++ b/test-result-summary-client/src/Build/AllTestsInfo.jsx
@@ -116,6 +116,7 @@ const Build = () => {
buildId: buildData[0]._id,
buildUrl: buildData[0].buildUrl,
rerunUrl: buildData[0].rerunLink,
+ timestamp: buildData[0].timestamp,
};
ret.action = {
testId: test._id,
diff --git a/test-result-summary-client/src/Build/TestTable.jsx b/test-result-summary-client/src/Build/TestTable.jsx
index 37c5f525..33a2af7d 100644
--- a/test-result-summary-client/src/Build/TestTable.jsx
+++ b/test-result-summary-client/src/Build/TestTable.jsx
@@ -24,17 +24,9 @@ const testResultSortingArray = [
export default class TestTable extends Component {
state = {
- filteredData: [],
+ filteredData: undefined,
};
- async componentDidUpdate(prevProps) {
- if (prevProps.testData !== this.props.testData) {
- this.setState({
- filteredData: this.props.testData,
- });
- }
- }
-
handleFilterChange = (filteredData) => {
this.setState({ filteredData });
};
@@ -266,6 +258,17 @@ export default class TestTable extends Component {
/>
),
},
+ {
+ title: 'Date',
+ dataIndex: 'timestamp',
+ key: 'timestamp',
+ width: 100,
+ render: (timestamp) =>
+ timestamp ? new Date(timestamp).toLocaleString() : 'N/A',
+ sorter: (a, b) => {
+ return a.timestamp - b.timestamp;
+ },
+ },
];
if (parents) {
columns.push(
@@ -351,7 +354,7 @@ export default class TestTable extends Component {
title}
pagination={{
diff --git a/test-result-summary-client/src/Search/SearchTestResult.jsx b/test-result-summary-client/src/Search/SearchTestResult.jsx
index 75bd017f..2d0260f7 100644
--- a/test-result-summary-client/src/Search/SearchTestResult.jsx
+++ b/test-result-summary-client/src/Search/SearchTestResult.jsx
@@ -1,35 +1,17 @@
import React, { Component } from 'react';
import TestTable from '../Build/TestTable';
-export default class SearchResult extends Component {
- state = {
- testData: [],
- };
-
- async componentDidUpdate(prevProps) {
- if (prevProps.tests !== this.props.tests) {
- await this.updateData();
- }
- }
-
- async updateData() {
- const { tests } = this.props;
-
- const testData = tests.map((element) => {
- const ret = {
- key: element._id,
- sortName: element.testName,
- testName: element.testName,
- action: { testId: element._id, testName: element.testName },
- result: { testResult: element.testResult, testId: element._id },
- build: { buildName: element.buildName },
- duration: element.duration,
- machine: element.machine,
- sortMachine: element.machine,
- buildUrl: element.buildUrl,
- };
- return ret;
- });
+export default class SearchTestResult extends Component {
+ render() {
+ const { searchText = '', tests } = this.props;
+ const testData = tests.map((element) => ({
+ ...element,
+ key: element._id,
+ sortName: element.testName,
+ action: { testId: element._id, testName: element.testName },
+ result: { testResult: element.testResult, testId: element._id },
+ build: { buildName: element.buildName },
+ }));
testData.sort((a, b) => {
let rt = a.result.testResult.localeCompare(b.result.testResult);
@@ -39,23 +21,10 @@ export default class SearchResult extends Component {
return rt;
});
- this.setState({
- testData,
- });
- }
-
- render() {
- const { searchText } = this.props;
- const { testData } = this.state;
+ const message = searchText ? ' "' + searchText + '"' : '';
return (
);
diff --git a/test-result-summary-client/src/Search/index.js b/test-result-summary-client/src/Search/index.js
index e512ce3e..bb88682c 100644
--- a/test-result-summary-client/src/Search/index.js
+++ b/test-result-summary-client/src/Search/index.js
@@ -1,2 +1,3 @@
export { default as SearchOutput } from './SearchOutput';
export { default as SearchResult } from './SearchResult';
+export { default as SearchTestResult } from './SearchTestResult';