Bug 1419612 - convert failure summary panel to react components (#2999)

This commit is contained in:
Sarah Clements 2017-12-07 17:33:54 -08:00 коммит произвёл Cameron Dawson
Родитель 2c3b9b7370
Коммит 22e45576f6
3 изменённых файлов: 231 добавлений и 125 удалений

Просмотреть файл

@ -87,6 +87,7 @@ require('./js/controllers/tcjobactions.js');
require('./plugins/tabs.js');
require('./plugins/controller.js');
require('./plugins/job_details_pane.jsx');
require('./plugins/failure_summary_panel.jsx');
require('./plugins/pinboard.js');
require('./plugins/annotations/controller.js');
require('./plugins/failure_summary/controller.js');

Просмотреть файл

@ -1,128 +1,11 @@
<div ng-controller="BugsPluginCtrl">
<ul class="list-unstyled failure-summary-list">
<li ng-repeat="suggestion in suggestions">
<div class="job-tabs-content">
<a class="btn btn-xs btn-light-bordered"
prevent-default-on-left-click
ng-show="(filerInAddress || user.is_staff)"
ng-click="fileBug($index)"
title="file a bug for this failure">
<i class="fa fa-bug"></i>
</a>
<span>{{::suggestion.search}}</span>
</div>
<!--Open recent bugs-->
<ul ng-if="suggestion.valid_open_recent"
class="list-unstyled failure-summary-bugs">
<li ng-repeat="bug in suggestion.bugs.open_recent">
<button class="btn btn-xs btn-light-bordered"
ng-click="pinboard_service.addBug(bug, selectedJob)"
title="add to list of bugs to associate with all pinned jobs">
<i class="fa fa-thumb-tack"></i>
</button>
<a href="{{:: getBugUrl(bug.id) }}"
target="_blank">{{::bug.id}}
<span ng-bind-html="bug.summary | escapeHTML | highlightCommonTerms:suggestion.search">
{{::bug.summary}}
</span>
</a>
</li>
</ul>
<!--All other bugs-->
<a ng-if="suggestion.valid_all_others && suggestion.valid_open_recent"
href=""
ng-click="suggestion.clicked_show_more = (suggestion.clicked_show_more === true ? false : true)"
class="show-hide-more">Show / Hide more</a>
<ul ng-if="suggestion.valid_all_others && (suggestion.clicked_show_more || !suggestion.valid_open_recent)"
class="list-unstyled failure-summary-bugs">
<li ng-repeat="bug in suggestion.bugs.all_others">
<button class="btn btn-xs btn-light-bordered"
ng-click="pinboard_service.addBug(bug, selectedJob)"
title="add to list of bugs to associate with all pinned jobs">
<i class="fa fa-thumb-tack"></i>
</button>
<a ng-if="bug.resolution === ''"
href="{{:: getBugUrl(bug.id) }}"
target="_blank">{{::bug.id}}
<span ng-bind-html="bug.summary | escapeHTML | highlightCommonTerms:suggestion.search">
{{::bug.summary}}
</span>
</a>
<a ng-if="bug.resolution !== ''"
href="{{:: getBugUrl(bug.id) }}"
title="{{::bug.resolution}}"
target="_blank"
class="deleted">{{::bug.id}}
<span ng-bind-html="bug.summary | escapeHTML | highlightCommonTerms:suggestion.search"
class="deleted">{{::bug.summary}}
</span>
</a>
</li>
</ul>
<mark ng-if="suggestion.bugs.too_many_open_recent || (suggestion.bugs.too_many_all_others && !suggestion.valid_open_recent)"
>Exceeded max ({{bug_limit}}) bug suggestions, most of which are likely false positives.</mark>
</li>
<li>
<span ng-if="errors.length > 0">
<div>
<span>No Bug Suggestions Available.</span>
</div>
<span><b>Unsuccessful Execution Steps</b></span>
<li ng-repeat="error in errors">
<ul>
<li>{{error.name}} : {{error.result}}.
<a title="Open in Log Viewer"
target="_blank"
href="{{error.lvURL}}">View log</a>
</li>
</ul>
</li>
</span>
</li>
<li ng-if="!tabs.failureSummary.is_loading && jobLogsAllParsed && bugSuggestionsLoaded && job_log_urls.length && suggestions.length == 0 && errors.length == 0">
<div class="failure-summary-line-empty">
<span>Failure summary is empty</span>
</div>
</li>
<li ng-if="!tabs.failureSummary.is_loading && jobLogsAllParsed && !bugSuggestionsLoaded && job_log_urls.length && logParseStatus === 'success'">
<div class="failure-summary-line-empty">
<span>Log parsing complete. Generating bug suggestions</span>
<span>The content of this panel will refresh in 5 seconds.</span>
</div>
</li>
<li ng-if="!tabs.failureSummary.is_loading && !jobLogsAllParsed"
ng-repeat="job_log_url in job_log_urls">
<div class="failure-summary-line-empty">
<span>Log parsing in progress. The</span>
<a title="Open the raw log in a new window"
target="_blank"
href="{{::job_log_url.url}}">raw log</a>
<span>is available. This panel will automatically recheck every 5 seconds.</span>
</div>
</li>
<li ng-if="!tabs.failureSummary.is_loading && logParseStatus === 'failed'">
<div class="failure-summary-line-empty">
<span>Log parsing failed. Unable to generate failure summary</span>
</div>
</li>
<li ng-if="!tabs.failureSummary.is_loading && !job_log_urls.length">
<div class="failure-summary-line-empty">
<span>No logs available for this job</span>
</div>
</li>
</ul>
<div ng-if="tabs.failureSummary.is_loading" class="overlay">
<div>
<span class="fa fa-spinner fa-pulse th-spinner-lg"></span>
</div>
<div class="w-100 h-100">
<failure-summary-panel
tabs="tabs" suggestions="suggestions" filerInAddress="filerInAddress"
fileBug="fileBug" user="user" getBugUrl="getBugUrl" bugLimit="bug_limit"
pinboardService="pinboard_service" selectedJob="selectedJob" errors="errors"
bugSuggestionsLoaded="bugSuggestionsLoaded" jobLogsAllParsed="jobLogsAllParsed"
jobLogUrls="job_log_urls" logParseStatus="logParseStatus">
</failure-summary-panel>
</div>
</div>

Просмотреть файл

@ -0,0 +1,222 @@
'use strict';
const PropTypes = require('prop-types');
class SuggestionsListItem extends React.Component {
constructor(props) {
super(props);
this.state = {
suggestionShowMore: false
};
this.fileBugEvent = this.fileBugEvent.bind(this);
}
fileBugEvent(event) {
event.preventDefault();
this.props.fileBug(this.props.index);
}
clickShowMore(event) {
event.preventDefault();
this.setState({ suggestionShowMore: !this.state.suggestionShowMore });
}
render() {
//If this method is bound in the constructor it gives me a warning about only setting state in a mounted component
// but if we move to allowing the arrow function in classes at some point, this problem should be solved.
this.clickShowMore = this.clickShowMore.bind(this);
return (
<li>
<div className="job-tabs-content">
{(this.props.filerInAddress || this.props.user.is_staff) &&
<a className="btn btn-xs btn-light-bordered"
onClick={this.fileBugEvent}
title="file a bug for this failure">
<i className="fa fa-bug"></i>
</a>}
<span>{this.props.suggestion.search}</span>
</div>
{/* <!--Open recent bugs--> */}
{this.props.suggestion.valid_open_recent &&
<ul className="list-unstyled failure-summary-bugs">
{this.props.suggestion.bugs.open_recent.map((bug, index) =>
<BugListItem
key={index} bug={bug} selectedJob={this.props.selectedJob}
getBugUrl={this.props.getBugUrl} pinboardService={this.props.pinboardService}
escapeHTMLFilter={this.props.escapeHTMLFilter} suggestion={this.props.suggestion}
highlightCommonTermsFilter={this.props.highlightCommonTermsFilter}/>)}
</ul>}
{/* <!--All other bugs--> */}
{this.props.suggestion.valid_all_others && this.props.suggestion.valid_open_recent &&
<a target="_blank"
href=""
onClick={this.clickShowMore}
className="show-hide-more">Show / Hide more</a>}
{this.props.suggestion.valid_all_others && (this.state.suggestionShowMore
|| !this.props.suggestion.valid_open_recent) &&
<ul className="list-unstyled failure-summary-bugs">
{this.props.suggestion.bugs.all_others.map((bug, index) =>
<BugListItem
key={index} bug={bug} selectedJob={this.props.selectedJob}
getBugUrl={this.props.getBugUrl} pinboardService={this.props.pinboardService}
escapeHTMLFilter={this.props.escapeHTMLFilter} suggestion={this.props.suggestion}
highlightCommonTermsFilter={this.props.highlightCommonTermsFilter}
bugClassName={bug.resolution !== "" ? "deleted" : ""}
title={bug.resolution !== "" ? bug.resolution : ""} />)}
</ul>}
{(this.props.suggestion.bugs.too_many_open_recent || (this.props.suggestion.bugs.too_many_all_others
&& !this.props.suggestion.valid_open_recent)) &&
<mark>Exceeded max {this.props.bugLimit} bug suggestions, most of which are likely false positives.</mark>}
</li>
);
}
}
const ListItem = props => (
<li>
<p className="failure-summary-line-empty mb-0">{props.text}</p>
</li>
);
const BugListItem = (props) => {
const pinboardServiceEvent = () => {
props.pinboardService.addBug(props.bug, props.selectedJob);
};
const getBugUrl = props.getBugUrl(props.bug.id);
const bugSummaryText = props.escapeHTMLFilter(props.bug.summary);
const bugSummaryHTML = { __html: props.highlightCommonTermsFilter(bugSummaryText, props.suggestion.search) };
return (
<li>
<button className="btn btn-xs btn-light-bordered"
onClick={pinboardServiceEvent}
title="add to list of bugs to associate with all pinned jobs">
<i className="fa fa-thumb-tack"></i>
</button>
<a className={`${props.bugClassName} ml-1`}
href={getBugUrl}
target="_blank"
title={props.title}>{props.bug.id}
<span className={`${props.bugClassName} ml-1`} dangerouslySetInnerHTML={bugSummaryHTML}></span>
</a>
</li>
);
};
const ErrorsList = (props) => {
const errorListItem = props.errors.map((error, index) =>
<li key={index}>{error.name} : {error.result}.
<a title="Open in Log Viewer"
target="_blank"
href={error.lvURL}><span className="ml-1">View log</span></a>
</li>);
return (
<li>
No Bug Suggestions Available.<br />
<span className="font-weight-bold">Unsuccessful Execution Steps</span>
<ul>{errorListItem}</ul>
</li>
);
};
class FailureSummaryPanel extends React.Component {
constructor(props) {
super(props);
}
render() {
const escapeHTMLFilter = this.props.$injector.get('$filter')('escapeHTML');
const highlightCommonTermsFilter = this.props.$injector.get('$filter')('highlightCommonTerms');
return (
<ul className="list-unstyled failure-summary-list">
{this.props.suggestions && this.props.suggestions.map((suggestion, index) =>
<SuggestionsListItem
key={index} index={index} suggestion={suggestion} user={this.props.user}
filerInAddress={this.props.filerInAddress} fileBug={this.props.fileBug}
highlightCommonTermsFilter={highlightCommonTermsFilter}
escapeHTMLFilter={escapeHTMLFilter} getBugUrl={this.props.getBugUrl}
bugLimit={this.props.bugLimit} pinboardService={this.props.pinboardService}
selectedJob={this.props.selectedJob} />)}
{this.props.errors && this.props.errors.length > 0 &&
<ErrorsList errors={this.props.errors} />}
{!this.props.tabs.failureSummary.is_loading && this.props.jobLogsAllParsed && this.props.bugSuggestionsLoaded &&
this.props.jobLogUrls.length === 0 && this.props.suggestions.length === 0 && this.props.errors.length === 0 &&
<ListItem text="Failure summary is empty" />}
{!this.props.tabs.failureSummary.is_loading && this.props.jobLogsAllParsed && !this.props.bugSuggestionsLoaded
&& this.props.jobLogUrls.length && this.props.logParseStatus === 'success' &&
<li>
<p className="failure-summary-line-empty mb-0">Log parsing complete. Generating bug suggestions.<br />
<span>The content of this panel will refresh in 5 seconds.</span></p>
</li>}
{this.props.jobLogUrls && !this.props.tabs.failureSummary.is_loading && !this.props.jobLogsAllParsed &&
this.props.jobLogUrls.map((job, index) =>
<li key={index}>
<p className="failure-summary-line-empty mb-0">Log parsing in progress.<br />
<a title="Open the raw log in a new window"
target="_blank"
href={job.url}>The raw log</a>
<span>is available. This panel will automatically recheck every 5 seconds.</span></p>
</li>)}
{!this.props.tabs.failureSummary.is_loading && this.props.logParseStatus === 'failed' &&
<ListItem text="Log parsing failed. Unable to generate failure summary." />}
{!this.props.tabs.failureSummary.is_loading && this.props.jobLogUrls && this.props.jobLogUrls.length === 0 &&
<ListItem text="No logs available for this job." />}
{this.props.tabs.failureSummary.is_loading &&
<div className="overlay">
<div>
<span className="fa fa-spinner fa-pulse th-spinner-lg"></span>
</div>
</div>}
</ul>
);
}
}
FailureSummaryPanel.propTypes = {
tabs: PropTypes.object,
suggestions: PropTypes.array,
filerInAddress: PropTypes.bool,
fileBug: PropTypes.func,
user: PropTypes.object,
getBugUrl: PropTypes.func,
pinboardService: PropTypes.object,
selectedJob: PropTypes.object,
$injector: PropTypes.object,
bugLimit: PropTypes.number,
errors: PropTypes.array,
bugSuggestionsLoaded: PropTypes.bool,
jobLogsAllParsed: PropTypes.bool,
jobLogUrls: PropTypes.array,
logParseStatus: PropTypes.string
};
module.exports = {
FailureSummaryPanel,
SuggestionsListItem,
BugListItem,
ErrorsList,
ListItem
};
treeherder.directive('failureSummaryPanel', ['reactDirective', '$injector', (reactDirective, $injector) =>
reactDirective(FailureSummaryPanel, undefined, {}, { $injector })]);