diff --git a/src/main/java/com/jenkinsci/plugins/badge/action/AbstractBadgeAction.java b/src/main/java/com/jenkinsci/plugins/badge/action/AbstractBadgeAction.java index 43e4536..5fd9959 100644 --- a/src/main/java/com/jenkinsci/plugins/badge/action/AbstractBadgeAction.java +++ b/src/main/java/com/jenkinsci/plugins/badge/action/AbstractBadgeAction.java @@ -87,7 +87,7 @@ public void setIcon(String icon) { @Whitelisted public String getIcon() { - if (StringUtils.isEmpty(icon) + if (StringUtils.isBlank(icon) || icon.startsWith("/") || icon.startsWith("symbol-") || icon.startsWith("icon-") @@ -122,7 +122,7 @@ public void setText(String text) { @Whitelisted public String getText() { - if (StringUtils.isEmpty(text)) { + if (StringUtils.isBlank(text)) { return text; } @@ -161,14 +161,14 @@ public void setLink(String link) { @Whitelisted public String getLink() { - if (StringUtils.isEmpty(link) + if (StringUtils.isBlank(link) || link.startsWith("/") || link.matches("^https?://.*") || link.matches("^mailto:.*")) { return link; } - LOGGER.log(Level.WARNING, "Invalid link value: '{}' - ignoring it", link); + LOGGER.log(Level.WARNING, () -> "Invalid link value: '" + link + "' - ignoring it"); return null; } diff --git a/src/main/java/com/jenkinsci/plugins/badge/action/BadgeSummaryAction.java b/src/main/java/com/jenkinsci/plugins/badge/action/BadgeSummaryAction.java index 217a5a3..ac9adc0 100644 --- a/src/main/java/com/jenkinsci/plugins/badge/action/BadgeSummaryAction.java +++ b/src/main/java/com/jenkinsci/plugins/badge/action/BadgeSummaryAction.java @@ -24,6 +24,9 @@ package com.jenkinsci.plugins.badge.action; import java.io.Serial; +import java.util.logging.Level; +import java.util.logging.Logger; +import jenkins.model.Jenkins; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted; @@ -36,10 +39,25 @@ public class BadgeSummaryAction extends AbstractBadgeAction { @Serial private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(BadgeSummaryAction.class.getName()); + public BadgeSummaryAction(String id, String icon, String text, String cssClass, String style, String link) { super(id, icon, text, cssClass, style, link); } + @Whitelisted + @Override + public String getIcon() { + String icon = super.getIcon(); + + if (StringUtils.isBlank(icon)) { + LOGGER.log(Level.WARNING, () -> "Invalid icon value: '" + icon + "' - using empty icon instead"); + return Jenkins.RESOURCE_PATH + "/images/16x16/empty.png"; + } + + return icon; + } + @Override public String getDisplayName() { return "Badge Summary Action"; diff --git a/src/main/resources/com/jenkinsci/plugins/badge/action/BadgeSummaryAction/summary.jelly b/src/main/resources/com/jenkinsci/plugins/badge/action/BadgeSummaryAction/summary.jelly index dd6359c..53076cb 100644 --- a/src/main/resources/com/jenkinsci/plugins/badge/action/BadgeSummaryAction/summary.jelly +++ b/src/main/resources/com/jenkinsci/plugins/badge/action/BadgeSummaryAction/summary.jelly @@ -25,11 +25,9 @@ THE SOFTWARE. - - - - ${it.text} - - - + + + ${it.text} + + diff --git a/src/test/java/com/jenkinsci/plugins/badge/action/ActionClassHierarchyTest.java b/src/test/java/com/jenkinsci/plugins/badge/action/ActionClassHierarchyTest.java index 802a68c..e43dca3 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/action/ActionClassHierarchyTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/action/ActionClassHierarchyTest.java @@ -28,27 +28,36 @@ import hudson.model.Action; import hudson.model.BuildBadgeAction; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class ActionClassHierarchyTest { - @Test - void abstractBadgeAction() { - assertTrue(Action.class.isAssignableFrom(AbstractBadgeAction.class)); - assertFalse(BuildBadgeAction.class.isAssignableFrom(AbstractBadgeAction.class)); - } + @Nested + class Badge { + + @Test + void abstractBadgeAction() { + assertTrue(Action.class.isAssignableFrom(AbstractBadgeAction.class)); + assertFalse(BuildBadgeAction.class.isAssignableFrom(AbstractBadgeAction.class)); + } - @Test - void badgeAction() { - assertTrue(Action.class.isAssignableFrom(BadgeAction.class)); - assertTrue(BuildBadgeAction.class.isAssignableFrom(BadgeAction.class)); - assertTrue(AbstractBadgeAction.class.isAssignableFrom(BadgeAction.class)); + @Test + void badgeAction() { + assertTrue(Action.class.isAssignableFrom(BadgeAction.class)); + assertTrue(BuildBadgeAction.class.isAssignableFrom(BadgeAction.class)); + assertTrue(AbstractBadgeAction.class.isAssignableFrom(BadgeAction.class)); + } } - @Test - void badgeSummaryAction() { - assertTrue(Action.class.isAssignableFrom(BadgeSummaryAction.class)); - assertFalse(BuildBadgeAction.class.isAssignableFrom(BadgeSummaryAction.class)); - assertTrue(AbstractBadgeAction.class.isAssignableFrom(BadgeSummaryAction.class)); + @Nested + class Summary { + + @Test + void badgeSummaryAction() { + assertTrue(Action.class.isAssignableFrom(BadgeSummaryAction.class)); + assertFalse(BuildBadgeAction.class.isAssignableFrom(BadgeSummaryAction.class)); + assertTrue(AbstractBadgeAction.class.isAssignableFrom(BadgeSummaryAction.class)); + } } } diff --git a/src/test/java/com/jenkinsci/plugins/badge/action/BadgeSummaryActionTest.java b/src/test/java/com/jenkinsci/plugins/badge/action/BadgeSummaryActionTest.java index da39f94..e85a7eb 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/action/BadgeSummaryActionTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/action/BadgeSummaryActionTest.java @@ -23,8 +23,76 @@ */ package com.jenkinsci.plugins.badge.action; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.jenkins.plugins.emoji.symbols.Emojis; +import io.jenkins.plugins.ionicons.Ionicons; +import jenkins.model.Jenkins; +import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; + class BadgeSummaryActionTest extends AbstractBadgeActionTest { + @Override + @Test + void icon(@SuppressWarnings("unused") JenkinsRule r) { + AbstractBadgeAction action = createAction("id", null, "text", "cssClass", "style", "link"); + assertEquals(Jenkins.RESOURCE_PATH + "/images/16x16/empty.png", action.getIcon()); + + action.setIcon(""); + assertEquals(Jenkins.RESOURCE_PATH + "/images/16x16/empty.png", action.getIcon()); + + action.setIcon("icon.png"); + assertEquals(Jenkins.RESOURCE_PATH + "/images/16x16/icon.png", action.getIcon()); + + action.setIcon("/relative/url/icon.png"); + assertEquals("/relative/url/icon.png", action.getIcon()); + + action.setIcon("symbol-rocket plugin-ionicons-api"); + assertEquals("symbol-rocket plugin-ionicons-api", action.getIcon()); + + action.setIcon("symbol-cube"); + assertEquals("symbol-cube", action.getIcon()); + + action.setIcon("icon-gear"); + assertEquals("icon-gear", action.getIcon()); + + action.setIcon("https://host.domain/icon.png"); + assertEquals("https://host.domain/icon.png", action.getIcon()); + + action.setIcon("completed.gif"); + assertEquals("symbol-status-blue", action.getIcon()); + action.setIcon("db_in.gif"); + assertEquals(Ionicons.getIconClassName("cloud-upload-outline"), action.getIcon()); + action.setIcon("db_out.gif"); + assertEquals(Ionicons.getIconClassName("cloud-download-outline"), action.getIcon()); + action.setIcon("delete.gif"); + assertEquals("symbol-trash", action.getIcon()); + action.setIcon("error.gif"); + assertEquals("symbol-status-red", action.getIcon()); + action.setIcon("folder.gif"); + assertEquals("symbol-folder", action.getIcon()); + action.setIcon("green.gif"); + assertEquals(Emojis.getIconClassName("green_square"), action.getIcon()); + action.setIcon("info.gif"); + assertEquals("symbol-information-circle", action.getIcon()); + action.setIcon("red.gif"); + assertEquals(Emojis.getIconClassName("red_square"), action.getIcon()); + action.setIcon("save.gif"); + assertEquals(Ionicons.getIconClassName("save-outline"), action.getIcon()); + action.setIcon("success.gif"); + assertEquals("symbol-status-blue", action.getIcon()); + action.setIcon("text.gif"); + assertEquals("symbol-document-text", action.getIcon()); + action.setIcon("warning.gif"); + assertEquals("symbol-status-yellow", action.getIcon()); + action.setIcon("yellow.gif"); + assertEquals(Emojis.getIconClassName("yellow_square"), action.getIcon()); + + action.setIcon("blue.gif"); + assertEquals(Jenkins.RESOURCE_PATH + "/images/16x16/blue.gif", action.getIcon()); + } + @Override protected AbstractBadgeAction createAction( String id, String icon, String text, String cssClass, String style, String link) { diff --git a/src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgeStepTest.java b/src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgesStepTest.java similarity index 98% rename from src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgeStepTest.java rename to src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgesStepTest.java index 47b9d74..66feaf7 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgeStepTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/dsl/AbstractRemoveBadgesStepTest.java @@ -32,7 +32,7 @@ import org.jvnet.hudson.test.junit.jupiter.WithJenkins; @WithJenkins -abstract class AbstractRemoveBadgeStepTest { +abstract class AbstractRemoveBadgesStepTest { @Test abstract void defaultConstructor(@SuppressWarnings("unused") JenkinsRule r); diff --git a/src/test/java/com/jenkinsci/plugins/badge/dsl/AddSummaryStepTest.java b/src/test/java/com/jenkinsci/plugins/badge/dsl/AddSummaryStepTest.java index bd882e0..47e470b 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/dsl/AddSummaryStepTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/dsl/AddSummaryStepTest.java @@ -28,6 +28,8 @@ import com.jenkinsci.plugins.badge.action.BadgeSummaryAction; import java.util.List; +import jenkins.model.Jenkins; +import org.apache.commons.lang.StringUtils; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.JenkinsRule; @@ -53,7 +55,11 @@ protected void assertFields(AbstractAddBadgeStep step, WorkflowRun run) { BadgeSummaryAction action = summaryActions.get(0); assertEquals(step.getId(), action.getId()); - assertEquals(step.getIcon(), action.getIcon()); + if (StringUtils.isEmpty(step.getIcon())) { + assertEquals(Jenkins.RESOURCE_PATH + "/images/16x16/empty.png", action.getIcon()); + } else { + assertEquals(step.getIcon(), action.getIcon()); + } assertEquals(step.getText(), action.getText()); assertEquals(step.getCssClass(), action.getCssClass()); assertEquals(step.getStyle(), action.getStyle()); diff --git a/src/test/java/com/jenkinsci/plugins/badge/dsl/RemoveBadgesStepTest.java b/src/test/java/com/jenkinsci/plugins/badge/dsl/RemoveBadgesStepTest.java index 13dcb1a..6019609 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/dsl/RemoveBadgesStepTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/dsl/RemoveBadgesStepTest.java @@ -37,7 +37,7 @@ import org.jvnet.hudson.test.junit.jupiter.WithJenkins; @WithJenkins -class RemoveBadgesStepTest extends AbstractRemoveBadgeStepTest { +class RemoveBadgesStepTest extends AbstractRemoveBadgesStepTest { @Override @Test diff --git a/src/test/java/com/jenkinsci/plugins/badge/dsl/StepClassHierarchyTest.java b/src/test/java/com/jenkinsci/plugins/badge/dsl/StepClassHierarchyTest.java index 24b8439..fd4b0d4 100644 --- a/src/test/java/com/jenkinsci/plugins/badge/dsl/StepClassHierarchyTest.java +++ b/src/test/java/com/jenkinsci/plugins/badge/dsl/StepClassHierarchyTest.java @@ -26,63 +26,72 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.jenkinsci.plugins.workflow.steps.Step; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class StepClassHierarchyTest { - @Test - void abstractAddBadgeStep() { - assertTrue(Step.class.isAssignableFrom(AbstractAddBadgeStep.class)); - } + @Nested + class Badge { - @Test - void abstractRemoveBadgesStep() { - assertTrue(Step.class.isAssignableFrom(AbstractRemoveBadgesStep.class)); - } + @Test + void abstractAddBadgeStep() { + assertTrue(Step.class.isAssignableFrom(AbstractAddBadgeStep.class)); + } - @Test - void addBadgeStep() { - assertTrue(Step.class.isAssignableFrom(AddBadgeStep.class)); - assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddBadgeStep.class)); - } + @Test + void abstractRemoveBadgesStep() { + assertTrue(Step.class.isAssignableFrom(AbstractRemoveBadgesStep.class)); + } - @Test - void addErrorBadgeStep() { - assertTrue(Step.class.isAssignableFrom(AddErrorBadgeStep.class)); - assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddErrorBadgeStep.class)); - assertTrue(AddBadgeStep.class.isAssignableFrom(AddErrorBadgeStep.class)); - } + @Test + void addBadgeStep() { + assertTrue(Step.class.isAssignableFrom(AddBadgeStep.class)); + assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddBadgeStep.class)); + } - @Test - void addInfoBadgeStep() { - assertTrue(Step.class.isAssignableFrom(AddInfoBadgeStep.class)); - assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddInfoBadgeStep.class)); - assertTrue(AddBadgeStep.class.isAssignableFrom(AddInfoBadgeStep.class)); - } + @Test + void addErrorBadgeStep() { + assertTrue(Step.class.isAssignableFrom(AddErrorBadgeStep.class)); + assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddErrorBadgeStep.class)); + assertTrue(AddBadgeStep.class.isAssignableFrom(AddErrorBadgeStep.class)); + } - @Test - void addSummaryStep() { - assertTrue(Step.class.isAssignableFrom(AddSummaryStep.class)); - assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddSummaryStep.class)); - assertTrue(AddBadgeStep.class.isAssignableFrom(AddSummaryStep.class)); - } + @Test + void addInfoBadgeStep() { + assertTrue(Step.class.isAssignableFrom(AddInfoBadgeStep.class)); + assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddInfoBadgeStep.class)); + assertTrue(AddBadgeStep.class.isAssignableFrom(AddInfoBadgeStep.class)); + } - @Test - void addWarningBadgeStep() { - assertTrue(Step.class.isAssignableFrom(AddWarningBadgeStep.class)); - assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddWarningBadgeStep.class)); - assertTrue(AddBadgeStep.class.isAssignableFrom(AddWarningBadgeStep.class)); - } + @Test + void addWarningBadgeStep() { + assertTrue(Step.class.isAssignableFrom(AddWarningBadgeStep.class)); + assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddWarningBadgeStep.class)); + assertTrue(AddBadgeStep.class.isAssignableFrom(AddWarningBadgeStep.class)); + } - @Test - void removeBadgesStep() { - assertTrue(Step.class.isAssignableFrom(RemoveBadgesStep.class)); - assertTrue(AbstractRemoveBadgesStep.class.isAssignableFrom(RemoveBadgesStep.class)); + @Test + void removeBadgesStep() { + assertTrue(Step.class.isAssignableFrom(RemoveBadgesStep.class)); + assertTrue(AbstractRemoveBadgesStep.class.isAssignableFrom(RemoveBadgesStep.class)); + } } - @Test - void removeSummariesStep() { - assertTrue(Step.class.isAssignableFrom(RemoveSummariesStep.class)); - assertTrue(AbstractRemoveBadgesStep.class.isAssignableFrom(RemoveSummariesStep.class)); + @Nested + class Summary { + + @Test + void addSummaryStep() { + assertTrue(Step.class.isAssignableFrom(AddSummaryStep.class)); + assertTrue(AbstractAddBadgeStep.class.isAssignableFrom(AddSummaryStep.class)); + assertTrue(AddBadgeStep.class.isAssignableFrom(AddSummaryStep.class)); + } + + @Test + void removeSummariesStep() { + assertTrue(Step.class.isAssignableFrom(RemoveSummariesStep.class)); + assertTrue(AbstractRemoveBadgesStep.class.isAssignableFrom(RemoveSummariesStep.class)); + } } } diff --git a/src/test/java/com/jenkinsci/plugins/badge/dsl/UITest.java b/src/test/java/com/jenkinsci/plugins/badge/dsl/UITest.java new file mode 100644 index 0000000..242823b --- /dev/null +++ b/src/test/java/com/jenkinsci/plugins/badge/dsl/UITest.java @@ -0,0 +1,394 @@ +package com.jenkinsci.plugins.badge.dsl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.UUID; +import jenkins.model.Jenkins; +import org.apache.commons.lang.StringUtils; +import org.htmlunit.html.DomElement; +import org.htmlunit.html.HtmlPage; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; + +@WithJenkins +class UITest { + + @Nested + class Badge { + + @Test + void iconWithLink(JenkinsRule r) throws Throwable { + AddBadgeStep step = new AddBadgeStep( + null, + "symbol-rocket plugin-ionicons-api", + "Test Text", + "Test Class", + "Test Style", + "https://jenkins.io"); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(6, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("a").get(3); + DomElement icon = badge.getLastElementChild(); + + assertEquals("a", badge.getTagName()); + assertEquals("svg", icon.getTagName()); + + assertEquals(step.getText(), icon.getAttribute("data-html-tooltip")); + assertEquals(step.getCssClass(), badge.getAttribute("class")); + assertEquals("icon-sm", icon.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + assertEquals(step.getLink(), badge.getAttribute("href")); + } + } + + @Test + void iconWithoutLink(JenkinsRule r) throws Throwable { + AddBadgeStep step = new AddBadgeStep( + null, "symbol-rocket plugin-ionicons-api", "Test Text", "Test Class", "Test Style", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(6, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("span").get(2); + DomElement icon = badge.getLastElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", badge.getTagName()); + + assertEquals(step.getText(), icon.getAttribute("data-html-tooltip")); + assertEquals(step.getCssClass(), badge.getAttribute("class")); + assertEquals("icon-sm", icon.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + } + } + + @Test + void textWithLink(JenkinsRule r) throws Throwable { + AddBadgeStep step = + new AddBadgeStep(null, null, "Test Text", "Test Class", "Test Style", "https://jenkins.io"); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(5, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("a").get(3); + + assertEquals("a", badge.getTagName()); + + assertEquals(step.getText(), badge.getTextContent()); + assertEquals(step.getCssClass(), badge.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + assertEquals(step.getLink(), badge.getAttribute("href")); + } + } + + @Test + void textWithoutLink(JenkinsRule r) throws Throwable { + AddBadgeStep step = new AddBadgeStep(null, null, "Test Text", "Test Class", "Test Style", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(5, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("span").get(2); + + assertEquals("span", badge.getTagName()); + + assertEquals(step.getText(), badge.getTextContent()); + assertEquals(step.getCssClass(), badge.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + } + } + + @Test + void info(JenkinsRule r) throws Throwable { + AddInfoBadgeStep step = new AddInfoBadgeStep(null, "Test Text", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(6, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("span").get(2); + DomElement icon = badge.getLastElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", badge.getTagName()); + + assertEquals(step.getText(), icon.getAttribute("data-html-tooltip")); + assertEquals("icon-sm", icon.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + } + } + + @Test + void warning(JenkinsRule r) throws Throwable { + AddWarningBadgeStep step = new AddWarningBadgeStep(null, "Test Text", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(6, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("span").get(2); + DomElement icon = badge.getLastElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", badge.getTagName()); + + assertEquals(step.getText(), icon.getAttribute("data-html-tooltip")); + assertEquals("icon-sm", icon.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + } + } + + @Test + void error(JenkinsRule r) throws Throwable { + AddErrorBadgeStep step = new AddErrorBadgeStep(null, "Test Text", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(6, builds.getElementsByTagName("span").size()); + + DomElement badge = builds.getElementsByTagName("span").get(2); + DomElement icon = badge.getLastElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", badge.getTagName()); + + assertEquals(step.getText(), icon.getAttribute("data-html-tooltip")); + assertEquals("icon-sm", icon.getAttribute("class")); + assertEquals(step.getStyle(), badge.getAttribute("style")); + } + } + + @Test + void remove(JenkinsRule r) throws Throwable { + AddBadgeStep addStep = new AddBadgeStep( + UUID.randomUUID().toString(), + "symbol-rocket plugin-ionicons-api", + "Test Text", + "Test Class", + "Test Style", + "https://jenkins.io"); + RemoveBadgesStep removeStep = new RemoveBadgesStep(addStep.getId()); + WorkflowJob job = runJob(r, addStep, removeStep); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job); + DomElement builds = overview.getElementById("jenkins-builds"); + + assertEquals(4, builds.getElementsByTagName("span").size()); + } + } + } + + @Nested + class Summary { + + @Test + void iconWithTextWithLink(JenkinsRule r) throws Throwable { + AddSummaryStep step = new AddSummaryStep( + null, + "symbol-rocket plugin-ionicons-api", + "Test Text", + "Test Class", + "Test Style", + "https://jenkins.io"); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(2, overview.getElementsByTagName("tr").size()); + + DomElement summary = overview.getElementsByTagName("tr").get(0); + DomElement icon = summary.getFirstElementChild().getFirstElementChild(); + DomElement link = summary.getLastElementChild().getFirstElementChild(); + DomElement text = link.getFirstElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("a", link.getTagName()); + assertEquals("span", text.getTagName()); + + assertEquals(step.getText(), text.getTextContent()); + assertEquals(step.getCssClass(), text.getAttribute("class")); + assertEquals(step.getStyle(), text.getAttribute("style")); + assertEquals(step.getLink(), link.getAttribute("href")); + } + } + + @Test + void iconWithTextWithoutLink(JenkinsRule r) throws Throwable { + AddSummaryStep step = new AddSummaryStep( + null, "symbol-rocket plugin-ionicons-api", "Test Text", "Test Class", "Test Style", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(2, overview.getElementsByTagName("tr").size()); + + DomElement summary = overview.getElementsByTagName("tr").get(0); + DomElement icon = summary.getFirstElementChild().getFirstElementChild(); + DomElement text = summary.getLastElementChild().getFirstElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", text.getTagName()); + + assertEquals(step.getText(), text.getTextContent()); + assertEquals(step.getCssClass(), text.getAttribute("class")); + assertEquals(step.getStyle(), text.getAttribute("style")); + } + } + + @Test + void iconWithoutTextWithoutLink(JenkinsRule r) throws Throwable { + AddSummaryStep step = new AddSummaryStep( + null, "symbol-rocket plugin-ionicons-api", null, "Test Class", "Test Style", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(2, overview.getElementsByTagName("tr").size()); + + DomElement summary = overview.getElementsByTagName("tr").get(0); + DomElement icon = summary.getFirstElementChild().getFirstElementChild(); + DomElement text = summary.getLastElementChild().getFirstElementChild(); + + assertEquals("svg", icon.getTagName()); + assertEquals("span", text.getTagName()); + + assertTrue(StringUtils.isBlank(text.getTextContent())); + assertEquals(step.getCssClass(), text.getAttribute("class")); + assertEquals(step.getStyle(), text.getAttribute("style")); + } + } + + @Test + void textWithoutIconWithLink(JenkinsRule r) throws Throwable { + AddSummaryStep step = + new AddSummaryStep(null, null, "Test Text", "Test Class", "Test Style", "https://jenkins.io"); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(2, overview.getElementsByTagName("tr").size()); + + DomElement summary = overview.getElementsByTagName("tr").get(0); + DomElement icon = summary.getFirstElementChild().getFirstElementChild(); + DomElement link = summary.getLastElementChild().getFirstElementChild(); + DomElement text = link.getFirstElementChild(); + + assertEquals("img", icon.getTagName()); + assertEquals("a", link.getTagName()); + assertEquals("span", text.getTagName()); + + assertEquals("/jenkins" + Jenkins.RESOURCE_PATH + "/images/16x16/empty.png", icon.getAttribute("src")); + assertEquals(step.getText(), text.getTextContent()); + assertEquals(step.getCssClass(), text.getAttribute("class")); + assertEquals(step.getStyle(), text.getAttribute("style")); + assertEquals(step.getLink(), link.getAttribute("href")); + } + } + + @Test + void textWithoutIconWithoutLink(JenkinsRule r) throws Throwable { + AddSummaryStep step = new AddSummaryStep(null, null, "Test Text", "Test Class", "Test Style", null); + WorkflowJob job = runJob(r, step, null); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(2, overview.getElementsByTagName("tr").size()); + + DomElement summary = overview.getElementsByTagName("tr").get(0); + DomElement icon = summary.getFirstElementChild().getFirstElementChild(); + DomElement text = summary.getLastElementChild().getFirstElementChild(); + + assertEquals("img", icon.getTagName()); + assertEquals("span", text.getTagName()); + + assertEquals("/jenkins" + Jenkins.RESOURCE_PATH + "/images/16x16/empty.png", icon.getAttribute("src")); + assertEquals(step.getText(), text.getTextContent()); + assertEquals(step.getCssClass(), text.getAttribute("class")); + assertEquals(step.getStyle(), text.getAttribute("style")); + } + } + + @Test + void remove(JenkinsRule r) throws Throwable { + AddSummaryStep addStep = new AddSummaryStep( + UUID.randomUUID().toString(), + "symbol-rocket plugin-ionicons-api", + "Test Text", + "Test Class", + "Test Style", + "https://jenkins.io"); + RemoveSummariesStep removeStep = new RemoveSummariesStep(addStep.getId()); + WorkflowJob job = runJob(r, addStep, removeStep); + + try (JenkinsRule.WebClient webClient = r.createWebClient()) { + HtmlPage overview = webClient.getPage(job.getLastBuild()); + + assertEquals(1, overview.getElementsByTagName("tr").size()); + } + } + } + + private static WorkflowJob runJob(JenkinsRule r, AbstractAddBadgeStep addStep, AbstractRemoveBadgesStep removeStep) + throws Exception { + WorkflowJob project = r.jenkins.createProject(WorkflowJob.class, "project"); + + String script = + """ + pipeline { + agent any + stages { + stage('Testing') { + steps { + %s + %s + } + } + } + } + """ + .formatted(addStep.toString(), removeStep != null ? removeStep.toString() : ""); + + project.setDefinition(new CpsFlowDefinition(script, true)); + r.assertBuildStatusSuccess(project.scheduleBuild2(0)); + + return project; + } +}