Skip to content

Commit

Permalink
Introduce UI tests (#214)
Browse files Browse the repository at this point in the history
* Add UI tests for all badge types and constellations

* Separate tests for badges and summaries

* Properly handle empty values

* Gracefully handle empty icon for summaries
  • Loading branch information
strangelookingnerd authored Nov 19, 2024
1 parent 67306d2 commit 59ed594
Show file tree
Hide file tree
Showing 10 changed files with 576 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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-")
Expand Down Expand Up @@ -122,7 +122,7 @@ public void setText(String text) {

@Whitelisted
public String getText() {
if (StringUtils.isEmpty(text)) {
if (StringUtils.isBlank(text)) {
return text;
}

Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ THE SOFTWARE.

<?jelly escape-by-default='false'?>
<j:jelly xmlns:j="jelly:core" xmlns:t="/lib/hudson">
<j:if test="${it.icon != null &amp;&amp; it.icon != ''}">
<t:summary icon="${it.icon}" href="${it.link}">
<span class="${it.cssClass}" style="${it.style}">
${it.text}
</span>
</t:summary>
</j:if>
<t:summary icon="${it.icon}" href="${it.link}">
<span class="${it.cssClass}" style="${it.style}">
${it.text}
</span>
</t:summary>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;

@WithJenkins
class RemoveBadgesStepTest extends AbstractRemoveBadgeStepTest {
class RemoveBadgesStepTest extends AbstractRemoveBadgesStepTest {

@Override
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Loading

0 comments on commit 59ed594

Please sign in to comment.