diff --git a/pom.xml b/pom.xml
index dd8c46d6e9b..0a8ec4b88e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -511,7 +511,7 @@
org.jsoup
jsoup
- 1.15.3
+ 1.23.1
io.searchbox
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java
index d7dc47bccea..d4ce246ba5a 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java
@@ -9,6 +9,7 @@
import java.text.MessageFormat;
+import static edu.harvard.iq.dataverse.util.MarkupCheckerUtil.htmlEqualTo;
import static jakarta.ws.rs.core.Response.Status.*;
import java.io.File;
import java.io.IOException;
@@ -408,7 +409,7 @@ private Long createFeaturedItemAndGetId(String dataverseAlias, String apiToken,
private void verifyUpdatedFeaturedItem(Response response, String expectedContent, String expectedImageFileName, int expectedDisplayOrder, String type, String dvObject, String dvObjectDisplayName) {
response.prettyPrint();
response.then().assertThat()
- .body("data.content", equalTo(expectedContent))
+ .body("data.content", htmlEqualTo(expectedContent))
.body("data.imageFileName", equalTo(expectedImageFileName))
.body("data.displayOrder", equalTo(expectedDisplayOrder))
.body("data.type", equalTo(type))
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java
index 410a72a1054..1c1a9756360 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java
@@ -994,6 +994,7 @@ public void testSubtreePermissions() {
// Wait a little while for the index to pick up the datasets, otherwise timing issue with searching for it.
UtilIT.sleepForDatasetIndex(datasetId2.toString(), apiToken);
+ UtilIT.sleepForDatasetIndex(datasetId.toString(), apiToken);
String identifier = JsonPath.from(datasetAsJson.getBody().asString()).getString("data.identifier");
String identifier2 = JsonPath.from(datasetAsJson2.getBody().asString()).getString("data.identifier");
diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java
index 02219059db3..d1527734ab3 100644
--- a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java
+++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java
@@ -1,10 +1,12 @@
package edu.harvard.iq.dataverse.util;
+import org.jsoup.Jsoup;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
public class MarkupCheckerTest {
@@ -28,7 +30,12 @@ public class MarkupCheckerTest {
"NULL, NULL"
}, nullValues = {"NULL"})
public void testSanitizeBasicHTML(String unsafe, String safe) {
- assertEquals(safe, MarkupChecker.sanitizeBasicHTML(unsafe));
+ String actual = MarkupChecker.sanitizeBasicHTML(unsafe);
+ MarkupCheckerUtil.assertHtmlEqual(safe, actual);
+ // Sanity check that the key tag we strip is truly gone
+ if(actual != null) {
+ assertFalse(actual.toLowerCase().contains("script"));
+ }
}
/**
@@ -51,21 +58,12 @@ public void testSanitizeBasicHTML(String unsafe, String safe) {
"NULL, NULL"
}, nullValues = {"NULL"})
public void testSanitizeAdvancedHTML(String unsafe, String safe) {
- String sanitizedOutput = MarkupChecker.sanitizeAdvancedHTML(unsafe);
-
- // Normalize both the expected and actual content by removing whitespaces
-
- String normalizedSafe = null;
- if (safe != null) {
- normalizedSafe = safe.replaceAll("\\s+", "").trim();
+ String actual = MarkupChecker.sanitizeAdvancedHTML(unsafe);
+ MarkupCheckerUtil.assertHtmlEqual(safe, actual);
+ // Sanity check that the key tag we strip is truly gone
+ if(actual != null) {
+ assertFalse(actual.toLowerCase().contains("script"));
}
-
- String normalizedOutput = null;
- if (sanitizedOutput != null) {
- normalizedOutput = sanitizedOutput.replaceAll("\\s+", "").trim();
- }
-
- assertEquals(normalizedSafe, normalizedOutput);
}
/**
@@ -90,5 +88,4 @@ public void testStripAllTags(String unsafe, String safe) {
public void testEscapeHtml() {
assertEquals("foo<br>bar", MarkupChecker.escapeHtml("foo
bar"));
}
-
}
diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java
new file mode 100644
index 00000000000..c28c983618f
--- /dev/null
+++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java
@@ -0,0 +1,97 @@
+package edu.harvard.iq.dataverse.util;
+
+import org.jsoup.Jsoup;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MarkupCheckerUtil {
+
+ /** Test HTML equivalence and ignore differences in the order of attributes
+ * This does (in normalizeHtml()) use Jsoup to help test Jsoup though.
+ *
+ * @param expected - the HTML we expect to be returned
+ * @param actual - the HTML we actually got
+ */
+ public static void assertHtmlEqual(String expected, String actual) {
+ if (expected == null || actual == null) {
+ assertEquals(expected, actual);
+ return;
+ }
+
+ String normalizedExpected = normalizeHtml(expected);
+ String normalizedActual = normalizeHtml(actual);
+ assertEquals(normalizedExpected, normalizedActual);
+ }
+
+ /**
+ * Creates a Hamcrest matcher that compares HTML after normalization, ignoring
+ * differences such as attribute ordering and whitespace.
+ *
+ * @param expected the expected HTML
+ * @return a matcher for HTML-equivalent strings
+ */
+ public static org.hamcrest.Matcher htmlEqualTo(String expected) {
+ return new org.hamcrest.BaseMatcher() {
+
+ @Override
+ public boolean matches(Object actual) {
+ if (expected == null || actual == null) {
+ return expected == actual;
+ }
+
+ if (!(actual instanceof String actualHtml)) {
+ return false;
+ }
+
+ return normalizeHtml(expected).equals(normalizeHtml(actualHtml));
+ }
+
+ @Override
+ public void describeTo(org.hamcrest.Description description) {
+ description.appendText("HTML equal to ")
+ .appendValue(expected);
+
+ if (expected != null) {
+ description.appendText(" after normalization as ")
+ .appendValue(normalizeHtml(expected));
+ }
+ }
+
+ @Override
+ public void describeMismatch(Object actual, org.hamcrest.Description description) {
+ if (actual == null) {
+ description.appendText("was null");
+ return;
+ }
+
+ if (!(actual instanceof String actualHtml)) {
+ description.appendText("was ")
+ .appendValue(actual);
+ return;
+ }
+
+ description.appendText("was ")
+ .appendValue(actualHtml)
+ .appendText(" after normalization as ")
+ .appendValue(normalizeHtml(actualHtml));
+ }
+ };
+ }
+
+ private static String normalizeHtml(String html) {
+ org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(html);
+ for (org.jsoup.nodes.Element el : doc.getAllElements()) {
+ org.jsoup.nodes.Attributes attrs = el.attributes();
+ java.util.List list = new java.util.ArrayList<>(attrs.asList());
+ list.sort(java.util.Map.Entry.comparingByKey());
+ for (org.jsoup.nodes.Attribute a : list) {
+ attrs.remove(a.getKey());
+ }
+ for (org.jsoup.nodes.Attribute a : list) {
+ attrs.put(a);
+ }
+ }
+ return doc.body().html().replaceAll("\\s+", "");
+ }
+}