Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>5.27</version>
<version>5.26</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -32,8 +32,8 @@
<properties>
<changelist>999999-SNAPSHOT</changelist>
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
<jenkins.baseline>2.504</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.3</jenkins.version>
<jenkins.baseline>2.516</jenkins.baseline>
<jenkins.version>2.533</jenkins.version>
<no-test-jar>false</no-test-jar>
<spotless.check.skip>false</spotless.check.skip>
<ban-junit4-imports.skip>false</ban-junit4-imports.skip>
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/hudson/tasks/junit/CaseResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -1099,9 +1099,9 @@ public boolean isRegression() {
@Restricted(NoExternalUse.class)
public String getIconFileName() {
return switch (getStatus()) {
case PASSED -> "symbol-checkmark-outline plugin-ionicons-api";
case SKIPPED -> "symbol-play-skip-forward-outline plugin-ionicons-api";
default -> "symbol-close-outline plugin-ionicons-api";
case PASSED -> "symbol-status-blue";
case SKIPPED -> "symbol-status-skipped plugin-junit";
default -> "symbol-status-red";
};
}
}
4 changes: 4 additions & 0 deletions src/main/java/hudson/tasks/junit/TestResultAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ public Object readResolve() {
return this;
}

public Widget getWidget() {
return new Widget(getResult());
}

private static final Logger logger = Logger.getLogger(TestResultAction.class.getName());

static final XStream XSTREAM = new XStream2();
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/hudson/tasks/junit/Widget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package hudson.tasks.junit;

import java.util.ArrayList;
import java.util.List;

public class Widget {

private final String symbol;
private final String symbolClass;
private final List<String> lines = new ArrayList<>();

public Widget(TestResult result) {
int failCount = result.getFailCount();
boolean isFailed = failCount > 0;
int totalCount = result.getTotalCount();

this.symbol = isFailed
? "symbol-close-circle-outline plugin-ionicons-api"
: "symbol-checkmark-done-outline plugin-ionicons-api";
this.symbolClass = isFailed ? "jenkins-!-error-color" : "jenkins-!-success-color";

List<String> counts = new ArrayList<>();

if (isFailed) {
lines.add(Messages.Widget_Failed(failCount));
counts.add(Messages.Widget_Passed(result.getPassCount()));

long regressions = result.getSuites().stream()
.flatMap(e -> e.getCases().stream())
.filter(e -> {
var previousResult = e.getPreviousResult();
if (previousResult == null) {
return false;
}
return e.isPassed();
})
.count();

if (regressions > 0) {
lines.add(Messages.Widget_Regression(regressions));
}

} else {
lines.add(Messages.Widget_AllTestsPassing());
}

if (result.getSkipCount() > 0) {
counts.add(Messages.Widget_Skipped(result.getSkipCount()));
}

counts.add(Messages.Widget_Total(totalCount));

lines.add(String.join(", ", counts));

lines.add(Messages.Widget_Took(result.getDurationString()));
}

public String getSymbol() {
return symbol;
}

public String getSymbolClass() {
return symbolClass;
}

public List<String> getLines() {
return lines;
}
}
20 changes: 18 additions & 2 deletions src/main/java/hudson/tasks/test/AbstractTestResultAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.management.Badge;
import jenkins.model.RunAction2;
import jenkins.model.Tab;
import jenkins.model.lazy.LazyBuildMixIn;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
Expand Down Expand Up @@ -81,7 +83,7 @@
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public abstract class AbstractTestResultAction<T extends AbstractTestResultAction>
public abstract class AbstractTestResultAction<T extends AbstractTestResultAction> extends Tab
implements HealthReportingAction, RunAction2 {

private static final Logger LOGGER = Logger.getLogger(AbstractTestResultAction.class.getName());
Expand All @@ -99,14 +101,17 @@ public abstract class AbstractTestResultAction<T extends AbstractTestResultActio
private Map<String, String> descriptions = new ConcurrentHashMap<>();

/** @since 1.545 */
protected AbstractTestResultAction() {}
protected AbstractTestResultAction() {
super(null);
}

/**
* @deprecated Use the default constructor and just call {@link Run#addAction} to associate the build with the action.
* @since 1.2-beta-1
*/
@Deprecated
protected AbstractTestResultAction(Run owner) {
super(owner);
onAttached(owner);
}

Expand All @@ -117,12 +122,14 @@ protected AbstractTestResultAction(AbstractBuild owner) {

@Override
public void onAttached(Run<?, ?> r) {
this.object = r;
this.run = r;
this.owner = r instanceof AbstractBuild ? (AbstractBuild<?, ?>) r : null;
}

@Override
public void onLoad(Run<?, ?> r) {
this.object = r;
this.run = r;
this.owner = r instanceof AbstractBuild ? (AbstractBuild<?, ?>) r : null;
}
Expand Down Expand Up @@ -180,6 +187,15 @@ public String getIconFileName() {
return "symbol-flask-outline plugin-ionicons-api";
}

@Override
public Badge getBadge() {
if (getFailCount() == 0) {
return null;
}

return new Badge(String.valueOf(getFailCount()), getFailCount() + " test failure", Badge.Severity.DANGER);
}

@Override
public HealthReport getBuildHealth() {
final double scaleFactor = getHealthScaleFactor();
Expand Down
156 changes: 80 additions & 76 deletions src/main/resources/hudson/tasks/junit/CaseResult/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -23,89 +23,93 @@ THE SOFTWARE.
-->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:test="/lib/hudson/test">
<l:layout title="${it.displayName} - ${it.run}" type="one-column" xmlns:p="/prism">
<l:main-panel>
<p:prism configuration="${it.prismConfiguration}" />
<link rel="stylesheet" href="${resURL}/plugin/junit/styles.css" defer="true" />

<j:set var="st" value="${it.status}" />

<div class="jenkins-app-bar">
<div class="jenkins-app-bar__content">
<h1>
<span class="jenkins-!-margin-right-2">
${it.displayName}
</span>
<st:nbsp />
<span class="jenkins-!-text-color-secondary" style="font-family: var(--font-family-mono)">
${it.className}
</span>
</h1>
</div>
<div class="jenkins-app-bar__controls">
<a href="history" class="jenkins-button">
<l:icon src="symbol-bar-chart-outline plugin-ionicons-api" />
${%History}
</a>
</div>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:test="/lib/hudson/test" xmlns:p="/prism">
<j:set var="test" value="${it}" />
<j:set var="it" value="${it.parentAction}" />

<l:run-subpage>
<j:set var="it" value="${test}" />
<p:prism configuration="${it.prismConfiguration}" />
<link rel="stylesheet" href="${resURL}/plugin/junit/styles.css" defer="true" />

<j:set var="st" value="${it.status}" />

<div class="jenkins-app-bar jenkins-app-bar--with-icon">
<div class="jenkins-app-bar__content">
<l:icon src="${it.iconFileName}" />
<h1>
<span class="jenkins-!-margin-right-2">
${it.displayName}
</span>
<st:nbsp />
<span class="jenkins-!-text-color-secondary" style="font-family: var(--font-family-mono)">
${it.className}
</span>
</h1>
</div>
<div class="jp-details">
<div class="${it.status.cssClass}">
<l:icon src="${it.iconFileName}" />
${it.status.message}
</div>
<div class="jenkins-app-bar__controls">
<div class="jenkins-details">
<test:condition-tag test="${it}" />

<j:if test="${!it.passed}">
<a href="${rootURL}/${it.run.url}" class="jenkins-details__item">
<div class="jenkins-details__item__icon">
<j:choose>
<j:when test="${it.skipped}">
<l:icon src="symbol-play-skip-forward-circle-outline plugin-ionicons-api" />
</j:when>
<j:otherwise>
<l:icon src="symbol-close-circle-outline plugin-ionicons-api" />
</j:otherwise>
</j:choose>
</div>

<j:choose>
<j:when test="${it.skipped}">
${%skippedFor(it.age)}
</j:when>
<j:otherwise>
${%failingFor(it.age)}
</j:otherwise>
</j:choose>
${%since.before}
#${it.failedSince}
${%since.after}
</a>
</j:if>

<test:condition-tag test="${it}" />

<j:if test="${!it.passed}">
<a href="${rootURL}/${it.run.url}" class="jp-pill jp-pill--tertiary">
<j:choose>
<j:when test="${it.skipped}">
<l:icon src="symbol-play-skip-forward-circle-outline plugin-ionicons-api" />
</j:when>
<j:otherwise>
<l:icon src="symbol-close-circle-outline plugin-ionicons-api" />
</j:otherwise>
</j:choose>

<j:choose>
<j:when test="${it.skipped}">
${%skippedFor(it.age)}
</j:when>
<j:otherwise>
${%failingFor(it.age)}
</j:otherwise>
</j:choose>
${%since.before}
#${it.failedSince}
${%since.after}
</a>
</j:if>
<div class="jenkins-details__item">
<div class="jenkins-details__item__icon">
<l:icon src="symbol-timer-outline plugin-ionicons-api" />
</div>
${%took(it.durationString)}

<div class="jp-pill jp-pill--tertiary">
<l:icon src="symbol-timer-outline plugin-ionicons-api" />
${%took(it.durationString)}
<j:if test="${it.suiteResult != null &amp;&amp; it.className != it.suiteResult.name}">
(from <st:out value="${it.suiteResult.name}"/>)
</j:if>
</div>

<j:if test="${it.suiteResult != null &amp;&amp; it.className != it.suiteResult.name}">
(from <st:out value="${it.suiteResult.name}"/>)
</j:if>
</div>
<a tooltip="${%History}"
href="history"
class="jenkins-button jenkins-details__button">
<l:icon src="symbol-bar-chart-outline plugin-ionicons-api" />
</a>

<test:edit-description-button permission="${it.run.UPDATE}" />
<t:editDescriptionButton permission="${it.run.UPDATE}" compact="true" />
</div>
</div>
</div>

<t:editableDescription permission="${it.run.UPDATE}" hideButton="true" />
<t:editableDescription permission="${it.run.UPDATE}" hideButton="true" />

<table style="margin-top: 1em; margin-left:0em;">
<j:forEach var="action" items="${it.testActions}">
<st:include page="summary.jelly" from="${action}" optional="true" it="${action}" />
</j:forEach>
</table>
<table>
<j:forEach var="action" items="${it.testActions}">
<st:include page="summary.jelly" from="${action}" optional="true" it="${action}" />
</j:forEach>
</table>

<div class="jp-code-list">
<st:include page="test-output.jelly" />
</div>
</l:main-panel>
</l:layout>
<div class="jp-code-list">
<st:include page="test-output.jelly" />
</div>
</l:run-subpage>
</j:jelly>
Loading