mirror of
https://github.com/reactos/developer-web-interface.git
synced 2024-11-23 03:49:43 +00:00
Testman integration (#4)
This commit is contained in:
parent
842c5acd06
commit
aceb7cf635
40
app.js
40
app.js
@ -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 });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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 => ({
|
||||
|
@ -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>
|
||||
|
@ -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 => ({
|
||||
|
@ -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>
|
||||
|
36
client/src/components/TestDetails.js
Normal file
36
client/src/components/TestDetails.js
Normal 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;
|
@ -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,
|
||||
|
@ -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}`);
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
20
client/src/redux/reducers/testmanReducer.js
Normal file
20
client/src/redux/reducers/testmanReducer.js
Normal 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;
|
@ -6,23 +6,23 @@ export const getBranch = state => state.branch;
|
||||
export const getNewPage = state => parseInt(state.isLoading.newPage, 10);
|
||||
|
||||
function* handleCommitsLoad() {
|
||||
try {
|
||||
const branch = yield select(getBranch);
|
||||
const newPage = yield select(getNewPage);
|
||||
let commits = yield call(fetchCommits, branch, newPage);
|
||||
yield put(setCommits(commits.commits.body));
|
||||
yield put(
|
||||
setPages(
|
||||
commits.page.next !== undefined ? commits.page.next.page : null,
|
||||
commits.page.prev !== undefined ? commits.page.prev.page : null
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
//dispatch error
|
||||
yield put(setCommitsError(error.toString()));
|
||||
}
|
||||
try {
|
||||
const branch = yield select(getBranch);
|
||||
const newPage = yield select(getNewPage);
|
||||
let commits = yield call(fetchCommits, branch, newPage);
|
||||
yield put(setCommits(commits.commits.body));
|
||||
yield put(
|
||||
setPages(
|
||||
commits.page.next !== undefined ? commits.page.next.page : null,
|
||||
commits.page.prev !== undefined ? commits.page.prev.page : null
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
//dispatch error
|
||||
yield put(setCommitsError(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* watchCommitsLoad() {
|
||||
yield throttle(500, COMMITS.LOAD, handleCommitsLoad);
|
||||
yield throttle(500, COMMITS.LOAD, handleCommitsLoad);
|
||||
}
|
||||
|
25
client/src/redux/sagas/commitsTestmanSaga.js
Normal file
25
client/src/redux/sagas/commitsTestmanSaga.js
Normal 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);
|
||||
}
|
@ -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()
|
||||
]);
|
||||
}
|
||||
|
@ -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
|
||||
|
25
client/src/redux/sagas/pullsTestmanSaga.js
Normal file
25
client/src/redux/sagas/pullsTestmanSaga.js
Normal 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
19
package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user