Testman integration (#4)

This commit is contained in:
Ayush Kumar Sinha 2019-08-15 21:15:23 +05:30 committed by Victor Perevertkin
parent 842c5acd06
commit aceb7cf635
18 changed files with 276 additions and 41 deletions

40
app.js
View File

@ -1,6 +1,7 @@
const dotenv = require('dotenv');
const express = require('express');
const rp = require('request-promise');
const convert = require('xml-js');
const app = express();
//app.disable('query parser');
dotenv.config();
@ -82,7 +83,7 @@ app.get('/api/branches', (req, res) => {
res.json(body);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
@ -122,7 +123,7 @@ app.get('/api/pulls', (req, res) => {
res.json(dataAndPage);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
@ -152,7 +153,7 @@ app.get('/api/buildsets', (req, res) => {
res.json(body);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
@ -179,7 +180,7 @@ app.get('/api/buildreq', (req, res) => {
res.json(body);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
@ -206,11 +207,11 @@ app.get('/api/builds', (req, res) => {
res.json(body);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
//------- BRANCHES END-POINT -------
//------- BUILDERS END-POINT -------
function builderReq() {
const builders = {
@ -231,7 +232,32 @@ app.get('/api/builders', (req, res) => {
res.json(body);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' });
res.json({ error: 'oops...something went wrong' + err });
});
});
//------- TESTMAN END-POINT -------
function testReq(startrev, endrev, page) {
const tests = {
uri: `https://reactos.org/testman/ajax-search.php?startrev=${startrev}&endrev=${endrev}&page=${page}&resultlist=0&requesttype=2`,
resolveWithFullResponse: false,
headers: {
'User-Agent': 'Request-Promise'
}
};
return tests;
}
app.get('/api/testman', (req, res) => {
rp(testReq(req.query.startrev, req.query.endrev, req.query.page))
.then(body => {
const result = convert.xml2json(body, { compact: true, spaces: 2 });
res.send(result);
})
.catch(function(err) {
res.json({ error: 'oops...something went wrong' + err });
});
});

View File

@ -14,7 +14,11 @@ class Commits extends React.Component {
renderCommits = commit => {
return (
<div className='panel-margin' key={commit.sha}>
<CommitsCard {...commit} builds={this.props.build[commit.sha]} />
<CommitsCard
{...commit}
builds={this.props.build[commit.sha]}
tests={this.props.testData[commit.sha]}
/>
</div>
);
};
@ -86,7 +90,8 @@ const mapStateToProps = ({
error,
branch,
page,
build
build,
testData
}) => ({
isLoading,
commits,
@ -94,7 +99,8 @@ const mapStateToProps = ({
error,
branch,
page,
build
build,
testData
});
const mapDispatchToProps = dispatch => ({

View File

@ -1,6 +1,7 @@
import React from 'react';
import { UncontrolledCollapse, CardBody, Card, CardHeader } from 'reactstrap';
import BuildDetails from './BuildDetails';
import TestDetails from './TestDetails';
function CommitsCard(props) {
let tog = 'toggler' + props.sha;
@ -87,7 +88,16 @@ function CommitsCard(props) {
<BuildDetails builds={props.builds} />
) : (
<div>
<strong>Loading Build Data ...</strong>
<strong>Loading Builds...</strong>
</div>
)}
<hr />
<h5>Test Details:</h5>
{props.tests ? (
<TestDetails tests={props.tests} />
) : (
<div>
<strong>No data Exists</strong>
</div>
)}
</CardBody>

View File

@ -13,7 +13,11 @@ class Pulls extends React.Component {
renderPulls = pull => {
return (
<div className='panel-margin' key={pull.id}>
<PullsCard {...pull} builds={this.props.build[pull.number]} />
<PullsCard
{...pull}
builds={this.props.build[pull.number]}
tests={this.props.testData[pull.merge_commit_sha]}
/>
</div>
);
};
@ -80,14 +84,16 @@ const mapStateToProps = ({
page,
isLoading,
error,
build
build,
testData
}) => ({
pulls,
builders,
page,
isLoading,
error,
build
build,
testData
});
const mapDispatchToProps = dispatch => ({

View File

@ -1,6 +1,7 @@
import React from 'react';
import { UncontrolledCollapse, CardBody, Card, CardHeader } from 'reactstrap';
import BuildDetails from './BuildDetails';
import TestDetails from './TestDetails';
function PullsCard(props) {
let tog = 'toggler' + props.id;
@ -12,6 +13,7 @@ function PullsCard(props) {
<CardHeader className='new' type='button' id={tog}>
<div className='row'>
<div className='col-sm'>{props.number}</div>
<div className='col-sm'>{props.merge_commit_sha.substring(0, 7)}</div>
<div className='col-sm'>{props.state}</div>
<div className='col-sm'>{props.user.login}</div>
</div>
@ -49,7 +51,16 @@ function PullsCard(props) {
{props.builds ? (
<BuildDetails builds={props.builds} />
) : (
<div>Loading Pulls data...</div>
<div>Loading Builds...</div>
)}
<hr />
<h5>Test Details:</h5>
{props.tests ? (
<TestDetails tests={props.tests} />
) : (
<p>
<strong>No data Exists</strong>
</p>
)}
</CardBody>
</UncontrolledCollapse>

View File

@ -0,0 +1,36 @@
import React from 'react';
function renderTest(test) {
return (
<React.Fragment key={test.id}>
<div className='col-sm-2'>Test id: {test.id}</div>
<div className='col-sm-4'>
<a
target='_blank'
rel='noreferrer noopener'
href={`https://reactos.org/testman/compare.php?ids=${test.id}`}
>
{test.source}
</a>
</div>
<div className='col-sm-3'>Count: {test.count}</div>
<div className='col-sm-3'>Failures: {test.failures}</div>
</React.Fragment>
);
}
function TestDetails(props) {
return (
<React.Fragment>
{props.tests.length > 0 ? (
<div className='row'>{props.tests.map(renderTest)}</div>
) : (
<p>
<strong>No data Exists</strong>
</p>
)}
</React.Fragment>
);
}
export default TestDetails;

View File

@ -1,4 +1,11 @@
import { COMMITS, BRANCHES, PULLS, BUILD_DATA, BUILDERS } from '../constants';
import {
COMMITS,
BRANCHES,
PULLS,
BUILD_DATA,
BUILDERS,
TESTMAN_DATA
} from '../constants';
export const loadCommits = newPage => ({
type: COMMITS.LOAD,
@ -28,6 +35,19 @@ export const setBuildSetsError = error => ({
error
});
export const loadTestman = () => ({
type: TESTMAN_DATA.LOAD
});
export const setTestman = tests => ({
type: TESTMAN_DATA.LOAD_SUCCESS,
tests
});
export const setTestmanError = error => ({
type: TESTMAN_DATA.LOAD_FAIL,
error
});
export const setPages = (next, prev) => ({
type: 'PAGE_LOAD_SUCCESS',
next,

View File

@ -7,6 +7,27 @@ export const fetchCommits = async (sha, page) => {
return data;
};
export const fetchTests = async (startrev, endrev, page) => {
let results = [];
let keepGoing = true;
while (keepGoing) {
const response = await fetch(
`/api/testman?startrev=${startrev}&endrev=${endrev}&page=${page}`
);
let data = await response.json();
results.push(data.results.result);
if (parseInt(data.results.resultcount._text) > 10) {
page++;
} else {
keepGoing = false;
return results.flat();
}
if (response.status >= 400) {
throw new Error(data.errors);
}
}
};
export const fetchBuildSets = async str => {
if (str) {
const response = await fetch(`/api/buildsets?${str}`);

View File

@ -24,6 +24,12 @@ export const BUILD_DATA = {
LOAD_FAIL: 'BUILD_DATA_LOAD_FAIL'
};
export const TESTMAN_DATA = {
LOAD: 'TESTMAN_DATA_LOAD',
LOAD_SUCCESS: 'TESTMAN_DATA_LOAD_SUCCESS',
LOAD_FAIL: 'TESTMAN_DATA_LOAD_FAIL'
};
export const BUILDERS = {
LOAD: 'BUILDER_LOAD',
LOAD_SUCCESS: 'BUILDER_LOAD_SUCCESS',

View File

@ -10,6 +10,7 @@ import pullsReducer from './pullsReducer';
import pageReducer from './pageReducer';
import pullStateReducer from './pullStateReducer';
import buildStatusReducer from './buildStatusReducer';
import testmanReducer from './testmanReducer';
const rootReducer = combineReducers({
isLoading: loadingReducer,
@ -21,7 +22,8 @@ const rootReducer = combineReducers({
pulls: pullsReducer,
pullState: pullStateReducer,
page: pageReducer,
build: buildStatusReducer
build: buildStatusReducer,
testData: testmanReducer
});
export default rootReducer;

View File

@ -0,0 +1,20 @@
import { TESTMAN_DATA } from '../constants';
const testmanReducer = (state = {}, action) => {
if (action.type === TESTMAN_DATA.LOAD_SUCCESS) {
let cleanedTestmanData = {};
for (const [sha, tests] of Object.entries(action.tests)) {
cleanedTestmanData[sha] = tests.map(t => {
let reducedKeyVal = {};
for (const [k, v] of Object.entries(t)) {
reducedKeyVal[k] = v._text;
}
return reducedKeyVal;
})
}
return cleanedTestmanData;
}
return state;
};
export default testmanReducer;

View File

@ -0,0 +1,25 @@
import { takeEvery, call, select, put } from 'redux-saga/effects';
import { COMMITS } from '../constants';
import { fetchTests } from '../api';
import { setTestman, setTestmanError } from '../actions';
function* handleTestmanLoad() {
try {
const commits = yield select(state => state.commits);
const shas = commits.map(commit => commit.sha);
const testResults = yield call(fetchTests, shas[9], shas[0], 1);
const testBySha = {};
for (let sha of shas) {
testBySha[sha] = testResults.filter(test =>
sha.startsWith(test.revision._text)
);
}
yield put(setTestman(testBySha));
} catch (error) {
yield put(setTestmanError(error.toString()));
}
}
export default function* watchTestmanLoad() {
yield takeEvery(COMMITS.LOAD_SUCCESS, handleTestmanLoad);
}

View File

@ -6,6 +6,9 @@ import pullsSaga from './pullsSaga';
import buildersSaga from './buildersSaga';
import buildSetSaga from './buildSetSaga';
import pullsBuildSaga from './pullsBuildSaga';
import commitsTestmanSaga from './commitsTestmanSaga';
import pullsTestmanSaga from './pullsTestmanSaga';
export default function* rootSaga() {
yield all([
commitsSaga(),
@ -13,6 +16,8 @@ export default function* rootSaga() {
pullsSaga(),
buildersSaga(),
buildSetSaga(),
pullsBuildSaga()
pullsBuildSaga(),
commitsTestmanSaga(),
pullsTestmanSaga()
]);
}

View File

@ -21,7 +21,8 @@ function getBuildReqQString(pulls, buildSetsRaw) {
buildSetsRaw.filter(
build =>
build.sourcestamps[0].branch === `refs/pull/${pull.number}/head` ||
build.sourcestamps[0].branch === `refs/pull/${pull.number}/merge`
build.sourcestamps[0].branch === `refs/pull/${pull.number}/merge` ||
build.sourcestamps[0].revision === `${pull.merge_commit_sha}`
)
)
.map(bd => 'buildsetid__contains=' + bd.bsid)
@ -49,12 +50,13 @@ function* handlePullsBuildLoad() {
const buildsRaw = yield call(fetchBuilds, getBuildQString(buildReqsRaw));
const buildsByPR = {};
for (let { number } of pulls) {
for (let { number, merge_commit_sha } of pulls) {
const buildSetIds = buildSetsRaw
.filter(
bs =>
bs.sourcestamps[0].branch === `refs/pull/${number}/head` ||
bs.sourcestamps[0].branch === `refs/pull/${number}/merge`
bs.sourcestamps[0].branch === `refs/pull/${number}/merge` ||
bs.sourcestamps[0].revision === `${merge_commit_sha}`
)
.map(bs => bs.bsid);
const buildReqIds = buildReqsRaw

View File

@ -0,0 +1,25 @@
import { takeEvery, call, select, put } from 'redux-saga/effects';
import { PULLS } from '../constants';
import { fetchTests } from '../api';
import { setTestman, setTestmanError } from '../actions';
function* handleTestmanLoad() {
try {
const pulls = yield select(state => state.pulls);
const shas = pulls.map(pull => pull.merge_commit_sha);
const testResults = yield call(fetchTests, shas[9], shas[0], 1);
const testByPulls = {};
for (let sha of shas) {
testByPulls[sha] = testResults.filter(test =>
sha.startsWith(test.revision._text)
);
}
yield put(setTestman(testByPulls));
} catch (error) {
yield put(setTestmanError(error.toString()));
}
}
export default function* watchTestmanLoad() {
yield takeEvery(PULLS.LOAD_SUCCESS, handleTestmanLoad);
}

19
package-lock.json generated
View File

@ -1262,9 +1262,9 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"loose-envify": {
"version": "1.4.0",
@ -1909,6 +1909,11 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
@ -2363,6 +2368,14 @@
"mkdirp": "^0.5.1"
}
},
"xml-js": {
"version": "1.6.11",
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
"requires": {
"sax": "^1.2.4"
}
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",

View File

@ -25,6 +25,7 @@
"path": "^0.12.7",
"react-router-dom": "^5.0.1",
"request": "^2.88.0",
"request-promise": "^4.2.4"
"request-promise": "^4.2.4",
"xml-js": "^1.6.11"
}
}