Merge branch 'master' into feature/JENKINS-37007-favorites-animations
This commit is contained in:
commit
8a636726fa
|
@ -35,7 +35,7 @@
|
|||
"skin-deep": "^0.16.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jenkins-cd/design-language": "0.0.64",
|
||||
"@jenkins-cd/design-language": "0.0.65",
|
||||
"@jenkins-cd/js-extensions": "0.0.19",
|
||||
"@jenkins-cd/js-modules": "0.0.5",
|
||||
"@jenkins-cd/sse-gateway": "0.0.6",
|
||||
|
|
|
@ -108,7 +108,7 @@ export class LogConsole extends Component {
|
|||
|
||||
render() {
|
||||
const lines = this.state.lines;
|
||||
const { prefix = '' } = this.props;
|
||||
const { prefix = '', hasMore = false } = this.props; // if hasMore true then show link to full log
|
||||
if (!lines) {
|
||||
return null;
|
||||
}
|
||||
|
@ -116,8 +116,21 @@ export class LogConsole extends Component {
|
|||
return (<code
|
||||
className="block"
|
||||
>
|
||||
{ hasMore && <div key={0} id={`${prefix}log-${0}`} className="fullLog">
|
||||
<a
|
||||
key={0}
|
||||
href={`?start=0#${prefix || ''}log-${1}`}
|
||||
>
|
||||
Show complete log
|
||||
</a>
|
||||
</div>}
|
||||
{ lines.map((line, index) => <p key={index + 1} id={`${prefix}log-${index + 1}`}>
|
||||
<a key={index + 1} href={`#${prefix || ''}log-${index + 1}`} name={`${prefix}log-${index + 1}`}>{line}</a>
|
||||
<a
|
||||
key={index + 1}
|
||||
href={`#${prefix || ''}log-${index + 1}`}
|
||||
name={`${prefix}log-${index + 1}`}
|
||||
>{line}
|
||||
</a>
|
||||
</p>)}</code>);
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +142,7 @@ LogConsole.propTypes = {
|
|||
scrollToAnchorTimeOut: func,
|
||||
scrollBottom: func,
|
||||
prefix: string,
|
||||
hasMore: bool,
|
||||
};
|
||||
|
||||
export default scrollHelper(LogConsole);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Icon } from 'react-material-icons-blue';
|
||||
import { fetchAllSuffix as suffix } from '../util/UrlUtils';
|
||||
|
||||
const { string } = PropTypes;
|
||||
|
||||
|
@ -12,6 +13,7 @@ export default class LogToolbar extends Component {
|
|||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
const logUrl = url.includes(suffix) ? url : `${url}${suffix}`;
|
||||
const style = { fill: '#4a4a4a' };
|
||||
return (<div className="log-header">
|
||||
<div className="log-header__section">
|
||||
|
@ -21,13 +23,13 @@ export default class LogToolbar extends Component {
|
|||
<a {...{
|
||||
title: 'Display the log in new window',
|
||||
target: '_blank',
|
||||
href: `${url}?start=0`,
|
||||
href: logUrl,
|
||||
}}>
|
||||
<Icon size={24} {...{ style, icon: 'launch' }} />
|
||||
</a>
|
||||
<a {...{
|
||||
title: 'Download the log file',
|
||||
href: `${url}?start=0&download=true`,
|
||||
href: `${logUrl}&download=true`,
|
||||
}}>
|
||||
<Icon size={24} {...{ style, icon: 'file_download' }} />
|
||||
</a>
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
createSelector,
|
||||
} from '../redux';
|
||||
|
||||
import { calculateStepsBaseUrl, calculateRunLogURLObject, calculateNodeBaseUrl } from '../util/UrlUtils';
|
||||
import { calculateStepsBaseUrl, calculateRunLogURLObject, calculateNodeBaseUrl, calculateFetchAll } from '../util/UrlUtils';
|
||||
import { calculateNode } from '../util/KaraokeHelper';
|
||||
|
||||
|
||||
|
@ -45,7 +45,9 @@ export class RunDetailsPipeline extends Component {
|
|||
} else {
|
||||
// console.log('fetch the log directly')
|
||||
const logGeneral = calculateRunLogURLObject(this.mergedConfig);
|
||||
fetchLog({ ...logGeneral });
|
||||
// fetchAll indicates whether we want all logs
|
||||
const fetchAll = this.mergedConfig.fetchAll;
|
||||
fetchLog({ ...logGeneral, fetchAll });
|
||||
}
|
||||
|
||||
// Listen for pipeline flow node events.
|
||||
|
@ -127,10 +129,13 @@ export class RunDetailsPipeline extends Component {
|
|||
// if we have actions we fire them
|
||||
this.props[nodeAction.action](this.mergedConfig);
|
||||
}
|
||||
const fetchAll = this.mergedConfig.fetchAll;
|
||||
// console.log('this.mergedConfig.fetchAll', fetchAll)
|
||||
// if we only interested in logs (in case of e.g. freestyle)
|
||||
const { logs, fetchLog } = nextProps;
|
||||
if (logs !== this.props.logs) {
|
||||
if (logs !== this.props.logs || fetchAll) {
|
||||
const logGeneral = calculateRunLogURLObject(this.mergedConfig);
|
||||
// console.log('logGenralReceive', logGeneral)
|
||||
const log = logs ? logs[logGeneral.url] : null;
|
||||
if (log && log !== null) {
|
||||
// we may have a streaming log
|
||||
|
@ -144,20 +149,26 @@ export class RunDetailsPipeline extends Component {
|
|||
this.timeout = setTimeout(() => fetchLog({ ...logGeneral, newStart }), 1000);
|
||||
}
|
||||
}
|
||||
} else if (fetchAll) {
|
||||
// kill current timeout if any
|
||||
clearTimeout(this.timeout);
|
||||
// we need to get mpre input from the log stream
|
||||
this.timeout = setTimeout(() => fetchLog({ ...logGeneral, fetchAll }), 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentWillUnmount() {
|
||||
const domNode = ReactDOM.findDOMNode(this.refs.scrollArea);
|
||||
domNode.removeEventListener('wheel', this._onScrollHandler);
|
||||
document.removeEventListener('keydown', this._handleKeys);
|
||||
if (this.listener.sse) {
|
||||
sse.unsubscribe(this.listener.sse);
|
||||
delete this.listener.sse;
|
||||
}
|
||||
this.props.cleanNodePointer();
|
||||
clearTimeout(this.timeout);
|
||||
domNode.removeEventListener('wheel', this._onScrollHandler);
|
||||
document.removeEventListener('keydown', this._handleKeys);
|
||||
}
|
||||
|
||||
// need to register handler to step out of karaoke mode
|
||||
|
@ -183,6 +194,7 @@ export class RunDetailsPipeline extends Component {
|
|||
isMultiBranch,
|
||||
params: { pipeline: name, branch, runId, node: nodeParam },
|
||||
} = props;
|
||||
const fetchAll = calculateFetchAll(props);
|
||||
// we would use default properties however the node can be null so no default properties will be triggered
|
||||
let { nodeReducer } = props;
|
||||
if (!nodeReducer) {
|
||||
|
@ -191,7 +203,7 @@ export class RunDetailsPipeline extends Component {
|
|||
// if we have a node param we do not want the calculation of the focused node
|
||||
const node = nodeParam || nodeReducer.id;
|
||||
|
||||
const mergedConfig = { ...config, name, branch, runId, isMultiBranch, node, nodeReducer, followAlong };
|
||||
const mergedConfig = { ...config, name, branch, runId, isMultiBranch, node, nodeReducer, followAlong, fetchAll };
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
|
@ -267,6 +279,19 @@ export class RunDetailsPipeline extends Component {
|
|||
};
|
||||
const noSteps = !log && currentSteps && currentSteps.model && currentSteps.model.length === 0;
|
||||
const shouldShowLogHeader = log !== null || !noSteps;
|
||||
const logProps = {
|
||||
scrollToBottom,
|
||||
key: logGeneral.url,
|
||||
};
|
||||
if (log) {
|
||||
// in follow along the Full Log button should not be shown, since you see everything already
|
||||
if (followAlong) {
|
||||
logProps.hasMore = false;
|
||||
} else {
|
||||
logProps.hasMore = log.hasMore;
|
||||
}
|
||||
logProps.logArray = log.logArray;
|
||||
}
|
||||
return (
|
||||
<div ref="scrollArea">
|
||||
{ nodes && nodes[nodeKey] && <Extensions.Renderer
|
||||
|
@ -298,7 +323,7 @@ export class RunDetailsPipeline extends Component {
|
|||
</EmptyStateView>
|
||||
}
|
||||
|
||||
{ log && <LogConsole key={logGeneral.url} logArray={log.logArray} scrollToBottom={scrollToBottom} /> }
|
||||
{ log && <LogConsole {...logProps} /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { ResultItem } from '@jenkins-cd/design-language';
|
||||
import { calculateLogUrl } from '../util/UrlUtils';
|
||||
import { calculateFetchAll, calculateLogUrl } from '../util/UrlUtils';
|
||||
|
||||
import LogConsole from './LogConsole';
|
||||
|
||||
|
@ -18,7 +18,8 @@ export default class Node extends Component {
|
|||
const { config = {} } = this.context;
|
||||
const node = this.expandAnchor(this.props);
|
||||
if (node && node.isFocused) {
|
||||
const mergedConfig = { ...config, node, nodesBaseUrl };
|
||||
const fetchAll = node.fetchAll;
|
||||
const mergedConfig = { ...config, node, nodesBaseUrl, fetchAll };
|
||||
fetchLog(mergedConfig);
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +34,9 @@ export default class Node extends Component {
|
|||
}
|
||||
const { config = {} } = this.context;
|
||||
const node = this.expandAnchor(nextProps);
|
||||
const mergedConfig = { ...config, node, nodesBaseUrl };
|
||||
if (logs && logs !== this.props.logs) {
|
||||
const fetchAll = node.fetchAll;
|
||||
const mergedConfig = { ...config, node, nodesBaseUrl, fetchAll };
|
||||
if (logs && logs !== this.props.logs || fetchAll) {
|
||||
const key = calculateLogUrl(mergedConfig);
|
||||
const log = logs ? logs[key] : null;
|
||||
if (log && log !== null) {
|
||||
|
@ -47,6 +49,9 @@ export default class Node extends Component {
|
|||
this.clearThisTimeout();
|
||||
this.timeout = setTimeout(() => fetchLog({ ...mergedConfig }), 1000);
|
||||
}
|
||||
} else if (!log && fetchAll) { // in case the link "full log" is clicked we need to trigger a refetch
|
||||
this.clearThisTimeout();
|
||||
this.timeout = setTimeout(() => fetchLog({ ...mergedConfig }), 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,26 +65,33 @@ export default class Node extends Component {
|
|||
clearTimeout(this.timeout);
|
||||
}
|
||||
}
|
||||
// Calculate whether we need to expand the step due to linking
|
||||
/*
|
||||
* Calculate whether we need to expand the step due to linking.
|
||||
* When we trigger a log-0 that means we want to see the full log
|
||||
*/
|
||||
expandAnchor(props) {
|
||||
const { node, location: { hash: anchorName } } = props;
|
||||
const isFocused = true;
|
||||
const fetchAll = calculateFetchAll(props);
|
||||
const general = { ...node, fetchAll };
|
||||
// e.g. #step-10-log-1 or #step-10
|
||||
if (anchorName) {
|
||||
const stepReg = /step-([0-9]{1,})?($|-log-([0-9]{1,})$)/;
|
||||
const match = stepReg.exec(anchorName);
|
||||
|
||||
if (match && match[1] && match[1] === node.id) {
|
||||
return { ...node, isFocused };
|
||||
return { ...general, isFocused };
|
||||
}
|
||||
} else if (this.state && this.state.isFocused) {
|
||||
return { ...node, isFocused };
|
||||
return { ...general, isFocused };
|
||||
}
|
||||
return { ...node };
|
||||
return general;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { logs, nodesBaseUrl, fetchLog, followAlong } = this.props;
|
||||
const node = this.expandAnchor(this.props);
|
||||
const fetchAll = node.fetchAll;
|
||||
// Early out
|
||||
if (!node || !fetchLog) {
|
||||
return null;
|
||||
|
@ -95,7 +107,7 @@ export default class Node extends Component {
|
|||
} = node;
|
||||
|
||||
const resultRun = result === 'UNKNOWN' || !result ? state : result;
|
||||
const log = logs ? logs[calculateLogUrl({ ...config, node, nodesBaseUrl })] : null;
|
||||
const log = logs ? logs[calculateLogUrl({ ...config, node, nodesBaseUrl, fetchAll })] : null;
|
||||
const getLogForNode = () => {
|
||||
// in case we do not have logs, or the logs are have no information attached we refetch them
|
||||
if (!log || !log.logArray) {
|
||||
|
@ -108,7 +120,21 @@ export default class Node extends Component {
|
|||
resultRun.toLowerCase() === 'failure'
|
||||
|| (resultRun.toLowerCase() === 'running' && followAlong)
|
||||
;
|
||||
return (<div>
|
||||
const logProps = {
|
||||
scrollToBottom,
|
||||
key: id,
|
||||
prefix: `step-${id}-`,
|
||||
};
|
||||
if (log) {
|
||||
// in follow along the Full Log button should not be shown, since you see everything already
|
||||
if (followAlong) {
|
||||
logProps.hasMore = false;
|
||||
} else {
|
||||
logProps.hasMore = log.hasMore;
|
||||
}
|
||||
logProps.logArray = log.logArray;
|
||||
}
|
||||
return (<div className="logConsole">
|
||||
<ResultItem
|
||||
key={id}
|
||||
result={runResult}
|
||||
|
@ -117,12 +143,11 @@ export default class Node extends Component {
|
|||
onExpand={getLogForNode}
|
||||
durationMillis={durationInMillis}
|
||||
>
|
||||
{ log && <LogConsole
|
||||
key={id}
|
||||
logArray={log.logArray}
|
||||
scrollToBottom={scrollToBottom}
|
||||
prefix={`step-${id}-`}
|
||||
/> }
|
||||
{ log && <LogConsole {...logProps} /> }
|
||||
|
||||
{ !log && <span>
|
||||
|
||||
</span> }
|
||||
</ResultItem>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ const TestCaseResultRow = (props) => {
|
|||
|
||||
let statusIndicator = null;
|
||||
switch (t.status) {
|
||||
case 'REGRESSION':
|
||||
case 'FAILED':
|
||||
statusIndicator = StatusIndicator.validResultValues.failure;
|
||||
break;
|
||||
|
@ -67,12 +68,11 @@ export default class TestResult extends Component {
|
|||
const suites = this.props.testResults.suites;
|
||||
const tests = [].concat.apply([], suites.map(t => t.cases));
|
||||
|
||||
// possible statuses: PASSED, FAILED, SKIPPED
|
||||
const failures = tests.filter(t => t.status === 'FAILED');
|
||||
// one of 5 possible statuses: PASSED, FIXED, SKIPPED, FAILED, REGRESSION see: hudson.tasks.junit.CaseResult$Status :(
|
||||
const fixed = tests.filter(t => t.status === 'FIXED');
|
||||
const skipped = tests.filter(t => t.status === 'SKIPPED');
|
||||
const newFailures = failures.filter(t => t.age === 1);
|
||||
const existingFailures = failures.filter(t => t.age > 1);
|
||||
const newFailures = tests.filter(t => (t.age <= 1 && t.status === 'FAILED') || t.status === 'REGRESSION');
|
||||
const existingFailures = tests.filter(t => t.age > 1 && t.status === 'FAILED');
|
||||
|
||||
let passBlock = null;
|
||||
let newFailureBlock = null;
|
||||
|
@ -129,13 +129,6 @@ export default class TestResult extends Component {
|
|||
</div>);
|
||||
}
|
||||
|
||||
if (fixed.length > 0) {
|
||||
fixedBlock = (<div className="test-result-block fixed-block">
|
||||
<h4>Fixed</h4>
|
||||
{fixed.map((t, i) => <TestCaseResultRow key={i} testCase={t} />)}
|
||||
</div>);
|
||||
}
|
||||
|
||||
if (skipped.length > 0) {
|
||||
skippedBlock = (<div className="test-result-block skipped-block">
|
||||
<h4>Skipped - {skipped.length}</h4>
|
||||
|
@ -144,14 +137,22 @@ export default class TestResult extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
// always show fixed, whether showing totals or the encouraging message
|
||||
if (fixed.length > 0) {
|
||||
fixedBlock = (<div className="test-result-block fixed-block">
|
||||
<h4>Fixed</h4>
|
||||
{fixed.map((t, i) => <TestCaseResultRow key={i} testCase={t} />)}
|
||||
</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{passBlock}
|
||||
{summaryBlock}
|
||||
{newFailureBlock}
|
||||
{existingFailureBlock}
|
||||
{fixedBlock}
|
||||
{skippedBlock}
|
||||
{passBlock}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ export const actionHandlers = {
|
|||
[ACTION_TYPES.SET_LOGS](state, { payload }): State {
|
||||
const logs = { ...state.logs } || {};
|
||||
logs[payload.logUrl] = payload;
|
||||
|
||||
return state.set('logs', logs);
|
||||
},
|
||||
|
||||
|
@ -768,6 +769,7 @@ export const actions = {
|
|||
const data = getState().adminStore.logs;
|
||||
const logUrl = calculateLogUrl(config);
|
||||
if (
|
||||
config.fetchAll ||
|
||||
!data || !data[logUrl] ||
|
||||
config.newStart > 0 ||
|
||||
(data && data[logUrl] && data[logUrl].newStart > 0 || !data[logUrl].logArray)
|
||||
|
@ -777,10 +779,21 @@ export const actions = {
|
|||
config.newStart || null,
|
||||
response => response.response.text()
|
||||
.then(text => {
|
||||
// By default only last 150 KB log data is returned in the response.
|
||||
const maxLength = 150000;
|
||||
const contentLength = Number(response.response.headers.get('X-Text-Size'));
|
||||
// set flag that there are more logs then we deliver
|
||||
let hasMore = contentLength > maxLength;
|
||||
// when we came from ?start=0, hasMore has to be false since there is no more
|
||||
// console.log(config.fetchAll, 'inner')
|
||||
if (config.fetchAll) {
|
||||
hasMore = false;
|
||||
}
|
||||
const { newStart } = response;
|
||||
const payload = {
|
||||
logUrl,
|
||||
newStart,
|
||||
hasMore,
|
||||
};
|
||||
if (text && !!text.trim()) {
|
||||
payload.logArray = text.trim().split('\n');
|
||||
|
|
|
@ -40,16 +40,43 @@ export const buildRunDetailsUrl = (organization, pipeline, branch, runId, tabNam
|
|||
*/
|
||||
export const uriString = (input) => encodeURIComponent(input).replace(/%2F/g, '%252F');
|
||||
|
||||
// general fetchAllTrigger
|
||||
export const fetchAllSuffix = '?start=0';
|
||||
|
||||
// Add fetchAllSuffix in case it is needed
|
||||
export const applyFetchAll = function (config, url) {
|
||||
// if we pass fetchAll means we want the full log -> start=0 will trigger that on the server
|
||||
if (config.fetchAll && !url.includes(fetchAllSuffix)) {
|
||||
return `${url}${fetchAllSuffix}`;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
// using the hook 'location.search'.includes('start=0') to trigger fetchAll
|
||||
export const calculateFetchAll = function (props) {
|
||||
const { location: { search } } = props;
|
||||
|
||||
if (search) {
|
||||
const stepReg = /start=([0-9]{1,})/;
|
||||
const match = stepReg.exec(search);
|
||||
if (match && match[1] && Number(match[1]) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
* helper to calculate log url. When we have a node we get create a special url, otherwise we use the url passed to us
|
||||
* @param config { nodesBaseUrl, node, url}
|
||||
*/
|
||||
export const calculateLogUrl = (config) => {
|
||||
let returnUrl = config.url;
|
||||
if (config.node) {
|
||||
const { nodesBaseUrl, node } = config;
|
||||
return `${nodesBaseUrl}/${node.id}/log/`;
|
||||
returnUrl = `${nodesBaseUrl}/${node.id}/log/`;
|
||||
}
|
||||
return config.url;
|
||||
return applyFetchAll(config, returnUrl);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -105,6 +132,7 @@ export function calculateRunLogURLObject(config) {
|
|||
url = `${baseUrl}/runs/${runId}/log/`;
|
||||
fileName = `${runId}.txt`;
|
||||
}
|
||||
url = applyFetchAll(config, url);
|
||||
return {
|
||||
url,
|
||||
fileName,
|
||||
|
|
|
@ -164,3 +164,29 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logConsole .result-item-children {
|
||||
background-color: @pre-bg;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
code div {
|
||||
font-size: 12px;
|
||||
margin: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
code div a{
|
||||
border: solid 1px @pre-color;
|
||||
padding: 3px 10px;
|
||||
margin: 5px;
|
||||
color: @pre-color;
|
||||
}
|
||||
|
||||
code div a:hover{
|
||||
background-color: @pre-color-hover;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
@blueocean-blue: #4A90E2;
|
||||
@blueocean-blue-darkened: darken(@blueocean-blue, 20%);
|
||||
@gray-base: #000;
|
||||
@pre-bg: lighten(@gray-base, 20%); // #333
|
||||
@pre-color: #f5f5f5;
|
||||
@pre-color-hover: #444!important;
|
||||
|
|
|
@ -62,6 +62,25 @@ describe("TestResults", () => {
|
|||
assert.equal(newFailed, 1);
|
||||
});
|
||||
|
||||
it("Handles REGRESSION case", () => {
|
||||
var failures = {
|
||||
"_class":"hudson.tasks.junit.TestResult",
|
||||
"duration":0.008, "empty":false, "failCount":3, "passCount":0, "skipCount":0, "suites":[
|
||||
{ "duration":0, "id":null, "name":"failure.TestThisWontFail", "stderr":null, "stdout":null, "timestamp":null, "cases": [
|
||||
{"age":5,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest2","skipped":false,"skippedMessage":null,"status":"FAILED","stderr":null,"stdout":null},
|
||||
{"age":2,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest3","skipped":false,"skippedMessage":null,"status":"REGRESSION","stderr":null,"stdout":null},
|
||||
{"age":1,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest4","skipped":false,"skippedMessage":null,"status":"FAILED","stderr":null,"stdout":null},
|
||||
],
|
||||
}]};
|
||||
|
||||
let wrapper = shallow(<TestResults testResults={failures} />);
|
||||
const newFailed = wrapper.find('.new-failure-block h4').text();
|
||||
assert.equal(newFailed, 'New failing - 2');
|
||||
|
||||
const failed = wrapper.find('.existing-failure-block h4').text();
|
||||
assert.equal(failed, 'Existing failures - 1');
|
||||
});
|
||||
|
||||
it("All passing shown", () => {
|
||||
let wrapper = shallow(<TestResults testResults={testResults1} />);
|
||||
let isDone = wrapper.html().indexOf('done_all') > 0;
|
||||
|
@ -78,7 +97,25 @@ describe("TestResults", () => {
|
|||
}]};
|
||||
|
||||
wrapper = shallow(<TestResults testResults={success} />);
|
||||
isDone = wrapper.html().indexOf('done_all') > 0;
|
||||
assert(isDone, "Done all not found, when should be");
|
||||
let html = wrapper.html();
|
||||
assert(html.indexOf('done_all') > 0, "Done all not found, when should be");
|
||||
assert(html.indexOf('fixed-block') < 0, "No fixed tests!");
|
||||
});
|
||||
|
||||
it("All passing and fixed shown", () => {
|
||||
var successWithFixed = {
|
||||
"_class":"hudson.tasks.junit.TestResult",
|
||||
"duration":0.008, "empty":false, "failCount":0, "passCount":3, "skipCount":0, "suites":[
|
||||
{ "duration":0, "id":null, "name":"failure.TestThisWontFail", "stderr":null, "stdout":null, "timestamp":null, "cases": [
|
||||
{"age":0,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest2","skipped":false,"skippedMessage":null,"status":"FIXED","stderr":null,"stdout":null},
|
||||
{"age":0,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest3","skipped":false,"skippedMessage":null,"status":"PASSED","stderr":null,"stdout":null},
|
||||
{"age":0,"className":"failure.TestThisWontFail","duration":0,"errorDetails":null,"errorStackTrace":null,"failedSince":0,"name":"aPassingTest4","skipped":false,"skippedMessage":null,"status":"PASSED","stderr":null,"stdout":null},
|
||||
],
|
||||
}]};
|
||||
|
||||
let wrapper = shallow(<TestResults testResults={successWithFixed} />);
|
||||
let html = wrapper.html();
|
||||
assert(html.indexOf('done_all') > 0, "Done all not found, when should be");
|
||||
assert(html.indexOf('fixed-block') > 0, "Should have fixed tests!");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('PipelineCard', () => {
|
|||
|
||||
assert.equal(wrapper.find('LiveStatusIndicator').length, 1);
|
||||
assert.equal(wrapper.find('.name').length, 1);
|
||||
assert.equal(wrapper.find('.name').text(), 'Jenkins / blueocean');
|
||||
assert.equal(wrapper.find('.name').text(), '<Link />');
|
||||
assert.equal(wrapper.find('.branch').length, 1);
|
||||
assert.equal(wrapper.find('.branchText').text(), 'feature/JENKINS-123');
|
||||
assert.equal(wrapper.find('.commit').length, 1);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"zombie": "^4.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jenkins-cd/design-language": "0.0.64",
|
||||
"@jenkins-cd/design-language": "0.0.65",
|
||||
"@jenkins-cd/js-extensions": "0.0.19",
|
||||
"@jenkins-cd/js-modules": "0.0.5",
|
||||
"history": "2.0.2",
|
||||
|
|
Loading…
Reference in New Issue