` elements with CSS classes:
+
+```html
+
+```
+
+Basic CSS example:
+
+```css
+.markdown-alert {
+ padding: 0.5rem 1rem;
+ margin-bottom: 1rem;
+ border-left: 4px solid;
+}
+
+.markdown-alert-note { border-color: #0969da; background-color: #ddf4ff; }
+.markdown-alert-tip { border-color: #1a7f37; background-color: #dcffe4; }
+.markdown-alert-important { border-color: #8250df; background-color: #f6f0ff; }
+.markdown-alert-warning { border-color: #9a6700; background-color: #fff8c5; }
+.markdown-alert-caution { border-color: #cf222e; background-color: #ffebe9; }
+```
+
+
+
+Icons can be added using GitHub's [Octicons](https://primer.style/octicons/):
+
+
+
+## License
+
+See the main commonmark-java project for license information.
diff --git a/commonmark-ext-gfm-alerts/pom.xml b/commonmark-ext-gfm-alerts/pom.xml
new file mode 100644
index 000000000..02ecbf802
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ org.commonmark
+ commonmark-parent
+ 0.28.1-SNAPSHOT
+
+
+ commonmark-ext-gfm-alerts
+ commonmark-java extension for alerts
+ commonmark-java extension for GFM alerts (admonition blocks) using [!TYPE] syntax (GitHub Flavored Markdown)
+
+
+
+ org.commonmark
+ commonmark
+
+
+
+ org.commonmark
+ commonmark-test-util
+ test
+
+
+
+
diff --git a/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png b/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png
new file mode 100644
index 000000000..47da9402b
Binary files /dev/null and b/commonmark-ext-gfm-alerts/screenshots/alerts-with-icons.png differ
diff --git a/commonmark-ext-gfm-alerts/screenshots/alerts.png b/commonmark-ext-gfm-alerts/screenshots/alerts.png
new file mode 100644
index 000000000..83d4009f0
Binary files /dev/null and b/commonmark-ext-gfm-alerts/screenshots/alerts.png differ
diff --git a/commonmark-ext-gfm-alerts/src/main/java/module-info.java b/commonmark-ext-gfm-alerts/src/main/java/module-info.java
new file mode 100644
index 000000000..e8b5aecb7
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/module-info.java
@@ -0,0 +1,5 @@
+module org.commonmark.ext.gfm.alerts {
+ exports org.commonmark.ext.gfm.alerts;
+
+ requires transitive org.commonmark;
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java
new file mode 100644
index 000000000..bb28e7344
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/Alert.java
@@ -0,0 +1,19 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.node.CustomBlock;
+
+/**
+ * Alert block for highlighting important information using {@code [!TYPE]} syntax.
+ */
+public class Alert extends CustomBlock {
+
+ private final String type;
+
+ public Alert(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java
new file mode 100644
index 000000000..3990034d2
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java
@@ -0,0 +1,118 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.ext.gfm.alerts.internal.AlertPostProcessor;
+import org.commonmark.ext.gfm.alerts.internal.AlertHtmlNodeRenderer;
+import org.commonmark.ext.gfm.alerts.internal.AlertMarkdownNodeRenderer;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.NodeRenderer;
+import org.commonmark.renderer.html.HtmlNodeRendererContext;
+import org.commonmark.renderer.html.HtmlNodeRendererFactory;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererFactory;
+import org.commonmark.renderer.markdown.MarkdownRenderer;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Extension for GFM alerts using {@code [!TYPE]} syntax (GitHub Flavored Markdown).
+ *
+ * Create with {@link #create()} or {@link #builder()} and configure on builders
+ * ({@link org.commonmark.parser.Parser.Builder#extensions(Iterable)},
+ * {@link HtmlRenderer.Builder#extensions(Iterable)}).
+ * Parsed alerts become {@link Alert} blocks.
+ */
+public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
+ MarkdownRenderer.MarkdownRendererExtension {
+
+ static final Set STANDARD_TYPES = Set.of("NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION");
+
+ private final Map customTypes;
+
+ private AlertsExtension(Builder builder) {
+ this.customTypes = new HashMap<>(builder.customTypes);
+ }
+
+ public static Extension create() {
+ return builder().build();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void extend(Parser.Builder parserBuilder) {
+ var allowedTypes = new HashSet<>(STANDARD_TYPES);
+ allowedTypes.addAll(customTypes.keySet());
+ parserBuilder.postProcessor(new AlertPostProcessor(allowedTypes));
+ }
+
+ @Override
+ public void extend(HtmlRenderer.Builder rendererBuilder) {
+ rendererBuilder.nodeRendererFactory(new HtmlNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(HtmlNodeRendererContext context) {
+ return new AlertHtmlNodeRenderer(context, customTypes);
+ }
+ });
+ }
+
+ @Override
+ public void extend(MarkdownRenderer.Builder rendererBuilder) {
+ rendererBuilder.nodeRendererFactory(new MarkdownNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(MarkdownNodeRendererContext context) {
+ return new AlertMarkdownNodeRenderer(context);
+ }
+
+ @Override
+ public Set getSpecialCharacters() {
+ return Set.of();
+ }
+ });
+ }
+
+ /**
+ * Builder for configuring the alerts extension.
+ */
+ public static class Builder {
+ private final Map customTypes = new HashMap<>();
+
+ /**
+ * Adds a custom alert type with a display title.
+ *
+ * This can also be used to override the display title of standard GFM types
+ * (e.g., for localization).
+ *
+ * @param type the alert type (must be uppercase)
+ * @param title the display title for this alert type
+ * @return {@code this}
+ */
+ public Builder addCustomType(String type, String title) {
+ if (type == null || type.isEmpty()) {
+ throw new IllegalArgumentException("Type must not be null or empty");
+ }
+ if (title == null || title.isEmpty()) {
+ throw new IllegalArgumentException("Title must not be null or empty");
+ }
+ if (!type.equals(type.toUpperCase(Locale.ROOT))) {
+ throw new IllegalArgumentException("Type must be uppercase: " + type);
+ }
+ customTypes.put(type, title);
+ return this;
+ }
+
+ /**
+ * @return a configured {@link Extension}
+ */
+ public Extension build() {
+ return new AlertsExtension(this);
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java
new file mode 100644
index 000000000..ca562ba33
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java
@@ -0,0 +1,78 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.html.HtmlNodeRendererContext;
+import org.commonmark.renderer.html.HtmlWriter;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class AlertHtmlNodeRenderer extends AlertNodeRenderer {
+
+ private final HtmlWriter htmlWriter;
+ private final HtmlNodeRendererContext context;
+ private final Map customTypeTitles;
+
+ public AlertHtmlNodeRenderer(HtmlNodeRendererContext context, Map customTypeTitles) {
+ this.htmlWriter = context.getWriter();
+ this.context = context;
+ this.customTypeTitles = customTypeTitles;
+ }
+
+ @Override
+ protected void renderAlert(Alert alert) {
+ var type = alert.getType();
+ var cssClass = type.toLowerCase();
+
+ htmlWriter.line();
+ var attributes = new LinkedHashMap();
+ attributes.put("class", "markdown-alert markdown-alert-" + cssClass);
+ attributes.put("data-alert-type", cssClass);
+
+ htmlWriter.tag("div", context.extendAttributes(alert, "div", attributes));
+ htmlWriter.line();
+
+ // Render alert title
+ htmlWriter.tag("p", context.extendAttributes(alert, "p", Map.of("class", "markdown-alert-title")));
+ htmlWriter.text(getAlertTitle(type));
+ htmlWriter.tag("/p");
+ htmlWriter.line();
+
+ // Render children (the alert content)
+ renderChildren(alert);
+
+ htmlWriter.tag("/div");
+ htmlWriter.line();
+ }
+
+ private String getAlertTitle(String type) {
+ var customTypeTitle = customTypeTitles.get(type);
+ if (customTypeTitle != null) {
+ return customTypeTitle;
+ }
+ switch (type) {
+ case "NOTE":
+ return "Note";
+ case "TIP":
+ return "Tip";
+ case "IMPORTANT":
+ return "Important";
+ case "WARNING":
+ return "Warning";
+ case "CAUTION":
+ return "Caution";
+ default:
+ throw new IllegalStateException("Unknown alert type: " + type);
+ }
+ }
+
+ private void renderChildren(Node parent) {
+ var node = parent.getFirstChild();
+ while (node != null) {
+ var next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java
new file mode 100644
index 000000000..e3da62aea
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertMarkdownNodeRenderer.java
@@ -0,0 +1,38 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
+import org.commonmark.renderer.markdown.MarkdownWriter;
+
+public class AlertMarkdownNodeRenderer extends AlertNodeRenderer {
+
+ private final MarkdownWriter writer;
+ private final MarkdownNodeRendererContext context;
+
+ public AlertMarkdownNodeRenderer(MarkdownNodeRendererContext context) {
+ this.writer = context.getWriter();
+ this.context = context;
+ }
+
+ @Override
+ protected void renderAlert(Alert alert) {
+ // First line: > [!TYPE]
+ writer.writePrefix("> ");
+ writer.pushPrefix("> ");
+ writer.raw("[!" + alert.getType() + "]");
+ writer.line();
+ renderChildren(alert);
+ writer.popPrefix();
+ writer.block();
+ }
+
+ private void renderChildren(Node parent) {
+ var node = parent.getFirstChild();
+ while (node != null) {
+ var next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java
new file mode 100644
index 000000000..45b34bb46
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertNodeRenderer.java
@@ -0,0 +1,23 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.Node;
+import org.commonmark.renderer.NodeRenderer;
+
+import java.util.Set;
+
+public abstract class AlertNodeRenderer implements NodeRenderer {
+
+ @Override
+ public Set> getNodeTypes() {
+ return Set.of(Alert.class);
+ }
+
+ @Override
+ public void render(Node node) {
+ var alert = (Alert) node;
+ renderAlert(alert);
+ }
+
+ protected abstract void renderAlert(Alert alert);
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java
new file mode 100644
index 000000000..8008fc8dd
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertPostProcessor.java
@@ -0,0 +1,111 @@
+package org.commonmark.ext.gfm.alerts.internal;
+
+import org.commonmark.ext.gfm.alerts.Alert;
+import org.commonmark.node.BlockQuote;
+import org.commonmark.node.HardLineBreak;
+import org.commonmark.node.Node;
+import org.commonmark.node.Paragraph;
+import org.commonmark.node.SoftLineBreak;
+import org.commonmark.node.Text;
+import org.commonmark.parser.PostProcessor;
+
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class AlertPostProcessor implements PostProcessor {
+
+ // Alert type marker, matching any case (GitHub supports lowercase, mixed, and uppercase)
+ private static final Pattern ALERT_PATTERN = Pattern.compile("^\\[!([a-zA-Z]+)]\\s*$");
+
+ private final Set allowedTypes;
+
+ public AlertPostProcessor(Set allowedTypes) {
+ this.allowedTypes = allowedTypes;
+ }
+
+ @Override
+ public Node process(Node document) {
+ // Only look at direct children of Document — GitHub only detects alerts at the top level.
+ var child = document.getFirstChild();
+ while (child != null) {
+ var next = child.getNext();
+ if (child instanceof BlockQuote) {
+ tryConvertToAlert((BlockQuote) child);
+ }
+ child = next;
+ }
+ return document;
+ }
+
+ private void tryConvertToAlert(BlockQuote blockQuote) {
+ var firstChild = blockQuote.getFirstChild();
+ if (!(firstChild instanceof Paragraph)) {
+ return;
+ }
+
+ var paragraph = (Paragraph) firstChild;
+ var firstInline = paragraph.getFirstChild();
+ if (!(firstInline instanceof Text)) {
+ return;
+ }
+
+ var textNode = (Text) firstInline;
+
+ // The alert marker can be the entire text node content, or just the first line
+ // before a line break (trailing spaces create a HardLineBreak instead of SoftLineBreak).
+ var afterMarker = firstInline.getNext();
+ if (afterMarker != null && !(afterMarker instanceof SoftLineBreak) && !(afterMarker instanceof HardLineBreak)) {
+ // Text followed by something other than a line break - not an alert
+ return;
+ }
+
+ var matcher = ALERT_PATTERN.matcher(textNode.getLiteral());
+ if (!matcher.matches()) {
+ return;
+ }
+
+ var type = matcher.group(1).toUpperCase(Locale.ROOT);
+ if (!allowedTypes.contains(type)) {
+ return;
+ }
+
+ // Must have content after the marker line. An alert with ONLY the marker
+ // and no content is a normal blockquote on GitHub.
+ if (afterMarker != null) {
+ // There's a line break after marker - check if there's content after it
+ if (afterMarker.getNext() == null && paragraph.getNext() == null) {
+ return;
+ }
+ afterMarker.unlink();
+ } else {
+ // Marker is the only thing in the paragraph
+ if (paragraph.getNext() == null) {
+ return;
+ }
+ }
+
+ // Valid alert. Create Alert node and transfer children.
+ var alert = new Alert(type);
+ alert.setSourceSpans(blockQuote.getSourceSpans());
+ blockQuote.insertAfter(alert);
+
+ // Remove the marker text from the first paragraph
+ firstInline.unlink();
+
+ // If paragraph is now empty, remove it
+ if (paragraph.getFirstChild() == null) {
+ paragraph.unlink();
+ }
+
+ // Move remaining children from blockquote to alert
+ var child = blockQuote.getFirstChild();
+ while (child != null) {
+ var next = child.getNext();
+ alert.appendChild(child);
+ child = next;
+ }
+
+ blockQuote.unlink();
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html b/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html
new file mode 100644
index 000000000..145232a87
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/main/javadoc/overview.html
@@ -0,0 +1,6 @@
+
+
+Extension for GitHub Flavored Markdown (GFM) alerts using blockquote syntax
+See {@link org.commonmark.ext.gfm.alerts.AlertsExtension}
+
+
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java
new file mode 100644
index 000000000..aca90e2df
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsMarkdownRendererTest.java
@@ -0,0 +1,73 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.markdown.MarkdownRenderer;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AlertsMarkdownRendererTest {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+ private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
+
+ @Test
+ public void alertRoundTrip() {
+ assertRoundTrip("> [!WARNING]\n> Be careful\n");
+ }
+
+ @Test
+ public void allStandardTypesRoundTrip() {
+ assertRoundTrip("> [!NOTE]\n> Note\n");
+ assertRoundTrip("> [!TIP]\n> Tip\n");
+ assertRoundTrip("> [!IMPORTANT]\n> Important\n");
+ assertRoundTrip("> [!WARNING]\n> Warning\n");
+ assertRoundTrip("> [!CAUTION]\n> Caution\n");
+ }
+
+ @Test
+ public void lowercaseTypeRendersAsUppercase() {
+ // Lowercase input gets normalized to uppercase type
+ String rendered = RENDERER.render(PARSER.parse("> [!note]\n> Content\n"));
+ assertThat(rendered).isEqualTo("> [!NOTE]\n> Content\n");
+ }
+
+ @Test
+ public void alertWithMultipleParagraphs() {
+ String input = "> [!NOTE]\n> First paragraph\n>\n> Second paragraph\n";
+ // MarkdownWriter always writes the prefix including trailing space
+ String expected = "> [!NOTE]\n> First paragraph\n> \n> Second paragraph\n";
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(expected);
+ }
+
+ @Test
+ public void customTypeRoundTrip() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ MarkdownRenderer renderer = MarkdownRenderer.builder().extensions(Set.of(extension)).build();
+
+ String input = "> [!INFO]\n> Custom type\n";
+ String rendered = renderer.render(parser.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+
+ @Test
+ public void alertWithList() {
+ String input = "> [!NOTE]\n> Items:\n> \n> - First\n> - Second\n";
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+
+ private void assertRoundTrip(String input) {
+ String rendered = RENDERER.render(PARSER.parse(input));
+ assertThat(rendered).isEqualTo(input);
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java
new file mode 100644
index 000000000..8155d8009
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsSpecTest.java
@@ -0,0 +1,44 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.commonmark.testutil.RenderingTestCase;
+import org.commonmark.testutil.example.Example;
+import org.commonmark.testutil.example.ExampleReader;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Set;
+
+@ParameterizedClass
+@MethodSource("data")
+public class AlertsSpecTest extends RenderingTestCase {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+ // Use softbreak(" ") to match GitHub's rendering for easier comparison with GitHub API output.
+ private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).softbreak(" \n").build();
+
+ @Parameter
+ Example example;
+
+ static List data() {
+ URL spec = AlertsSpecTest.class.getResource("/alerts-spec.txt");
+ return ExampleReader.readExamples(spec, "alert");
+ }
+
+ @Test
+ public void testHtmlRendering() {
+ assertRendering(example.getSource(), example.getHtml());
+ }
+
+ @Override
+ protected String render(String source) {
+ return RENDERER.render(PARSER.parse(source));
+ }
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java
new file mode 100644
index 000000000..c46c532fe
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java
@@ -0,0 +1,140 @@
+package org.commonmark.ext.gfm.alerts;
+
+import org.commonmark.Extension;
+import org.commonmark.node.Node;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AlertsTest {
+
+ private static final Set EXTENSIONS = Set.of(AlertsExtension.create());
+ private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
+
+ // Custom types
+
+ @Test
+ public void customType() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!INFO]\n> Custom alert"))).isEqualTo(
+ "\n" +
+ "
Information
\n" +
+ "
Custom alert
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void multipleCustomTypes() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .addCustomType("SUCCESS", "Success!")
+ .addCustomType("DANGER", "Danger!")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!INFO]\n> Info content\n\n> [!SUCCESS]\n> Success content\n\n> [!DANGER]\n> Danger content"))).isEqualTo(
+ "\n" +
+ "
Information
\n" +
+ "
Info content
\n" +
+ "
\n" +
+ "\n" +
+ "
Success!
\n" +
+ "
Success content
\n" +
+ "
\n" +
+ "\n" +
+ "
Danger!
\n" +
+ "
Danger content
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void standardTypesWithCustomConfigured() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!NOTE]\n> Standard type"))).isEqualTo(
+ "\n" +
+ "
Note
\n" +
+ "
Standard type
\n" +
+ "
\n");
+ }
+
+ @Test
+ public void overrideStandardTypeTitle() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("NOTE", "Nota")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+ HtmlRenderer renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
+
+ assertThat(renderer.render(parser.parse("> [!NOTE]\n> Localized title"))).isEqualTo(
+ "\n" +
+ "
Nota
\n" +
+ "
Localized title
\n" +
+ "
\n");
+ }
+
+ // Custom type validation
+
+ @Test
+ public void customTypeMustBeUppercase() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("info", "Information").build());
+ }
+
+ @Test
+ public void customTypeMustNotBeEmpty() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("", "Title").build());
+ }
+
+ @Test
+ public void customTypeTitleMustNotBeEmpty() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AlertsExtension.builder().addCustomType("INFO", "").build());
+ }
+
+ // AST
+
+ @Test
+ public void alertParsedAsAlertNode() {
+ Node document = PARSER.parse("> [!NOTE]\n> This is a note");
+ Node firstChild = document.getFirstChild();
+ assertThat(firstChild).isInstanceOf(Alert.class);
+ Alert alert = (Alert) firstChild;
+ assertThat(alert.getType()).isEqualTo("NOTE");
+ }
+
+ @Test
+ public void customTypeParsedAsAlertNode() {
+ Extension extension = AlertsExtension.builder()
+ .addCustomType("INFO", "Information")
+ .build();
+
+ Parser parser = Parser.builder().extensions(Set.of(extension)).build();
+
+ Node document = parser.parse("> [!INFO]\n> Custom alert");
+ Alert alert = (Alert) document.getFirstChild();
+
+ assertThat(alert.getType()).isEqualTo("INFO");
+ }
+
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java
new file mode 100644
index 000000000..34b78385c
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/examples/AlertsExample.java
@@ -0,0 +1,85 @@
+package org.commonmark.ext.gfm.alerts.examples;
+
+import org.commonmark.ext.gfm.alerts.AlertsExtension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+
+import java.util.List;
+
+/**
+ * Example demonstrating the use of the GFM Alerts extension.
+ */
+public class AlertsExample {
+
+ public static void main(String[] args) {
+ standardTypesExample();
+ System.out.println("\n" + "=".repeat(60) + "\n");
+ customTypesExample();
+ }
+
+ private static void standardTypesExample() {
+ System.out.println("STANDARD GFM ALERT TYPES");
+ System.out.println("=".repeat(60));
+
+ var extension = AlertsExtension.create();
+
+ var parser = Parser.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var renderer = HtmlRenderer.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var markdown = "# GFM Alerts Demo\n\n" +
+ "> [!NOTE]\n" +
+ "> Highlights information that users should take into account.\n\n" +
+ "> [!TIP]\n" +
+ "> Helpful advice for doing things better.\n\n" +
+ "> [!IMPORTANT]\n" +
+ "> Key information users need to know.\n\n" +
+ "> [!WARNING]\n" +
+ "> Urgent info that needs immediate attention.\n\n" +
+ "> [!CAUTION]\n" +
+ "> Advises about risks or negative outcomes.\n";
+
+ var html = renderer.render(parser.parse(markdown));
+
+ System.out.println("Markdown Input:");
+ System.out.println(markdown);
+ System.out.println("\nHTML Output:");
+ System.out.println(html);
+ }
+
+ private static void customTypesExample() {
+ System.out.println("CUSTOM ALERT TYPES");
+ System.out.println("=".repeat(60));
+
+ var extension = AlertsExtension.builder()
+ .addCustomType("BUG", "Known Bug")
+ .build();
+
+ var parser = Parser.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var renderer = HtmlRenderer.builder()
+ .extensions(List.of(extension))
+ .build();
+
+ var markdown = "# Custom Alert Types\n\n" +
+ "> [!NOTE]\n" +
+ "> Useful information that users should know.\n\n" +
+ "> [!TIP]\n" +
+ "> Helpful advice for doing things better.\n\n" +
+ "> [!BUG]\n" +
+ "> This feature has a known issue with large files (see #42).\n";
+
+ var html = renderer.render(parser.parse(markdown));
+
+ System.out.println("Markdown Input:");
+ System.out.println(markdown);
+ System.out.println("\nHTML Output:");
+ System.out.println(html);
+ }
+}
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md
new file mode 100644
index 000000000..9c1cf117b
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec-template.md
@@ -0,0 +1,280 @@
+# Alerts
+
+## Standard types
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is a tip
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> This is important
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!WARNING]
+> This is a warning
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!CAUTION]
+> This is a caution
+````````````````````````````````
+
+## Case insensitivity
+
+Alert type matching is case-insensitive.
+
+```````````````````````````````` example alert
+> [!note]
+> Content
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!Note]
+> Content
+````````````````````````````````
+
+## Alert content
+
+Marker alone in first paragraph, blank line, then content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+>
+> Content
+````````````````````````````````
+
+Multiple paragraphs:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First paragraph
+>
+> Second paragraph
+````````````````````````````````
+
+Inline formatting:
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is **bold** and *italic*
+````````````````````````````````
+
+Code block inside alert:
+
+```````````````````````````````` example alert
+> [!TIP]
+> Code:
+>
+> function() { }
+>
+> End
+````````````````````````````````
+
+List inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> Items:
+> - First item
+> - Second item
+````````````````````````````````
+
+Links inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> Check out [this link](https://example.com) for more info
+````````````````````````````````
+
+Heading inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> ## Heading
+> Content below heading
+````````````````````````````````
+
+Empty lines in middle of alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First
+>
+>
+> After empty lines
+````````````````````````````````
+
+## Not an alert
+
+Text after marker on the same line:
+
+```````````````````````````````` example alert
+> [!NOTE] Some text
+````````````````````````````````
+
+Unknown type:
+
+```````````````````````````````` example alert
+> [!INVALID]
+> Some text
+````````````````````````````````
+
+Unconfigured custom type is not an alert:
+
+```````````````````````````````` example alert
+> [!INFO]
+> Should be blockquote
+````````````````````````````````
+
+Marker with no content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+````````````````````````````````
+
+Whitespace-only content after marker:
+
+```````````````````````````````` example alert
+> [!TIP]
+>
+>
+````````````````````````````````
+
+Extra space inside marker:
+
+```````````````````````````````` example alert
+> [! NOTE]
+> Should be blockquote
+````````````````````````````````
+
+Missing brackets:
+
+```````````````````````````````` example alert
+> !NOTE
+> Should be blockquote
+````````````````````````````````
+
+Missing exclamation mark:
+
+```````````````````````````````` example alert
+> [NOTE]
+> Should be blockquote
+````````````````````````````````
+
+Regular blockquote is not affected:
+
+```````````````````````````````` example alert
+> This is a regular blockquote
+````````````````````````````````
+
+## Boundaries
+
+Trailing spaces after marker:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+````````````````````````````````
+
+Trailing tabs after marker:
+
+```````````````````````````````` example alert
+> [!WARNING]→→
+> Be careful
+````````````````````````````````
+
+Leading spaces before blockquote marker:
+
+```````````````````````````````` example alert
+ > [!IMPORTANT]
+ > Content
+````````````````````````````````
+
+Blank line after marker ends the blockquote (not an alert):
+
+```````````````````````````````` example alert
+> [!NOTE]
+
+Some text
+````````````````````````````````
+
+Alert followed by blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is an alert
+
+> This is a blockquote
+````````````````````````````````
+
+Adjacent alerts:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First alert
+
+> [!WARNING]
+> Second alert
+````````````````````````````````
+
+## Nesting and containers
+
+Nested alert inside alert renders as blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> [!WARNING]
+>> Nested content
+````````````````````````````````
+
+Nested blockquote inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> Nested blockquote
+````````````````````````````````
+
+Alert inside list item stays as blockquote:
+
+```````````````````````````````` example alert
+- > [!NOTE]
+ > Test
+````````````````````````````````
+
+Alert marker in content is treated as text:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+> [!WARNING]
+> This is still part of the note
+````````````````````````````````
+
+## Continuation and interruption
+
+Lazy continuation:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First line
+Lazy continuation
+> Continues alert
+````````````````````````````````
+
+Alert type after regular blockquote content is not an alert:
+
+```````````````````````````````` example alert
+> Regular blockquote
+> [!NOTE]
+> More text
+````````````````````````````````
\ No newline at end of file
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt
new file mode 100644
index 000000000..6f041fee4
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/alerts-spec.txt
@@ -0,0 +1,492 @@
+# Alerts
+
+Expectations verified against GitHub Markdown API (gh api markdown -f mode=gfm).
+Our HTML omits GitHub's SVG icons and uses a `data-alert-type` attribute instead.
+
+## Standard types
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is a tip
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> This is important
+.
+
+
Important
+
This is important
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!WARNING]
+> This is a warning
+.
+
+
Warning
+
This is a warning
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!CAUTION]
+> This is a caution
+.
+
+
Caution
+
This is a caution
+
+````````````````````````````````
+
+## Case insensitivity
+
+Alert type matching is case-insensitive.
+
+```````````````````````````````` example alert
+> [!note]
+> Content
+.
+
+````````````````````````````````
+
+```````````````````````````````` example alert
+> [!Note]
+> Content
+.
+
+````````````````````````````````
+
+## Alert content
+
+Marker alone in first paragraph, blank line, then content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+>
+> Content
+.
+
+````````````````````````````````
+
+Multiple paragraphs:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First paragraph
+>
+> Second paragraph
+.
+
+
Note
+
First paragraph
+
Second paragraph
+
+````````````````````````````````
+
+Inline formatting:
+
+```````````````````````````````` example alert
+> [!TIP]
+> This is **bold** and *italic*
+.
+
+
Tip
+
This is bold and italic
+
+````````````````````````````````
+
+Code block inside alert:
+
+```````````````````````````````` example alert
+> [!TIP]
+> Code:
+>
+> function() { }
+>
+> End
+.
+
+
Tip
+
Code:
+
function() { }
+
+
End
+
+````````````````````````````````
+
+List inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> Items:
+> - First item
+> - Second item
+.
+
+
Important
+
Items:
+
+First item
+Second item
+
+
+````````````````````````````````
+
+Links inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> Check out [this link](https://example.com) for more info
+.
+
+````````````````````````````````
+
+Heading inside alert:
+
+```````````````````````````````` example alert
+> [!IMPORTANT]
+> ## Heading
+> Content below heading
+.
+
+
Important
+
Heading
+
Content below heading
+
+````````````````````````````````
+
+Empty lines in middle of alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First
+>
+>
+> After empty lines
+.
+
+
Note
+
First
+
After empty lines
+
+````````````````````````````````
+
+## Not an alert
+
+Text after marker on the same line:
+
+```````````````````````````````` example alert
+> [!NOTE] Some text
+.
+
+[!NOTE] Some text
+
+````````````````````````````````
+
+Unknown type:
+
+```````````````````````````````` example alert
+> [!INVALID]
+> Some text
+.
+
+[!INVALID]
+Some text
+
+````````````````````````````````
+
+Unconfigured custom type is not an alert:
+
+```````````````````````````````` example alert
+> [!INFO]
+> Should be blockquote
+.
+
+[!INFO]
+Should be blockquote
+
+````````````````````````````````
+
+Marker with no content:
+
+```````````````````````````````` example alert
+> [!NOTE]
+.
+
+[!NOTE]
+
+````````````````````````````````
+
+Whitespace-only content after marker:
+
+```````````````````````````````` example alert
+> [!TIP]
+>
+>
+.
+
+[!TIP]
+
+````````````````````````````````
+
+Extra space inside marker:
+
+```````````````````````````````` example alert
+> [! NOTE]
+> Should be blockquote
+.
+
+[! NOTE]
+Should be blockquote
+
+````````````````````````````````
+
+Missing brackets:
+
+```````````````````````````````` example alert
+> !NOTE
+> Should be blockquote
+.
+
+!NOTE
+Should be blockquote
+
+````````````````````````````````
+
+Missing exclamation mark:
+
+```````````````````````````````` example alert
+> [NOTE]
+> Should be blockquote
+.
+
+[NOTE]
+Should be blockquote
+
+````````````````````````````````
+
+Regular blockquote is not affected:
+
+```````````````````````````````` example alert
+> This is a regular blockquote
+.
+
+This is a regular blockquote
+
+````````````````````````````````
+
+## Boundaries
+
+Trailing spaces after marker:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+.
+
+````````````````````````````````
+
+Trailing tabs after marker:
+
+```````````````````````````````` example alert
+> [!WARNING]→→
+> Be careful
+.
+
+````````````````````````````````
+
+Leading spaces before blockquote marker:
+
+```````````````````````````````` example alert
+ > [!IMPORTANT]
+ > Content
+.
+
+````````````````````````````````
+
+Blank line after marker ends the blockquote (not an alert):
+
+```````````````````````````````` example alert
+> [!NOTE]
+
+Some text
+.
+
+[!NOTE]
+
+Some text
+````````````````````````````````
+
+Alert followed by blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is an alert
+
+> This is a blockquote
+.
+
+
Note
+
This is an alert
+
+
+This is a blockquote
+
+````````````````````````````````
+
+Adjacent alerts:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First alert
+
+> [!WARNING]
+> Second alert
+.
+
+
+````````````````````````````````
+
+## Nesting and containers
+
+Nested alert inside alert renders as blockquote:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> [!WARNING]
+>> Nested content
+.
+
+
Note
+
This is a note
+
+[!WARNING]
+Nested content
+
+
+````````````````````````````````
+
+Nested blockquote inside alert:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+>> Nested blockquote
+.
+
+
Note
+
This is a note
+
+Nested blockquote
+
+
+````````````````````````````````
+
+Alert inside list item stays as blockquote:
+
+```````````````````````````````` example alert
+- > [!NOTE]
+ > Test
+.
+
+````````````````````````````````
+
+Alert marker in content is treated as text:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> This is a note
+> [!WARNING]
+> This is still part of the note
+.
+
+
Note
+
This is a note
+[!WARNING]
+This is still part of the note
+
+````````````````````````````````
+
+## Continuation and interruption
+
+Lazy continuation:
+
+```````````````````````````````` example alert
+> [!NOTE]
+> First line
+Lazy continuation
+> Continues alert
+.
+
+
Note
+
First line
+Lazy continuation
+Continues alert
+
+````````````````````````````````
+
+Alert type after regular blockquote content is not an alert:
+
+```````````````````````````````` example alert
+> Regular blockquote
+> [!NOTE]
+> More text
+.
+
+Regular blockquote
+[!NOTE]
+More text
+
+````````````````````````````````
diff --git a/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java b/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java
new file mode 100644
index 000000000..06192f107
--- /dev/null
+++ b/commonmark-ext-gfm-alerts/src/test/resources/generate-alerts-spec.java
@@ -0,0 +1,111 @@
+///usr/bin/env jbang "$0" "$@" ; exit $?
+
+// Generates alerts-spec.txt from alerts-spec-template.md by rendering each example
+// through the GitHub Markdown API and inserting the normalized HTML expectation.
+//
+// Prerequisites: gh CLI installed and authenticated (gh auth login)
+// Usage: cd commonmark-ext-gfm-alerts/src/test/resources && jbang generate-alerts-spec.java
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+class GenerateAlertsSpec {
+
+ private static final String FENCE = "````````````````````````````````";
+ private static final String EXAMPLE_OPEN = FENCE + " example alert";
+
+ public static void main(String[] args) throws Exception {
+ var templatePath = Path.of("alerts-spec-template.md");
+ if (!Files.exists(templatePath)) {
+ System.err.println("Run from the directory containing alerts-spec-template.md");
+ System.exit(1);
+ }
+
+ var lines = Files.readAllLines(templatePath);
+ var output = new ArrayList();
+ var header = "Expectations verified against GitHub Markdown API (gh api markdown -f mode=gfm).\n" +
+ "Our HTML omits GitHub's SVG icons and uses a `data-alert-type` attribute instead.";
+
+ int exampleCount = 0;
+ int i = 0;
+ while (i < lines.size()) {
+ var line = lines.get(i);
+
+ // Insert header after the first heading
+ if (i == 0 && line.startsWith("# ")) {
+ output.add(line);
+ output.add("");
+ output.add(header);
+ i++;
+ continue;
+ }
+
+ if (line.equals(EXAMPLE_OPEN)) {
+ // Collect source lines until closing fence
+ output.add(line);
+ i++;
+ var sourceLines = new ArrayList();
+ while (i < lines.size() && !lines.get(i).equals(FENCE)) {
+ sourceLines.add(lines.get(i));
+ output.add(lines.get(i));
+ i++;
+ }
+
+ // Render via GitHub API (→ represents tabs in the spec format)
+ var source = String.join("\n", sourceLines).replace("\u2192", "\t");
+ exampleCount++;
+ System.out.printf("%d: %s%n", exampleCount,
+ source.substring(0, Math.min(40, source.length())).replace("\n", "\\n"));
+
+ var ghHtml = normalizeHtml(renderViaGh(source));
+
+ // Insert separator and HTML expectation
+ output.add(".");
+ output.add(ghHtml);
+ output.add(FENCE);
+ i++; // skip closing fence from template
+ } else {
+ output.add(line);
+ i++;
+ }
+ }
+
+ var specPath = Path.of("alerts-spec.txt");
+ Files.writeString(specPath, String.join("\n", output) + "\n");
+ System.out.println("Done — " + exampleCount + " examples written to alerts-spec.txt");
+ }
+
+ static String renderViaGh(String markdown) throws Exception {
+ var process = new ProcessBuilder("gh", "api", "markdown", "-f", "mode=gfm", "-f", "text=" + markdown)
+ .redirectErrorStream(true)
+ .start();
+ var output = new String(process.getInputStream().readAllBytes());
+ if (process.waitFor() != 0) {
+ throw new RuntimeException("gh api failed: " + output);
+ }
+ return output;
+ }
+
+ // Normalize GitHub API HTML to match our renderer output.
+ static String normalizeHtml(String html) {
+ // Strip GitHub-specific elements and attributes
+ html = Pattern.compile("]*>.*? ", Pattern.DOTALL).matcher(html).replaceAll("");
+ html = html.replaceAll(" (dir=\"auto\"|rel=\"nofollow\"|class=\"notranslate\")", "");
+ // Add data-alert-type and insert newlines to match our renderer's formatting
+ html = Pattern.compile("class=\"markdown-alert markdown-alert-(\\w+)\"")
+ .matcher(html)
+ .replaceAll("class=\"markdown-alert markdown-alert-$1\" data-alert-type=\"$1\"");
+ html = Pattern.compile("(data-alert-type=\"\\w+\">)(
", "
\n");
+ return html.replace("\r\n", "\n").lines()
+ .map(String::stripTrailing)
+ .reduce((a, b) -> a + "\n" + b)
+ .orElse("")
+ .strip();
+ }
+}
\ No newline at end of file
diff --git a/commonmark-ext-gfm-strikethrough/pom.xml b/commonmark-ext-gfm-strikethrough/pom.xml
index 1fd1269bf..9d8f55e5f 100644
--- a/commonmark-ext-gfm-strikethrough/pom.xml
+++ b/commonmark-ext-gfm-strikethrough/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-gfm-strikethrough
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
index 507fc0a88..c497a4db3 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
@@ -3,11 +3,11 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class StrikethroughMarkdownRendererTest {
@@ -30,6 +30,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
index 2ca5471b0..f1199b521 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughSpecTest.java
@@ -7,30 +7,27 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.Set;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class StrikethroughSpecTest extends RenderingTestCase {
private static final Set EXTENSIONS = Set.of(StrikethroughExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
- private final Example example;
+ @Parameter
+ Example example;
- public StrikethroughSpecTest(Example example) {
- this.example = example;
- }
-
- @Parameters(name = "{0}")
- public static List data() {
- return ExampleReader.readExampleObjects(TestResources.getGfmSpec(), "strikethrough");
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getGfmSpec(), "strikethrough");
}
@Test
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
index cb3019957..c29391cdd 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughTest.java
@@ -12,12 +12,12 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class StrikethroughTest extends RenderingTestCase {
@@ -84,14 +84,14 @@ public void insideBlockQuote() {
public void delimited() {
Node document = PARSER.parse("~~foo~~");
Strikethrough strikethrough = (Strikethrough) document.getFirstChild().getFirstChild();
- assertEquals("~~", strikethrough.getOpeningDelimiter());
- assertEquals("~~", strikethrough.getClosingDelimiter());
+ assertThat(strikethrough.getOpeningDelimiter()).isEqualTo("~~");
+ assertThat(strikethrough.getClosingDelimiter()).isEqualTo("~~");
}
@Test
public void textContentRenderer() {
Node document = PARSER.parse("~~foo~~");
- assertEquals("/foo/", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("/foo/");
}
@Test
@@ -104,7 +104,7 @@ public void requireTwoTildesOption() {
.build();
Node document = parser.parse("~foo~ ~~bar~~");
- assertEquals("(sub)foo(/sub) /bar/", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("(sub)foo(/sub) /bar/");
}
@Test
@@ -117,8 +117,7 @@ public void sourceSpans() {
Node document = parser.parse("hey ~~there~~\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node strikethrough = block.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 9)),
- strikethrough.getSourceSpans());
+ assertThat(strikethrough.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 9)));
}
@Override
diff --git a/commonmark-ext-gfm-tables/pom.xml b/commonmark-ext-gfm-tables/pom.xml
index d261a1d24..5bd323168 100644
--- a/commonmark-ext-gfm-tables/pom.xml
+++ b/commonmark-ext-gfm-tables/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-gfm-tables
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
index 0dfdd3159..57af128d8 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java
@@ -274,17 +274,17 @@ public static class Factory extends AbstractBlockParserFactory {
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
List paragraphLines = matchedBlockParser.getParagraphLines().getLines();
- if (paragraphLines.size() == 1 && Characters.find('|', paragraphLines.get(0).getContent(), 0) != -1) {
+ if (paragraphLines.size() >= 1 && Characters.find('|', paragraphLines.get(paragraphLines.size() - 1).getContent(), 0) != -1) {
SourceLine line = state.getLine();
SourceLine separatorLine = line.substring(state.getIndex(), line.getContent().length());
List columns = parseSeparator(separatorLine.getContent());
if (columns != null && !columns.isEmpty()) {
- SourceLine paragraph = paragraphLines.get(0);
+ SourceLine paragraph = paragraphLines.get(paragraphLines.size() - 1);
List headerCells = split(paragraph);
if (columns.size() >= headerCells.size()) {
return BlockStart.of(new TableBlockParser(columns, paragraph))
.atIndex(state.getIndex())
- .replaceActiveBlockParser();
+ .replaceParagraphLines(1);
}
}
}
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
index 16303b58c..85c11206c 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TableMarkdownRendererTest.java
@@ -3,11 +3,11 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class TableMarkdownRendererTest {
@@ -70,6 +70,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
index d5ea8c836..e7f3db4d1 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesSpecTest.java
@@ -7,30 +7,27 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.Set;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class TablesSpecTest extends RenderingTestCase {
private static final Set EXTENSIONS = Set.of(TablesExtension.create());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
- private final Example example;
+ @Parameter
+ Example example;
- public TablesSpecTest(Example example) {
- this.example = example;
- }
-
- @Parameters(name = "{0}")
- public static List data() {
- return ExampleReader.readExampleObjects(TestResources.getGfmSpec(), "table");
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getGfmSpec(), "table");
}
@Test
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
index 57f4a4ae5..3f4b37d54 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTest.java
@@ -1,8 +1,7 @@
package org.commonmark.ext.gfm.tables;
import org.commonmark.Extension;
-import org.commonmark.node.Node;
-import org.commonmark.node.SourceSpan;
+import org.commonmark.node.*;
import org.commonmark.parser.IncludeSourceSpans;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
@@ -10,13 +9,13 @@
import org.commonmark.renderer.html.AttributeProviderFactory;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class TablesTest extends RenderingTestCase {
@@ -79,11 +78,6 @@ public void separatorNeedsPipes() {
assertRendering("Abc|Def\n|--- ---", "Abc|Def\n|--- ---
\n");
}
- @Test
- public void headerMustBeOneLine() {
- assertRendering("No\nAbc|Def\n---|---", "No\nAbc|Def\n---|---
\n");
- }
-
@Test
public void oneHeadNoBody() {
assertRendering("Abc|Def\n---|---", "\n" +
@@ -702,6 +696,26 @@ public void danglingPipe() {
"|
\n");
}
+ @Test
+ public void interruptsParagraph() {
+ assertRendering("text\n" +
+ "|a |\n" +
+ "|---|\n" +
+ "|b |", "text
\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "a \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ "b \n" +
+ " \n" +
+ " \n" +
+ "
\n");
+ }
+
@Test
public void attributeProviderIsApplied() {
AttributeProviderFactory factory = new AttributeProviderFactory() {
@@ -730,7 +744,7 @@ public void setAttributes(Node node, String tagName, Map attribu
.extensions(EXTENSIONS)
.build();
String rendered = renderer.render(PARSER.parse("Abc|Def\n---|---\n1|2"));
- assertThat(rendered, is("\n" +
+ assertThat(rendered).isEqualTo("\n" +
"\n" +
"\n" +
"Abc \n" +
@@ -743,7 +757,7 @@ public void setAttributes(Node node, String tagName, Map attribu
"2 \n" +
" \n" +
"\n" +
- "
\n"));
+ "
\n");
}
@Test
@@ -766,7 +780,7 @@ public void setAttributes(Node node, String tagName, Map attribu
.extensions(EXTENSIONS)
.build();
String rendered = renderer.render(PARSER.parse("Abc|Def\n-----|---\n1|2"));
- assertThat(rendered, is("\n" +
+ assertThat(rendered).isEqualTo("\n" +
"\n" +
"\n" +
"Abc \n" +
@@ -779,7 +793,7 @@ public void setAttributes(Node node, String tagName, Map attribu
"2 \n" +
" \n" +
"\n" +
- "
\n"));
+ "
\n");
}
@Test
@@ -791,49 +805,78 @@ public void sourceSpans() {
Node document = parser.parse("Abc|Def\n---|---\n|1|2\n 3|four|\n|||\n");
TableBlock block = (TableBlock) document.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 7),
- SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)),
- block.getSourceSpans());
+ assertThat(block.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 7),
+ SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)));
TableHead head = (TableHead) block.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 7)), head.getSourceSpans());
+ assertThat(head.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7)));
TableRow headRow = (TableRow) head.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 7)), headRow.getSourceSpans());
+ assertThat(headRow.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7)));
TableCell headRowCell1 = (TableCell) headRow.getFirstChild();
TableCell headRowCell2 = (TableCell) headRow.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 3)), headRowCell1.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 3)), headRowCell1.getFirstChild().getSourceSpans());
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 3)), headRowCell2.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 3)), headRowCell2.getFirstChild().getSourceSpans());
+ assertThat(headRowCell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 3)));
+ assertThat(headRowCell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 3)));
+ assertThat(headRowCell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3)));
+ assertThat(headRowCell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3)));
TableBody body = (TableBody) block.getLastChild();
- assertEquals(List.of(SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)), body.getSourceSpans());
+ assertThat(body.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 16, 4), SourceSpan.of(3, 0, 21, 8), SourceSpan.of(4, 0, 30, 3)));
TableRow bodyRow1 = (TableRow) body.getFirstChild();
- assertEquals(List.of(SourceSpan.of(2, 0, 16, 4)), bodyRow1.getSourceSpans());
+ assertThat(bodyRow1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 0, 16, 4)));
TableCell bodyRow1Cell1 = (TableCell) bodyRow1.getFirstChild();
TableCell bodyRow1Cell2 = (TableCell) bodyRow1.getLastChild();
- assertEquals(List.of(SourceSpan.of(2, 1, 17, 1)), bodyRow1Cell1.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(2, 1, 17, 1)), bodyRow1Cell1.getFirstChild().getSourceSpans());
- assertEquals(List.of(SourceSpan.of(2, 3, 19, 1)), bodyRow1Cell2.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(2, 3, 19, 1)), bodyRow1Cell2.getFirstChild().getSourceSpans());
+ assertThat(bodyRow1Cell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 1, 17, 1)));
+ assertThat(bodyRow1Cell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 1, 17, 1)));
+ assertThat(bodyRow1Cell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 3, 19, 1)));
+ assertThat(bodyRow1Cell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(2, 3, 19, 1)));
TableRow bodyRow2 = (TableRow) body.getFirstChild().getNext();
- assertEquals(List.of(SourceSpan.of(3, 0, 21, 8)), bodyRow2.getSourceSpans());
+ assertThat(bodyRow2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 0, 21, 8)));
TableCell bodyRow2Cell1 = (TableCell) bodyRow2.getFirstChild();
TableCell bodyRow2Cell2 = (TableCell) bodyRow2.getLastChild();
- assertEquals(List.of(SourceSpan.of(3, 1, 22, 1)), bodyRow2Cell1.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(3, 1, 22, 1)), bodyRow2Cell1.getFirstChild().getSourceSpans());
- assertEquals(List.of(SourceSpan.of(3, 3, 24, 4)), bodyRow2Cell2.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(3, 3, 24, 4)), bodyRow2Cell2.getFirstChild().getSourceSpans());
+ assertThat(bodyRow2Cell1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 1, 22, 1)));
+ assertThat(bodyRow2Cell1.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 1, 22, 1)));
+ assertThat(bodyRow2Cell2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 3, 24, 4)));
+ assertThat(bodyRow2Cell2.getFirstChild().getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 3, 24, 4)));
TableRow bodyRow3 = (TableRow) body.getLastChild();
- assertEquals(List.of(SourceSpan.of(4, 0, 30, 3)), bodyRow3.getSourceSpans());
+ assertThat(bodyRow3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(4, 0, 30, 3)));
TableCell bodyRow3Cell1 = (TableCell) bodyRow3.getFirstChild();
TableCell bodyRow3Cell2 = (TableCell) bodyRow3.getLastChild();
- assertEquals(List.of(), bodyRow3Cell1.getSourceSpans());
- assertEquals(List.of(), bodyRow3Cell2.getSourceSpans());
+ assertThat(bodyRow3Cell1.getSourceSpans()).isEqualTo(List.of());
+ assertThat(bodyRow3Cell2.getSourceSpans()).isEqualTo(List.of());
+ }
+
+ @Test
+ public void sourceSpansWhenInterrupting() {
+ var parser = Parser.builder()
+ .extensions(EXTENSIONS)
+ .includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES)
+ .build();
+ var document = parser.parse("a\n" +
+ "bc\n" +
+ "|de|\n" +
+ "|---|\n" +
+ "|fg|");
+
+ var paragraph = (Paragraph) document.getFirstChild();
+ var text = (Text) paragraph.getFirstChild();
+ assertThat(text.getLiteral()).isEqualTo("a");
+ assertThat(text.getNext()).isInstanceOf(SoftLineBreak.class);
+ var text2 = (Text) text.getNext().getNext();
+ assertThat(text2.getLiteral()).isEqualTo("bc");
+
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(
+ SourceSpan.of(0, 0, 0, 1),
+ SourceSpan.of(1, 0, 2, 2)));
+
+ var table = (TableBlock) document.getLastChild();
+ assertThat(table.getSourceSpans()).isEqualTo(List.of(
+ SourceSpan.of(2, 0, 5, 4),
+ SourceSpan.of(3, 0, 10, 5),
+ SourceSpan.of(4, 0, 16, 4)));
}
@Override
diff --git a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
index c5ef8cb5a..966f097fd 100644
--- a/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
+++ b/commonmark-ext-gfm-tables/src/test/java/org/commonmark/ext/gfm/tables/TablesTextContentTest.java
@@ -5,7 +5,7 @@
import org.commonmark.renderer.text.LineBreakRendering;
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.testutil.Asserts;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
diff --git a/commonmark-ext-heading-anchor/pom.xml b/commonmark-ext-heading-anchor/pom.xml
index e4a62b2b3..26d2d19b1 100644
--- a/commonmark-ext-heading-anchor/pom.xml
+++ b/commonmark-ext-heading-anchor/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-heading-anchor
diff --git a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
index af2ae13cf..438a3a9bd 100644
--- a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
+++ b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorConfigurationTest.java
@@ -3,12 +3,11 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
public class HeadingAnchorConfigurationTest {
@@ -30,25 +29,25 @@ public void testDefaultConfigurationHasNoAdditions() {
HtmlRenderer renderer = HtmlRenderer.builder()
.extensions(List.of(HeadingAnchorExtension.create()))
.build();
- assertThat(doRender(renderer, "# "), equalTo(" \n"));
+ assertThat(doRender(renderer, "# ")).isEqualTo(" \n");
}
@Test
public void testDefaultIdWhenNoTextOnHeader() {
HtmlRenderer renderer = buildRenderer("defid", "", "");
- assertThat(doRender(renderer, "# "), equalTo(" \n"));
+ assertThat(doRender(renderer, "# ")).isEqualTo(" \n");
}
@Test
public void testPrefixAddedToHeader() {
HtmlRenderer renderer = buildRenderer("", "pre-", "");
- assertThat(doRender(renderer, "# text"), equalTo("text \n"));
+ assertThat(doRender(renderer, "# text")).isEqualTo("text \n");
}
@Test
public void testSuffixAddedToHeader() {
HtmlRenderer renderer = buildRenderer("", "", "-post");
- assertThat(doRender(renderer, "# text"), equalTo("text \n"));
+ assertThat(doRender(renderer, "# text")).isEqualTo("text \n");
}
private String doRender(HtmlRenderer renderer, String text) {
diff --git a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
index 872306bed..3149542e3 100644
--- a/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
+++ b/commonmark-ext-heading-anchor/src/test/java/org/commonmark/ext/heading/anchor/HeadingAnchorTest.java
@@ -4,7 +4,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
diff --git a/commonmark-ext-image-attributes/pom.xml b/commonmark-ext-image-attributes/pom.xml
index 55b0dce20..e646bc3fd 100644
--- a/commonmark-ext-image-attributes/pom.xml
+++ b/commonmark-ext-image-attributes/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-image-attributes
diff --git a/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java b/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
index b863bd4b7..3edf8497e 100644
--- a/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
+++ b/commonmark-ext-image-attributes/src/test/java/org/commonmark/ext/image/attributes/ImageAttributesTest.java
@@ -8,12 +8,12 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class ImageAttributesTest extends RenderingTestCase {
@@ -131,8 +131,7 @@ public void sourceSpans() {
Node document = parser.parse("x{height=3 width=4}\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node text = block.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 19)),
- text.getSourceSpans());
+ assertThat(text.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 19)));
}
@Override
diff --git a/commonmark-ext-ins/pom.xml b/commonmark-ext-ins/pom.xml
index 9dbe2cf06..48481c073 100644
--- a/commonmark-ext-ins/pom.xml
+++ b/commonmark-ext-ins/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-ins
diff --git a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
index 28f2cd354..6fc9ead67 100644
--- a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
+++ b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsMarkdownRendererTest.java
@@ -3,11 +3,11 @@
import org.commonmark.Extension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class InsMarkdownRendererTest {
@@ -28,6 +28,6 @@ protected String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
index 4603da60b..a5c91a395 100644
--- a/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
+++ b/commonmark-ext-ins/src/test/java/org/commonmark/ext/ins/InsTest.java
@@ -9,12 +9,12 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class InsTest extends RenderingTestCase {
@@ -82,14 +82,14 @@ public void insideBlockQuote() {
public void delimited() {
Node document = PARSER.parse("++foo++");
Ins ins = (Ins) document.getFirstChild().getFirstChild();
- assertEquals("++", ins.getOpeningDelimiter());
- assertEquals("++", ins.getClosingDelimiter());
+ assertThat(ins.getOpeningDelimiter()).isEqualTo("++");
+ assertThat(ins.getClosingDelimiter()).isEqualTo("++");
}
@Test
public void textContentRenderer() {
Node document = PARSER.parse("++foo++");
- assertEquals("foo", CONTENT_RENDERER.render(document));
+ assertThat(CONTENT_RENDERER.render(document)).isEqualTo("foo");
}
@Test
@@ -102,8 +102,7 @@ public void sourceSpans() {
Node document = parser.parse("hey ++there++\n");
Paragraph block = (Paragraph) document.getFirstChild();
Node ins = block.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 9)),
- ins.getSourceSpans());
+ assertThat(ins.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 9)));
}
@Override
diff --git a/commonmark-ext-task-list-items/pom.xml b/commonmark-ext-task-list-items/pom.xml
index 3926c8b5e..4359f8707 100644
--- a/commonmark-ext-task-list-items/pom.xml
+++ b/commonmark-ext-task-list-items/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-task-list-items
diff --git a/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java b/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
index c13e10bb7..0adc615a7 100644
--- a/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
+++ b/commonmark-ext-task-list-items/src/test/java/org/commonmark/ext/task/list/items/TaskListItemsTest.java
@@ -4,7 +4,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
diff --git a/commonmark-ext-yaml-front-matter/pom.xml b/commonmark-ext-yaml-front-matter/pom.xml
index 9e329e0d3..e6822f771 100644
--- a/commonmark-ext-yaml-front-matter/pom.xml
+++ b/commonmark-ext-yaml-front-matter/pom.xml
@@ -4,7 +4,7 @@
commonmark-parent
org.commonmark
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-ext-yaml-front-matter
diff --git a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
index f46c11b3c..db17d4a4e 100644
--- a/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
+++ b/commonmark-ext-yaml-front-matter/src/test/java/org/commonmark/ext/front/matter/YamlFrontMatterTest.java
@@ -6,14 +6,13 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
public class YamlFrontMatterTest extends RenderingTestCase {
private static final Set EXTENSIONS = Set.of(YamlFrontMatterExtension.create());
@@ -31,10 +30,10 @@ public void simpleValue() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("hello", data.keySet().iterator().next());
- assertEquals(1, data.get("hello").size());
- assertEquals("world", data.get("hello").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("hello");
+ assertThat(data.get("hello")).hasSize(1);
+ assertThat(data.get("hello").get(0)).isEqualTo("world");
assertRendering(input, rendered);
}
@@ -50,9 +49,9 @@ public void emptyValue() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("key", data.keySet().iterator().next());
- assertEquals(0, data.get("key").size());
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("key");
+ assertThat(data.get("key")).hasSize(0);
assertRendering(input, rendered);
}
@@ -70,11 +69,11 @@ public void listValues() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("list"));
- assertEquals(2, data.get("list").size());
- assertEquals("value1", data.get("list").get(0));
- assertEquals("value2", data.get("list").get(1));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("list");
+ assertThat(data.get("list")).hasSize(2);
+ assertThat(data.get("list").get(0)).isEqualTo("value1");
+ assertThat(data.get("list").get(1)).isEqualTo("value2");
assertRendering(input, rendered);
}
@@ -92,10 +91,10 @@ public void literalValue1() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("hello markdown!\nliteral thing...", data.get("literal").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("hello markdown!\nliteral thing...");
assertRendering(input, rendered);
}
@@ -112,10 +111,10 @@ public void literalValue2() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("- hello markdown!", data.get("literal").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("- hello markdown!");
assertRendering(input, rendered);
}
@@ -137,20 +136,20 @@ public void complexValues() {
Map> data = getFrontMatter(input);
- assertEquals(3, data.size());
+ assertThat(data).hasSize(3);
- assertTrue(data.containsKey("simple"));
- assertEquals(1, data.get("simple").size());
- assertEquals("value", data.get("simple").get(0));
+ assertThat(data).containsKey("simple");
+ assertThat(data.get("simple")).hasSize(1);
+ assertThat(data.get("simple").get(0)).isEqualTo("value");
- assertTrue(data.containsKey("literal"));
- assertEquals(1, data.get("literal").size());
- assertEquals("hello markdown!\n\nliteral literal", data.get("literal").get(0));
+ assertThat(data).containsKey("literal");
+ assertThat(data.get("literal")).hasSize(1);
+ assertThat(data.get("literal").get(0)).isEqualTo("hello markdown!\n\nliteral literal");
- assertTrue(data.containsKey("list"));
- assertEquals(2, data.get("list").size());
- assertEquals("value1", data.get("list").get(0));
- assertEquals("value2", data.get("list").get(1));
+ assertThat(data).containsKey("list");
+ assertThat(data.get("list")).hasSize(2);
+ assertThat(data.get("list").get(0)).isEqualTo("value1");
+ assertThat(data.get("list").get(1)).isEqualTo("value2");
assertRendering(input, rendered);
}
@@ -164,7 +163,7 @@ public void empty() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -180,7 +179,7 @@ public void yamlInParagraph() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -195,7 +194,7 @@ public void yamlOnSecondLine() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -208,7 +207,7 @@ public void nonMatchedStartTag() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -222,7 +221,7 @@ public void inList() {
Map> data = getFrontMatter(input);
- assertTrue(data.isEmpty());
+ assertThat(data).isEmpty();
assertRendering(input, rendered);
}
@@ -240,9 +239,9 @@ public void visitorIgnoresOtherCustomNodes() {
document.accept(visitor);
Map> data = visitor.getData();
- assertEquals(1, data.size());
- assertTrue(data.containsKey("hello"));
- assertEquals(List.of("world"), data.get("hello"));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("hello");
+ assertThat(data.get("hello")).isEqualTo(List.of("world"));
}
@Test
@@ -261,9 +260,9 @@ public void nodesCanBeModified() {
document.accept(visitor);
Map> data = visitor.getData();
- assertEquals(1, data.size());
- assertTrue(data.containsKey("see"));
- assertEquals(List.of("you"), data.get("see"));
+ assertThat(data).hasSize(1);
+ assertThat(data).containsKey("see");
+ assertThat(data.get("see")).isEqualTo(List.of("you"));
}
@Test
@@ -275,10 +274,10 @@ public void dotInKeys() {
Map> data = getFrontMatter(input);
- assertEquals(1, data.size());
- assertEquals("ms.author", data.keySet().iterator().next());
- assertEquals(1, data.get("ms.author").size());
- assertEquals("author", data.get("ms.author").get(0));
+ assertThat(data).hasSize(1);
+ assertThat(data.keySet().iterator().next()).isEqualTo("ms.author");
+ assertThat(data.get("ms.author")).hasSize(1);
+ assertThat(data.get("ms.author").get(0)).isEqualTo("author");
}
@Test
@@ -292,9 +291,9 @@ public void singleQuotedLiterals() {
Map> data = getFrontMatter(input);
- assertEquals(2, data.size());
- assertEquals("It's me", data.get("string").get(0));
- assertEquals("I'm here", data.get("list").get(0));
+ assertThat(data).hasSize(2);
+ assertThat(data.get("string").get(0)).isEqualTo("It's me");
+ assertThat(data.get("list").get(0)).isEqualTo("I'm here");
}
@Test
@@ -308,9 +307,9 @@ public void doubleQuotedLiteral() {
Map> data = getFrontMatter(input);
- assertEquals(2, data.size());
- assertEquals("backslash: \\ quote: \"", data.get("string").get(0));
- assertEquals("hey", data.get("list").get(0));
+ assertThat(data).hasSize(2);
+ assertThat(data.get("string").get(0)).isEqualTo("backslash: \\ quote: \"");
+ assertThat(data.get("list").get(0)).isEqualTo("hey");
}
@Override
diff --git a/commonmark-integration-test/pom.xml b/commonmark-integration-test/pom.xml
index c5e572291..7e0048a73 100644
--- a/commonmark-integration-test/pom.xml
+++ b/commonmark-integration-test/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-integration-test
@@ -20,10 +20,18 @@
org.commonmark
commonmark-ext-autolink
+
+ org.commonmark
+ commonmark-ext-footnotes
+
org.commonmark
commonmark-ext-ins
+
+ org.commonmark
+ commonmark-ext-gfm-alerts
+
org.commonmark
commonmark-ext-gfm-strikethrough
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
index 8ee15164a..f1259b825 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/BoundsIntegrationTest.java
@@ -3,39 +3,30 @@
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.testutil.TestResources;
-import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.util.ArrayList;
import java.util.List;
-import static org.junit.Assert.assertNotNull;
+import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests various substrings of the spec examples to check for out of bounds exceptions.
*/
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class BoundsIntegrationTest {
private static final Parser PARSER = Parser.builder().build();
- protected final String input;
+ @Parameter
+ String input;
- public BoundsIntegrationTest(String input) {
- this.input = input;
- }
-
- @Parameterized.Parameters(name = "{0}")
- public static List data() {
- List examples = ExampleReader.readExamples(TestResources.getSpec());
- List data = new ArrayList<>();
- for (Example example : examples) {
- data.add(new Object[]{example.getSource()});
- }
- return data;
+ static List data() {
+ return ExampleReader.readExampleSources(TestResources.getSpec());
}
@Test
@@ -54,7 +45,7 @@ private void parse(String input) {
try {
Node parsed = PARSER.parse(input);
// Parsing should always return a node
- assertNotNull(parsed);
+ assertThat(parsed).isNotNull();
} catch (Exception e) {
throw new AssertionError("Parsing failed, input: " + input, e);
}
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
index ee7ee5290..9090c797f 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/Extensions.java
@@ -2,7 +2,9 @@
import org.commonmark.Extension;
import org.commonmark.ext.autolink.AutolinkExtension;
+import org.commonmark.ext.footnotes.FootnotesExtension;
import org.commonmark.ext.front.matter.YamlFrontMatterExtension;
+import org.commonmark.ext.gfm.alerts.AlertsExtension;
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.image.attributes.ImageAttributesExtension;
@@ -15,8 +17,10 @@ public class Extensions {
static final List ALL_EXTENSIONS = List.of(
AutolinkExtension.create(),
+ FootnotesExtension.create(),
ImageAttributesExtension.create(),
InsExtension.create(),
+ AlertsExtension.create(),
StrikethroughExtension.create(),
TablesExtension.create(),
TaskListItemsExtension.create(),
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
index 64cf60ed9..523154d2c 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/ExtensionsIntegrationTest.java
@@ -3,7 +3,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
/**
* Tests to ensure all extensions work well together.
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
index f05efe1c3..fe14273ab 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/MarkdownRendererIntegrationTest.java
@@ -10,24 +10,16 @@
import org.commonmark.ext.task.list.items.TaskListItemsExtension;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class MarkdownRendererIntegrationTest {
- private static final List EXTENSIONS = List.of(
- AutolinkExtension.create(),
- ImageAttributesExtension.create(),
- InsExtension.create(),
- StrikethroughExtension.create(),
- TablesExtension.create(),
- TaskListItemsExtension.create(),
- YamlFrontMatterExtension.create());
- private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
- private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(EXTENSIONS).build();
+ private static final Parser PARSER = Parser.builder().extensions(Extensions.ALL_EXTENSIONS).build();
+ private static final MarkdownRenderer RENDERER = MarkdownRenderer.builder().extensions(Extensions.ALL_EXTENSIONS).build();
@Test
public void testStrikethroughInTable() {
@@ -40,6 +32,6 @@ private String render(String source) {
private void assertRoundTrip(String input) {
String rendered = render(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
}
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
index a0649f537..171cc51b1 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/SourceSpanIntegrationTest.java
@@ -14,10 +14,6 @@ public class SourceSpanIntegrationTest extends SpecIntegrationTest {
.includeSourceSpans(IncludeSourceSpans.BLOCKS)
.build();
- public SourceSpanIntegrationTest(Example example) {
- super(example);
- }
-
@Override
protected String render(String source) {
return RENDERER.render(PARSER.parse(source));
diff --git a/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java b/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
index 2b615aa41..07853d402 100644
--- a/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
+++ b/commonmark-integration-test/src/test/java/org/commonmark/integration/SpecIntegrationTest.java
@@ -4,7 +4,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.SpecTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.*;
@@ -20,10 +20,6 @@ public class SpecIntegrationTest extends SpecTestCase {
protected static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(Extensions.ALL_EXTENSIONS).percentEncodeUrls(true).build();
protected static final Map OVERRIDDEN_EXAMPLES = getOverriddenExamples();
- public SpecIntegrationTest(Example example) {
- super(example);
- }
-
@Test
public void testHtmlRendering() {
String expectedHtml = OVERRIDDEN_EXAMPLES.get(example.getSource());
diff --git a/commonmark-test-util/pom.xml b/commonmark-test-util/pom.xml
index 7fb4881f1..6a9c342cc 100644
--- a/commonmark-test-util/pom.xml
+++ b/commonmark-test-util/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-test-util
@@ -13,8 +13,12 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupiter
+
+
+ org.assertj
+ assertj-core
diff --git a/commonmark-test-util/src/main/java/module-info.java b/commonmark-test-util/src/main/java/module-info.java
index ef983a513..12980d80a 100644
--- a/commonmark-test-util/src/main/java/module-info.java
+++ b/commonmark-test-util/src/main/java/module-info.java
@@ -2,5 +2,6 @@
exports org.commonmark.testutil;
exports org.commonmark.testutil.example;
- requires junit;
+ requires org.assertj.core;
+ requires org.junit.jupiter.params;
}
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
index 64124b129..971a1b4ea 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/Asserts.java
@@ -1,13 +1,13 @@
package org.commonmark.testutil;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class Asserts {
public static void assertRendering(String source, String expectedRendering, String actualRendering) {
// include source for better assertion errors
String expected = showTabs(expectedRendering + "\n\n" + source);
String actual = showTabs(actualRendering + "\n\n" + source);
- assertEquals(expected, actual);
+ assertThat(actual).isEqualTo(expected);
}
private static String showTabs(String s) {
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
index b585f4604..f7da4c008 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/RenderingTestCase.java
@@ -1,5 +1,7 @@
package org.commonmark.testutil;
+import static org.assertj.core.api.Assertions.assertThat;
+
public abstract class RenderingTestCase {
protected abstract String render(String source);
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
index 2fb175772..c29a6a69a 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/SpecTestCase.java
@@ -2,30 +2,22 @@
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import java.util.ArrayList;
import java.util.List;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public abstract class SpecTestCase {
- protected final Example example;
+ @Parameter
+ protected Example example;
- public SpecTestCase(Example example) {
- this.example = example;
+ static List data() {
+ return ExampleReader.readExamples(TestResources.getSpec());
}
-
- @Parameters(name = "{0}")
- public static List data() {
- List examples = ExampleReader.readExamples(TestResources.getSpec());
- List data = new ArrayList<>();
- for (Example example : examples) {
- data.add(new Object[]{example});
- }
- return data;
- }
-
}
diff --git a/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java b/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
index 6f5dd6276..d40a10f63 100644
--- a/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
+++ b/commonmark-test-util/src/main/java/org/commonmark/testutil/example/ExampleReader.java
@@ -7,6 +7,7 @@
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Reader for files containing examples of CommonMark source and the expected HTML rendering (e.g. spec.txt).
@@ -42,15 +43,13 @@ public static List readExamples(URL url) {
}
}
+ public static List readExamples(URL url, String info) {
+ var examples = readExamples(url);
+ return examples.stream().filter(e -> e.getInfo().contains(info)).collect(Collectors.toList());
+ }
+
public static List readExampleObjects(URL url, String info) {
- List examples = readExamples(url);
- List data = new ArrayList<>();
- for (Example example : examples) {
- if (example.getInfo().contains(info)) {
- data.add(new Object[]{example});
- }
- }
- return data;
+ return readExamples(url, info).stream().map(e -> new Object[]{e}).collect(Collectors.toList());
}
public static List readExampleSources(URL url) {
diff --git a/commonmark/pom.xml b/commonmark/pom.xml
index 17f3bc747..4e060edaa 100644
--- a/commonmark/pom.xml
+++ b/commonmark/pom.xml
@@ -4,7 +4,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark
diff --git a/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java b/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
index c7e967d46..516f944b2 100644
--- a/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/BlockStartImpl.java
@@ -9,6 +9,7 @@ public class BlockStartImpl extends BlockStart {
private int newIndex = -1;
private int newColumn = -1;
private boolean replaceActiveBlockParser = false;
+ private int replaceParagraphLines = 0;
public BlockStartImpl(BlockParser... blockParsers) {
this.blockParsers = blockParsers;
@@ -30,6 +31,10 @@ public boolean isReplaceActiveBlockParser() {
return replaceActiveBlockParser;
}
+ int getReplaceParagraphLines() {
+ return replaceParagraphLines;
+ }
+
@Override
public BlockStart atIndex(int newIndex) {
this.newIndex = newIndex;
@@ -48,4 +53,12 @@ public BlockStart replaceActiveBlockParser() {
return this;
}
+ @Override
+ public BlockStart replaceParagraphLines(int lines) {
+ if (!(lines >= 1)) {
+ throw new IllegalArgumentException("Lines must be >= 1");
+ }
+ this.replaceParagraphLines = lines;
+ return this;
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
index 6059cc51c..07d97296b 100644
--- a/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/DocumentParser.java
@@ -76,6 +76,7 @@ public class DocumentParser implements ParserState {
private final List linkProcessors;
private final Set linkMarkers;
private final IncludeSourceSpans includeSourceSpans;
+ private final int maxOpenBlockParsers;
private final DocumentBlockParser documentBlockParser;
private final Definitions definitions = new Definitions();
@@ -84,7 +85,8 @@ public class DocumentParser implements ParserState {
public DocumentParser(List blockParserFactories, InlineParserFactory inlineParserFactory,
List inlineContentParserFactories, List delimiterProcessors,
- List linkProcessors, Set linkMarkers, IncludeSourceSpans includeSourceSpans) {
+ List linkProcessors, Set linkMarkers,
+ IncludeSourceSpans includeSourceSpans, int maxOpenBlockParsers) {
this.blockParserFactories = blockParserFactories;
this.inlineParserFactory = inlineParserFactory;
this.inlineContentParserFactories = inlineContentParserFactories;
@@ -92,6 +94,7 @@ public DocumentParser(List blockParserFactories, InlineParse
this.linkProcessors = linkProcessors;
this.linkMarkers = linkMarkers;
this.includeSourceSpans = includeSourceSpans;
+ this.maxOpenBlockParsers = maxOpenBlockParsers;
this.documentBlockParser = new DocumentBlockParser();
activateBlockParser(new OpenBlockParser(documentBlockParser, 0));
@@ -270,9 +273,15 @@ private void parseLine(String ln, int inputIndex) {
}
List replacedSourceSpans = null;
- if (blockStart.isReplaceActiveBlockParser()) {
- Block replacedBlock = prepareActiveBlockParserForReplacement();
- replacedSourceSpans = replacedBlock.getSourceSpans();
+ if (blockStart.getReplaceParagraphLines() >= 1 || blockStart.isReplaceActiveBlockParser()) {
+ var activeBlockParser = getActiveBlockParser();
+ if (activeBlockParser instanceof ParagraphParser) {
+ var paragraphParser = (ParagraphParser) activeBlockParser;
+ var lines = blockStart.isReplaceActiveBlockParser() ? Integer.MAX_VALUE : blockStart.getReplaceParagraphLines();
+ replacedSourceSpans = replaceParagraphLines(lines, paragraphParser);
+ } else if (blockStart.isReplaceActiveBlockParser()) {
+ replacedSourceSpans = prepareActiveBlockParserForReplacement(activeBlockParser);
+ }
}
for (BlockParser newBlockParser : blockStart.getBlockParsers()) {
@@ -455,6 +464,9 @@ private void addSourceSpans() {
}
private BlockStartImpl findBlockStart(BlockParser blockParser) {
+ if (openBlockParsers.size() > maxOpenBlockParsers) {
+ return null;
+ }
MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser);
for (BlockParserFactory blockParserFactory : blockParserFactories) {
BlockStart result = blockParserFactory.tryStart(this, matchedBlockParser);
@@ -498,24 +510,23 @@ private OpenBlockParser deactivateBlockParser() {
return openBlockParsers.remove(openBlockParsers.size() - 1);
}
- private Block prepareActiveBlockParserForReplacement() {
- // Note that we don't want to parse inlines, as it's getting replaced.
- BlockParser old = deactivateBlockParser().blockParser;
+ private List replaceParagraphLines(int lines, ParagraphParser paragraphParser) {
+ // Remove lines from paragraph as the new block is using them.
+ // If all lines are used, this also unlinks the Paragraph block.
+ var sourceSpans = paragraphParser.removeLines(lines);
+ // Close the paragraph block parser, which will finalize it.
+ closeBlockParsers(1);
+ return sourceSpans;
+ }
- if (old instanceof ParagraphParser) {
- ParagraphParser paragraphParser = (ParagraphParser) old;
- // Collect any link reference definitions. Note that replacing the active block parser is done after a
- // block parser got the current paragraph content using MatchedBlockParser#getContentString. In case the
- // paragraph started with link reference definitions, we parse and strip them before the block parser gets
- // the content. We want to keep them.
- // If no replacement happens, we collect the definitions as part of finalizing blocks.
- addDefinitionsFrom(paragraphParser);
- }
+ private List prepareActiveBlockParserForReplacement(BlockParser blockParser) {
+ // Note that we don't want to parse inlines here, as it's getting replaced.
+ deactivateBlockParser();
// Do this so that source positions are calculated, which we will carry over to the replacing block.
- old.closeBlock();
- old.getBlock().unlink();
- return old.getBlock();
+ blockParser.closeBlock();
+ blockParser.getBlock().unlink();
+ return blockParser.getBlock().getSourceSpans();
}
private Document finalizeAndProcess() {
diff --git a/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java b/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
index 3bc9ba5c4..05f070137 100644
--- a/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/HeadingParser.java
@@ -60,7 +60,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar
if (!paragraph.isEmpty()) {
return BlockStart.of(new HeadingParser(setextHeadingLevel, paragraph))
.atIndex(line.getContent().length())
- .replaceActiveBlockParser();
+ .replaceParagraphLines(paragraph.getLines().size());
}
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
index 44f153e00..44422f421 100644
--- a/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
+++ b/commonmark/src/main/java/org/commonmark/internal/InlineParserImpl.java
@@ -383,14 +383,13 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) {
// - Collapsed: `[foo][]` (foo is both the text and label)
// - Shortcut: `[foo]` (foo is both the text and label)
- String text = scanner.getSource(opener.contentPosition, beforeClose).getContent();
-
// Starting position is after the closing `]`
- Position afterClose = scanner.position();
+ var afterClose = scanner.position();
// Maybe an inline link/image
var destinationTitle = parseInlineDestinationTitle(scanner);
if (destinationTitle != null) {
+ var text = scanner.getSource(opener.contentPosition, beforeClose).getContent();
return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, null, destinationTitle.destination, destinationTitle.title, afterClose);
}
// Not an inline link/image, rewind back to after `]`.
@@ -401,7 +400,7 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) {
// failed to be parsed as an inline link/image before.
// See if there's a link label like `[bar]` or `[]`
- String label = parseLinkLabel(scanner);
+ var label = parseLinkLabel(scanner);
if (label == null) {
// No label, rewind back
scanner.setPosition(afterClose);
@@ -413,6 +412,7 @@ private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) {
return null;
}
+ var text = scanner.getSource(opener.contentPosition, beforeClose).getContent();
return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, label, null, null, afterClose);
}
@@ -440,16 +440,9 @@ private Node wrapBracket(Bracket opener, Node wrapperNode, boolean includeMarker
opener.bracketNode.unlink();
removeLastBracket();
- // Links within links are not allowed. We found this link, so there can be no other link around it.
+ // Links within links are not allowed. We found this link, so there can be no other links around it.
if (opener.markerNode == null) {
- Bracket bracket = lastBracket;
- while (bracket != null) {
- if (bracket.markerNode == null) {
- // Disallow link opener. It will still get matched, but will not result in a link.
- bracket.allowed = false;
- }
- bracket = bracket.previous;
- }
+ disallowPreviousLinks();
}
return wrapperNode;
@@ -475,6 +468,15 @@ private Node replaceBracket(Bracket opener, Node node, boolean includeMarker) {
n.unlink();
n = next;
}
+
+ // Links within links are not allowed. We found this link, so there can be no other links around it.
+ // Note that this makes any syntax like `[foo]` behave the same as built-in links, which is probably a good
+ // default (it works for footnotes). It might be useful for a `LinkProcessor` to be able to specify the
+ // behavior; something we could add to `LinkResult` in the future if requested.
+ if (opener.markerNode == null || !includeMarker) {
+ disallowPreviousLinks();
+ }
+
return node;
}
@@ -489,6 +491,17 @@ private void removeLastBracket() {
lastBracket = lastBracket.previous;
}
+ private void disallowPreviousLinks() {
+ Bracket bracket = lastBracket;
+ while (bracket != null) {
+ if (bracket.markerNode == null) {
+ // Disallow link opener. It will still get matched, but will not result in a link.
+ bracket.allowed = false;
+ }
+ bracket = bracket.previous;
+ }
+ }
+
/**
* Try to parse the destination and an optional title for an inline link/image.
*/
@@ -585,7 +598,9 @@ static String parseLinkLabel(Scanner scanner) {
private Node parseLineBreak() {
scanner.next();
- if (trailingSpaces >= 2) {
+ var hard = trailingSpaces >= 2;
+ trailingSpaces = 0;
+ if (hard) {
return new HardLineBreak();
} else {
return new SoftLineBreak();
diff --git a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
index b58e669ef..637d3b111 100644
--- a/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/LinkReferenceDefinitionParser.java
@@ -10,6 +10,7 @@
import org.commonmark.parser.beta.Scanner;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -102,6 +103,14 @@ State getState() {
return state;
}
+ List removeLines(int lines) {
+ var removedSpans = Collections.unmodifiableList(new ArrayList<>(
+ sourceSpans.subList(Math.max(sourceSpans.size() - lines, 0), sourceSpans.size())));
+ removeLast(lines, paragraphLines);
+ removeLast(lines, sourceSpans);
+ return removedSpans;
+ }
+
private boolean startDefinition(Scanner scanner) {
// Finish any outstanding references now. We don't do this earlier because we need addSourceSpan to have been
// called before we do it.
@@ -269,6 +278,16 @@ private void finishReference() {
title = null;
}
+ private static void removeLast(int n, List list) {
+ if (n >= list.size()) {
+ list.clear();
+ } else {
+ for (int i = 0; i < n; i++) {
+ list.remove(list.size() - 1);
+ }
+ }
+ }
+
enum State {
// Looking for the start of a definition, i.e. `[`
START_DEFINITION,
diff --git a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
index 93a2dd593..27eb1e647 100644
--- a/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
+++ b/commonmark/src/main/java/org/commonmark/internal/ParagraphParser.java
@@ -79,4 +79,8 @@ public void parseInlines(InlineParser inlineParser) {
public SourceLines getParagraphLines() {
return linkReferenceDefinitionParser.getParagraphLines();
}
+
+ public List removeLines(int lines) {
+ return linkReferenceDefinitionParser.removeLines(lines);
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java
deleted file mode 100644
index a9271dcdb..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/BulletListHolder.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-import org.commonmark.node.BulletList;
-
-public class BulletListHolder extends ListHolder {
- private final String marker;
-
- public BulletListHolder(ListHolder parent, BulletList list) {
- super(parent);
- marker = list.getMarker();
- }
-
- public String getMarker() {
- return marker;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java
deleted file mode 100644
index cb06d4a9d..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/ListHolder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-public abstract class ListHolder {
- private static final String INDENT_DEFAULT = " ";
- private static final String INDENT_EMPTY = "";
-
- private final ListHolder parent;
- private final String indent;
-
- ListHolder(ListHolder parent) {
- this.parent = parent;
-
- if (parent != null) {
- indent = parent.indent + INDENT_DEFAULT;
- } else {
- indent = INDENT_EMPTY;
- }
- }
-
- public ListHolder getParent() {
- return parent;
- }
-
- public String getIndent() {
- return indent;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java b/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java
deleted file mode 100644
index e5e470951..000000000
--- a/commonmark/src/main/java/org/commonmark/internal/renderer/text/OrderedListHolder.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.commonmark.internal.renderer.text;
-
-import org.commonmark.node.OrderedList;
-
-public class OrderedListHolder extends ListHolder {
- private final String delimiter;
- private int counter;
-
- public OrderedListHolder(ListHolder parent, OrderedList list) {
- super(parent);
- delimiter = list.getMarkerDelimiter() != null ? list.getMarkerDelimiter() : ".";
- counter = list.getMarkerStartNumber() != null ? list.getMarkerStartNumber() : 1;
- }
-
- public String getDelimiter() {
- return delimiter;
- }
-
- public int getCounter() {
- return counter;
- }
-
- public void increaseCounter() {
- counter++;
- }
-}
diff --git a/commonmark/src/main/java/org/commonmark/node/ListItem.java b/commonmark/src/main/java/org/commonmark/node/ListItem.java
index e488e8fbe..c4d1214e7 100644
--- a/commonmark/src/main/java/org/commonmark/node/ListItem.java
+++ b/commonmark/src/main/java/org/commonmark/node/ListItem.java
@@ -2,6 +2,10 @@
/**
* A child of a {@link ListBlock}, containing other blocks (e.g. {@link Paragraph}, other lists, etc).
+ *
+ * Note that a list item can't directly contain {@link Text}, it needs to be:
+ * {@link ListItem} : {@link Paragraph} : {@link Text}.
+ * If you want a list that is rendered tightly, create a list with {@link ListBlock#setTight(boolean)}.
*
* @see CommonMark Spec: List items
*/
@@ -57,4 +61,18 @@ public Integer getContentIndent() {
public void setContentIndent(Integer contentIndent) {
this.contentIndent = contentIndent;
}
+
+ /**
+ * @deprecated list items should only contain block nodes; if you're trying to create a list that is rendered
+ * without paragraphs, use {@link ListBlock#setTight(boolean)} instead.
+ */
+ @Override
+ @Deprecated
+ public void appendChild(Node child) {
+ super.appendChild(child);
+ }
+
+ public void appendChild(Block child) {
+ super.appendChild(child);
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/parser/Parser.java b/commonmark/src/main/java/org/commonmark/parser/Parser.java
index b98d0581f..8faac789b 100644
--- a/commonmark/src/main/java/org/commonmark/parser/Parser.java
+++ b/commonmark/src/main/java/org/commonmark/parser/Parser.java
@@ -37,6 +37,7 @@ public class Parser {
private final InlineParserFactory inlineParserFactory;
private final List postProcessors;
private final IncludeSourceSpans includeSourceSpans;
+ private final int maxOpenBlockParsers;
private Parser(Builder builder) {
this.blockParserFactories = DocumentParser.calculateBlockParserFactories(builder.blockParserFactories, builder.enabledBlockTypes);
@@ -47,6 +48,7 @@ private Parser(Builder builder) {
this.linkProcessors = builder.linkProcessors;
this.linkMarkers = builder.linkMarkers;
this.includeSourceSpans = builder.includeSourceSpans;
+ this.maxOpenBlockParsers = builder.maxOpenBlockParsers;
// Try to construct an inline parser. Invalid configuration might result in an exception, which we want to
// detect as soon as possible.
@@ -106,7 +108,7 @@ public Node parseReader(Reader input) throws IOException {
private DocumentParser createDocumentParser() {
return new DocumentParser(blockParserFactories, inlineParserFactory, inlineContentParserFactories,
- delimiterProcessors, linkProcessors, linkMarkers, includeSourceSpans);
+ delimiterProcessors, linkProcessors, linkMarkers, includeSourceSpans, maxOpenBlockParsers);
}
private Node postProcess(Node document) {
@@ -129,6 +131,7 @@ public static class Builder {
private Set> enabledBlockTypes = DocumentParser.getDefaultBlockParserTypes();
private InlineParserFactory inlineParserFactory;
private IncludeSourceSpans includeSourceSpans = IncludeSourceSpans.NONE;
+ private int maxOpenBlockParsers = Integer.MAX_VALUE;
/**
* @return the configured {@link Parser}
@@ -200,6 +203,27 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) {
return this;
}
+ /**
+ * Limit how many block parsers may be open at once while parsing.
+ *
+ * Once the limit is reached, additional block starts are treated as plain text instead of
+ * creating deeper nested block structure.
+ *
+ * The document root parser is not counted. The default is unlimited, so callers that keep
+ * using {@code Parser.builder().build()} preserve behavior.
+ *
+ * @param maxOpenBlockParsers maximum number of open non-document block parsers, must be
+ * zero or greater
+ * @return {@code this}
+ */
+ public Builder maxOpenBlockParsers(int maxOpenBlockParsers) {
+ if (maxOpenBlockParsers < 0) {
+ throw new IllegalArgumentException("maxOpenBlockParsers must be >= 0");
+ }
+ this.maxOpenBlockParsers = maxOpenBlockParsers;
+ return this;
+ }
+
/**
* Add a custom block parser factory.
*
diff --git a/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java b/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java
index d9e7a2b49..c41f1caa3 100644
--- a/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java
+++ b/commonmark/src/main/java/org/commonmark/parser/block/BlockStart.java
@@ -10,18 +10,59 @@ public abstract class BlockStart {
protected BlockStart() {
}
+ /**
+ * Result for when there is no block start.
+ */
public static BlockStart none() {
return null;
}
+ /**
+ * Start block(s) with the specified parser(s).
+ */
public static BlockStart of(BlockParser... blockParsers) {
return new BlockStartImpl(blockParsers);
}
+ /**
+ * Continue parsing at the specified index.
+ *
+ * @param newIndex the new index, see {@link ParserState#getIndex()}
+ */
public abstract BlockStart atIndex(int newIndex);
+ /**
+ * Continue parsing at the specified column (for tab handling).
+ *
+ * @param newColumn the new column, see {@link ParserState#getColumn()}
+ */
public abstract BlockStart atColumn(int newColumn);
+ /**
+ * @deprecated use {@link #replaceParagraphLines(int)} instead; please raise an issue if that doesn't work for you
+ * for some reason.
+ */
+ @Deprecated
public abstract BlockStart replaceActiveBlockParser();
+ /**
+ * Replace a number of lines from the current paragraph (as returned by
+ * {@link MatchedBlockParser#getParagraphLines()}) with the new block.
+ *
+ * This is useful for parsing blocks that start with normal paragraphs and only have special marker syntax in later
+ * lines, e.g. in this:
+ *
+ * Foo
+ * ===
+ *
+ * The Foo line is initially parsed as a normal paragraph, then === is parsed as a heading
+ * marker, replacing the 1 paragraph line before. The end result is a single Heading block.
+ *
+ * Note that source spans from the replaced lines are automatically added to the new block.
+ *
+ * @param lines the number of lines to replace (at least 1); use {@link Integer#MAX_VALUE} to replace the whole
+ * paragraph
+ */
+ public abstract BlockStart replaceParagraphLines(int lines);
+
}
diff --git a/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java b/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java
index 1f2bcfb2a..c4619d8c2 100644
--- a/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java
+++ b/commonmark/src/main/java/org/commonmark/parser/block/MatchedBlockParser.java
@@ -12,7 +12,8 @@ public interface MatchedBlockParser {
BlockParser getMatchedBlockParser();
/**
- * Returns the current paragraph lines if the matched block is a paragraph.
+ * Returns the current paragraph lines if the matched block is a paragraph. If you want to use some or all of the
+ * lines for starting a new block instead, use {@link BlockStart#replaceParagraphLines(int)}.
*
* @return paragraph content or an empty list
*/
diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java
index 0603aa013..5c536558e 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java
@@ -311,6 +311,11 @@ public void visit(Text text) {
sb.append(text.getLiteral());
}
+ @Override
+ public void visit(Code code) {
+ sb.append(code.getLiteral());
+ }
+
@Override
public void visit(SoftLineBreak softLineBreak) {
sb.append('\n');
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
index 32510feaf..5a81676f4 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
@@ -257,7 +257,7 @@ public void visit(ListItem listItem) {
throw new IllegalStateException("Unknown list holder type: " + listHolder);
}
Integer contentIndent = listItem.getContentIndent();
- String spaces = contentIndent != null ? repeat(" ", contentIndent - marker.length()) : " ";
+ String spaces = contentIndent != null ? repeat(" ", Math.max(contentIndent - marker.length(), 1)) : " ";
writer.writePrefix(marker);
writer.writePrefix(spaces);
writer.pushPrefix(repeat(" ", marker.length() + spaces.length()));
diff --git a/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java
index 68b1fbce5..ee564cbdb 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java
@@ -1,8 +1,5 @@
package org.commonmark.renderer.text;
-import org.commonmark.internal.renderer.text.BulletListHolder;
-import org.commonmark.internal.renderer.text.ListHolder;
-import org.commonmark.internal.renderer.text.OrderedListHolder;
import org.commonmark.node.*;
import org.commonmark.renderer.NodeRenderer;
@@ -161,19 +158,30 @@ public void visit(Link link) {
@Override
public void visit(ListItem listItem) {
if (listHolder != null && listHolder instanceof OrderedListHolder) {
- OrderedListHolder orderedListHolder = (OrderedListHolder) listHolder;
- String indent = stripNewlines() ? "" : orderedListHolder.getIndent();
- textContent.write(indent + orderedListHolder.getCounter() + orderedListHolder.getDelimiter() + " ");
+ var orderedListHolder = (OrderedListHolder) listHolder;
+ var marker = orderedListHolder.getCounter() + orderedListHolder.getDelimiter();
+ var spaces = " ";
+ textContent.write(marker);
+ textContent.write(spaces);
+ textContent.pushPrefix(repeat(" ", marker.length() + spaces.length()));
visitChildren(listItem);
textContent.block();
+ textContent.popPrefix();
orderedListHolder.increaseCounter();
} else if (listHolder != null && listHolder instanceof BulletListHolder) {
BulletListHolder bulletListHolder = (BulletListHolder) listHolder;
if (!stripNewlines()) {
- textContent.write(bulletListHolder.getIndent() + bulletListHolder.getMarker() + " ");
+ var marker = bulletListHolder.getMarker();
+ var spaces = " ";
+ textContent.write(marker);
+ textContent.write(spaces);
+ textContent.pushPrefix(repeat(" ", marker.length() + spaces.length()));
}
visitChildren(listItem);
textContent.block();
+ if (!stripNewlines()) {
+ textContent.popPrefix();
+ }
}
}
@@ -268,4 +276,61 @@ private static String stripTrailingNewline(String s) {
return s;
}
}
+
+ // Keep for Android compat (String.repeat only available on Android 12 and later)
+ private static String repeat(String s, int count) {
+ var sb = new StringBuilder(s.length() * count);
+ for (int i = 0; i < count; i++) {
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ private static class BulletListHolder extends ListHolder {
+ private final String marker;
+
+ public BulletListHolder(ListHolder parent, BulletList list) {
+ super(parent);
+ marker = list.getMarker();
+ }
+
+ public String getMarker() {
+ return marker;
+ }
+ }
+
+ private abstract static class ListHolder {
+ private final ListHolder parent;
+
+ ListHolder(ListHolder parent) {
+ this.parent = parent;
+ }
+
+ public ListHolder getParent() {
+ return parent;
+ }
+ }
+
+ private static class OrderedListHolder extends ListHolder {
+ private final String delimiter;
+ private int counter;
+
+ public OrderedListHolder(ListHolder parent, OrderedList list) {
+ super(parent);
+ delimiter = list.getMarkerDelimiter() != null ? list.getMarkerDelimiter() : ".";
+ counter = list.getMarkerStartNumber() != null ? list.getMarkerStartNumber() : 1;
+ }
+
+ public String getDelimiter() {
+ return delimiter;
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+
+ public void increaseCounter() {
+ counter++;
+ }
+ }
}
diff --git a/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java b/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java
index 2b9f35070..1fb482785 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/text/TextContentWriter.java
@@ -8,6 +8,7 @@ public class TextContentWriter {
private final Appendable buffer;
private final LineBreakRendering lineBreakRendering;
+ private final LinkedList prefixes = new LinkedList<>();
private final LinkedList tight = new LinkedList<>();
private String blockSeparator = null;
@@ -36,6 +37,7 @@ public void colon() {
public void line() {
append('\n');
+ writePrefixes();
}
public void block() {
@@ -61,6 +63,32 @@ public void write(char c) {
append(c);
}
+ /**
+ * Push a prefix onto the top of the stack. All prefixes are written at the beginning of each line, until the
+ * prefix is popped again.
+ *
+ * @param prefix the raw prefix string
+ */
+ public void pushPrefix(String prefix) {
+ prefixes.addLast(prefix);
+ }
+
+ /**
+ * Write a prefix.
+ *
+ * @param prefix the raw prefix string to write
+ */
+ public void writePrefix(String prefix) {
+ write(prefix);
+ }
+
+ /**
+ * Remove the last prefix from the top of the stack.
+ */
+ public void popPrefix() {
+ prefixes.removeLast();
+ }
+
/**
* Change whether blocks are tight or loose. Loose is the default where blocks are separated by a blank line. Tight
* is where blocks are not separated by a blank line. Tight blocks are used in lists, if there are no blank lines
@@ -84,12 +112,26 @@ private boolean isTight() {
return !tight.isEmpty() && tight.getLast();
}
+ private void writePrefixes() {
+ for (String prefix : prefixes) {
+ append(prefix);
+ }
+ }
+
/**
* If a block separator has been enqueued with {@link #block()} but not yet written, write it now.
*/
private void flushBlockSeparator() {
if (blockSeparator != null) {
- append(blockSeparator);
+ if (blockSeparator.equals("\n") || blockSeparator.equals("\n\n")) {
+ for (int i = 0; i < blockSeparator.length(); i++) {
+ var sep = blockSeparator.charAt(i);
+ append(sep);
+ writePrefixes();
+ }
+ } else {
+ append(blockSeparator);
+ }
blockSeparator = null;
}
}
diff --git a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java
index 45143b852..a834665ff 100644
--- a/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/internal/DocumentParserTest.java
@@ -2,17 +2,15 @@
import org.commonmark.node.*;
import org.commonmark.parser.block.BlockParserFactory;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
-public class DocumentParserTest {
+class DocumentParserTest {
private static final List CORE_FACTORIES = List.of(
new BlockQuoteParser.Factory(),
new HeadingParser.Factory(),
@@ -23,28 +21,28 @@ public class DocumentParserTest {
new IndentedCodeBlockParser.Factory());
@Test
- public void calculateBlockParserFactories_givenAFullListOfAllowedNodes_includesAllCoreFactories() {
+ void calculateBlockParserFactories_givenAFullListOfAllowedNodes_includesAllCoreFactories() {
List customParserFactories = List.of();
var enabledBlockTypes = Set.of(BlockQuote.class, Heading.class, FencedCodeBlock.class, HtmlBlock.class, ThematicBreak.class, ListBlock.class, IndentedCodeBlock.class);
List blockParserFactories = DocumentParser.calculateBlockParserFactories(customParserFactories, enabledBlockTypes);
- assertThat(blockParserFactories.size(), is(CORE_FACTORIES.size()));
+ assertThat(blockParserFactories).hasSameSizeAs(CORE_FACTORIES);
for (BlockParserFactory factory : CORE_FACTORIES) {
- assertTrue(hasInstance(blockParserFactories, factory.getClass()));
+ assertThat(hasInstance(blockParserFactories, factory.getClass())).isTrue();
}
}
@Test
- public void calculateBlockParserFactories_givenAListOfAllowedNodes_includesAssociatedFactories() {
+ void calculateBlockParserFactories_givenAListOfAllowedNodes_includesAssociatedFactories() {
List customParserFactories = List.of();
Set> nodes = new HashSet<>();
nodes.add(IndentedCodeBlock.class);
List blockParserFactories = DocumentParser.calculateBlockParserFactories(customParserFactories, nodes);
- assertThat(blockParserFactories.size(), is(1));
- assertTrue(hasInstance(blockParserFactories, IndentedCodeBlockParser.Factory.class));
+ assertThat(blockParserFactories).hasSize(1);
+ assertThat(hasInstance(blockParserFactories, IndentedCodeBlockParser.Factory.class)).isTrue();
}
private boolean hasInstance(List blockParserFactories, Class extends BlockParserFactory> factoryClass) {
diff --git a/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java b/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java
index 3f22adac6..b69ada0e9 100644
--- a/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/internal/LinkReferenceDefinitionParserTest.java
@@ -3,82 +3,82 @@
import org.commonmark.internal.LinkReferenceDefinitionParser.State;
import org.commonmark.node.LinkReferenceDefinition;
import org.commonmark.parser.SourceLine;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
-public class LinkReferenceDefinitionParserTest {
+class LinkReferenceDefinitionParserTest {
private final LinkReferenceDefinitionParser parser = new LinkReferenceDefinitionParser();
@Test
- public void testStartLabel() {
+ void testStartLabel() {
assertState("[", State.LABEL, "[");
}
@Test
- public void testStartNoLabel() {
+ void testStartNoLabel() {
// Not a label
assertParagraph("a");
// Can not go back to parsing link reference definitions
parse("a");
parse("[");
- assertEquals(State.PARAGRAPH, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.PARAGRAPH);
assertParagraphLines("a\n[", parser);
}
@Test
- public void testEmptyLabel() {
+ void testEmptyLabel() {
assertParagraph("[]: /");
assertParagraph("[ ]: /");
assertParagraph("[ \t\n\u000B\f\r ]: /");
}
@Test
- public void testLabelColon() {
+ void testLabelColon() {
assertParagraph("[foo] : /");
}
@Test
- public void testLabel() {
+ void testLabel() {
assertState("[foo]:", State.DESTINATION, "[foo]:");
assertState("[ foo ]:", State.DESTINATION, "[ foo ]:");
}
@Test
- public void testLabelInvalid() {
+ void testLabelInvalid() {
assertParagraph("[foo[]:");
}
@Test
- public void testLabelMultiline() {
+ void testLabelMultiline() {
parse("[two");
- assertEquals(State.LABEL, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.LABEL);
parse("lines]:");
- assertEquals(State.DESTINATION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.DESTINATION);
parse("/url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
assertDef(parser.getDefinitions().get(0), "two\nlines", "/url", null);
}
@Test
- public void testLabelStartsWithNewline() {
+ void testLabelStartsWithNewline() {
parse("[");
- assertEquals(State.LABEL, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.LABEL);
parse("weird]:");
- assertEquals(State.DESTINATION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.DESTINATION);
parse("/url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
assertDef(parser.getDefinitions().get(0), "\nweird", "/url", null);
}
@Test
- public void testDestination() {
+ void testDestination() {
parse("[foo]: /url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
assertParagraphLines("", parser);
- assertEquals(1, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).hasSize(1);
assertDef(parser.getDefinitions().get(0), "foo", "/url", null);
parse("[bar]: ");
@@ -86,91 +86,91 @@ public void testDestination() {
}
@Test
- public void testDestinationInvalid() {
+ void testDestinationInvalid() {
assertParagraph("[foo]: ");
}
@Test
- public void testTitle() {
+ void testTitle() {
parse("[foo]: /url 'title'");
- assertEquals(State.START_DEFINITION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_DEFINITION);
assertParagraphLines("", parser);
- assertEquals(1, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).hasSize(1);
assertDef(parser.getDefinitions().get(0), "foo", "/url", "title");
}
@Test
- public void testTitleStartWhitespace() {
+ void testTitleStartWhitespace() {
parse("[foo]: /url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
assertParagraphLines("", parser);
parse(" ");
- assertEquals(State.START_DEFINITION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_DEFINITION);
assertParagraphLines(" ", parser);
- assertEquals(1, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).hasSize(1);
assertDef(parser.getDefinitions().get(0), "foo", "/url", null);
}
@Test
- public void testTitleMultiline() {
+ void testTitleMultiline() {
parse("[foo]: /url 'two");
- assertEquals(State.TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.TITLE);
assertParagraphLines("[foo]: /url 'two", parser);
- assertEquals(0, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).isEmpty();
parse("lines");
- assertEquals(State.TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.TITLE);
assertParagraphLines("[foo]: /url 'two\nlines", parser);
- assertEquals(0, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).isEmpty();
parse("'");
- assertEquals(State.START_DEFINITION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_DEFINITION);
assertParagraphLines("", parser);
- assertEquals(1, parser.getDefinitions().size());
+ assertThat(parser.getDefinitions()).hasSize(1);
assertDef(parser.getDefinitions().get(0), "foo", "/url", "two\nlines\n");
}
@Test
- public void testTitleMultiline2() {
+ void testTitleMultiline2() {
parse("[foo]: /url '");
- assertEquals(State.TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.TITLE);
parse("title'");
- assertEquals(State.START_DEFINITION, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_DEFINITION);
assertDef(parser.getDefinitions().get(0), "foo", "/url", "\ntitle");
}
@Test
- public void testTitleMultiline3() {
+ void testTitleMultiline3() {
parse("[foo]: /url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
// Note that this looks like a valid title until we parse "bad", at which point we need to treat the whole line
// as a paragraph line and discard any already parsed title.
parse("\"title\" bad");
- assertEquals(State.PARAGRAPH, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.PARAGRAPH);
assertDef(parser.getDefinitions().get(0), "foo", "/url", null);
}
@Test
- public void testTitleMultiline4() {
+ void testTitleMultiline4() {
parse("[foo]: /url");
- assertEquals(State.START_TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.START_TITLE);
parse("(title");
- assertEquals(State.TITLE, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.TITLE);
parse("foo(");
- assertEquals(State.PARAGRAPH, parser.getState());
+ assertThat(parser.getState()).isEqualTo(State.PARAGRAPH);
assertDef(parser.getDefinitions().get(0), "foo", "/url", null);
}
@Test
- public void testTitleInvalid() {
+ void testTitleInvalid() {
assertParagraph("[foo]: /url (invalid(");
assertParagraph("[foo]: 'title'");
assertParagraph("[foo]: /url 'title' INVALID");
@@ -188,18 +188,18 @@ private static void assertState(String input, State state, String paragraphConte
LinkReferenceDefinitionParser parser = new LinkReferenceDefinitionParser();
// TODO: Should we check things with source spans here?
parser.parse(SourceLine.of(input, null));
- assertEquals(state, parser.getState());
+ assertThat(parser.getState()).isEqualTo(state);
assertParagraphLines(paragraphContent, parser);
}
private static void assertDef(LinkReferenceDefinition def, String label, String destination, String title) {
- assertEquals(label, def.getLabel());
- assertEquals(destination, def.getDestination());
- assertEquals(title, def.getTitle());
+ assertThat(def.getLabel()).isEqualTo(label);
+ assertThat(def.getDestination()).isEqualTo(destination);
+ assertThat(def.getTitle()).isEqualTo(title);
}
private static void assertParagraphLines(String expectedContent, LinkReferenceDefinitionParser parser) {
String actual = parser.getParagraphLines().getContent();
- assertEquals(expectedContent, actual);
+ assertThat(actual).isEqualTo(expectedContent);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java b/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java
index 9433eb7d0..eb2f1a801 100644
--- a/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java
+++ b/commonmark/src/test/java/org/commonmark/internal/util/EscapingTest.java
@@ -1,21 +1,21 @@
package org.commonmark.internal.util;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
-public class EscapingTest {
+class EscapingTest {
@Test
- public void testEscapeHtml() {
- assertEquals("nothing to escape", Escaping.escapeHtml("nothing to escape"));
- assertEquals("&", Escaping.escapeHtml("&"));
- assertEquals("<", Escaping.escapeHtml("<"));
- assertEquals(">", Escaping.escapeHtml(">"));
- assertEquals(""", Escaping.escapeHtml("\""));
- assertEquals("< start", Escaping.escapeHtml("< start"));
- assertEquals("end >", Escaping.escapeHtml("end >"));
- assertEquals("< both >", Escaping.escapeHtml("< both >"));
- assertEquals("< middle & too >", Escaping.escapeHtml("< middle & too >"));
+ void testEscapeHtml() {
+ assertThat(Escaping.escapeHtml("nothing to escape")).isEqualTo("nothing to escape");
+ assertThat(Escaping.escapeHtml("&")).isEqualTo("&");
+ assertThat(Escaping.escapeHtml("<")).isEqualTo("<");
+ assertThat(Escaping.escapeHtml(">")).isEqualTo(">");
+ assertThat(Escaping.escapeHtml("\"")).isEqualTo(""");
+ assertThat(Escaping.escapeHtml("< start")).isEqualTo("< start");
+ assertThat(Escaping.escapeHtml("end >")).isEqualTo("end >");
+ assertThat(Escaping.escapeHtml("< both >")).isEqualTo("< both >");
+ assertThat(Escaping.escapeHtml("< middle & too >")).isEqualTo("< middle & too >");
}
}
diff --git a/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java b/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java
index fc48623a8..b52713846 100644
--- a/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java
+++ b/commonmark/src/test/java/org/commonmark/internal/util/LineReaderTest.java
@@ -1,6 +1,6 @@
package org.commonmark.internal.util;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -9,13 +9,14 @@
import java.util.Objects;
import static java.util.stream.Collectors.joining;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.commonmark.internal.util.LineReader.CHAR_BUFFER_SIZE;
-import static org.junit.Assert.*;
-public class LineReaderTest {
+class LineReaderTest {
@Test
- public void testReadLine() throws IOException {
+ void testReadLine() throws IOException {
assertLines();
assertLines("", "\n");
@@ -48,21 +49,16 @@ public void testReadLine() throws IOException {
}
@Test
- public void testClose() throws IOException {
+ void testClose() throws IOException {
var reader = new InputStreamReader(new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8)));
var lineReader = new LineReader(reader);
lineReader.close();
lineReader.close();
- try {
- reader.read();
- fail("Expected read to throw after closing reader");
- } catch (IOException e) {
- // Expected
- }
+ assertThatThrownBy(reader::read).isInstanceOf(IOException.class);
}
private void assertLines(String... s) throws IOException {
- assertTrue("Expected parts needs to be even (pairs of content and terminator)", s.length % 2 == 0);
+ assertThat(s.length).as("Expected parts needs to be even (pairs of content and terminator)").isEven();
var input = Arrays.stream(s).filter(Objects::nonNull).collect(joining(""));
assertLines(new StringReader(input), s);
@@ -77,8 +73,8 @@ private static void assertLines(Reader reader, String... expectedParts) throws I
lines.add(line);
lines.add(lineReader.getLineTerminator());
}
- assertNull(lineReader.getLineTerminator());
- assertEquals(Arrays.asList(expectedParts), lines);
+ assertThat(lineReader.getLineTerminator()).isNull();
+ assertThat(lines).containsExactly(expectedParts);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java b/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java
index ed97b6f31..d0f45a6bc 100644
--- a/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/parser/InlineContentParserTest.java
@@ -1,53 +1,51 @@
package org.commonmark.parser;
-import org.commonmark.node.CustomNode;
-import org.commonmark.node.Heading;
-import org.commonmark.node.Image;
-import org.commonmark.node.Text;
+import org.commonmark.node.*;
import org.commonmark.parser.beta.InlineContentParser;
import org.commonmark.parser.beta.InlineContentParserFactory;
import org.commonmark.parser.beta.InlineParserState;
import org.commonmark.parser.beta.ParsedInline;
import org.commonmark.test.Nodes;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
-public class InlineContentParserTest {
+class InlineContentParserTest {
@Test
- public void customInlineContentParser() {
+ void customInlineContentParser() {
var parser = Parser.builder().customInlineContentParserFactory(new DollarInlineParser.Factory()).build();
var doc = parser.parse("Test: $hey *there*$ $you$\n\n# Heading $heading$\n");
var inline1 = Nodes.find(doc, DollarInline.class);
- assertEquals("hey *there*", inline1.getLiteral());
+ assertThat(inline1.getLiteral()).isEqualTo("hey *there*");
var inline2 = (DollarInline) doc.getFirstChild().getLastChild();
- assertEquals("you", inline2.getLiteral());
+ assertThat(inline2.getLiteral()).isEqualTo("you");
var heading = Nodes.find(doc, Heading.class);
var inline3 = (DollarInline) heading.getLastChild();
- assertEquals("heading", inline3.getLiteral());
+ assertThat(inline3.getLiteral()).isEqualTo("heading");
// Parser is created for each inline snippet, which is why the index resets for the second snippet.
- assertEquals(0, inline1.getIndex());
- assertEquals(1, inline2.getIndex());
- assertEquals(0, inline3.getIndex());
+ assertThat(inline1.getIndex()).isEqualTo(0);
+ assertThat(inline2.getIndex()).isEqualTo(1);
+ assertThat(inline3.getIndex()).isEqualTo(0);
}
@Test
- public void bangInlineContentParser() {
+ void bangInlineContentParser() {
// See if using ! for a custom inline content parser works.
// ![] is used for images, but if it's not followed by a [, it should be possible to parse it differently.
var parser = Parser.builder().customInlineContentParserFactory(new BangInlineParser.Factory()).build();
var doc = parser.parse(" !notimage");
var image = Nodes.find(doc, Image.class);
- assertEquals("url", image.getDestination());
- assertEquals(" ", ((Text) image.getNext()).getLiteral());
- assertEquals(BangInline.class, image.getNext().getNext().getClass());
- assertEquals("notimage", ((Text) image.getNext().getNext().getNext()).getLiteral());
+ assertThat(image.getDestination()).isEqualTo("url");
+ assertThat(((Text) image.getNext()).getLiteral()).isEqualTo(" ");
+ // Class
+ assertThat(image.getNext().getNext()).isInstanceOf(BangInline.class);
+ assertThat(((Text) image.getNext().getNext().getNext()).getLiteral()).isEqualTo("notimage");
}
private static class DollarInline extends CustomNode {
diff --git a/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java b/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java
index 6209ac9a0..ef8739128 100644
--- a/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java
+++ b/commonmark/src/test/java/org/commonmark/parser/beta/LinkProcessorTest.java
@@ -4,13 +4,14 @@
import org.commonmark.node.Text;
import org.commonmark.parser.Parser;
import org.commonmark.test.Nodes;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LinkProcessorTest {
-public class LinkProcessorTest {
@Test
- public void testLinkMarkerShouldNotBeIncludedByDefault() {
+ void testLinkMarkerShouldNotBeIncludedByDefault() {
// If a link marker is registered but is not processed, the built-in link processor shouldn't consume it.
// And I think by default, other processors shouldn't consume it either (by accident).
// So requiring processors to opt into including the marker is better than requiring them to opt out,
@@ -19,7 +20,7 @@ public void testLinkMarkerShouldNotBeIncludedByDefault() {
var parser = Parser.builder().linkMarker('^').build();
var doc = parser.parse("^[test](url)");
var link = Nodes.find(doc, Link.class);
- assertEquals("url", link.getDestination());
- assertEquals("^", ((Text) link.getPrevious()).getLiteral());
+ assertThat(link.getDestination()).isEqualTo("url");
+ assertThat(((Text) link.getPrevious()).getLiteral()).isEqualTo("^");
}
}
diff --git a/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java b/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java
index 14c118ab9..bd74cab0e 100644
--- a/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java
+++ b/commonmark/src/test/java/org/commonmark/parser/beta/ScannerTest.java
@@ -3,87 +3,87 @@
import org.commonmark.node.SourceSpan;
import org.commonmark.parser.SourceLine;
import org.commonmark.parser.SourceLines;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
-import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
-public class ScannerTest {
+class ScannerTest {
@Test
- public void testNext() {
+ void testNext() {
Scanner scanner = new Scanner(List.of(
SourceLine.of("foo bar", null)),
0, 4);
- assertEquals('b', scanner.peek());
+ assertThat(scanner.peek()).isEqualTo('b');
scanner.next();
- assertEquals('a', scanner.peek());
+ assertThat(scanner.peek()).isEqualTo('a');
scanner.next();
- assertEquals('r', scanner.peek());
+ assertThat(scanner.peek()).isEqualTo('r');
scanner.next();
- assertEquals('\0', scanner.peek());
+ assertThat(scanner.peek()).isEqualTo('\0');
}
@Test
- public void testMultipleLines() {
+ void testMultipleLines() {
Scanner scanner = new Scanner(List.of(
SourceLine.of("ab", null),
SourceLine.of("cde", null)),
0, 0);
- assertTrue(scanner.hasNext());
- assertEquals('\0', scanner.peekPreviousCodePoint());
- assertEquals('a', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\0');
+ assertThat(scanner.peek()).isEqualTo('a');
scanner.next();
- assertTrue(scanner.hasNext());
- assertEquals('a', scanner.peekPreviousCodePoint());
- assertEquals('b', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('a');
+ assertThat(scanner.peek()).isEqualTo('b');
scanner.next();
- assertTrue(scanner.hasNext());
- assertEquals('b', scanner.peekPreviousCodePoint());
- assertEquals('\n', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('b');
+ assertThat(scanner.peek()).isEqualTo('\n');
scanner.next();
- assertTrue(scanner.hasNext());
- assertEquals('\n', scanner.peekPreviousCodePoint());
- assertEquals('c', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\n');
+ assertThat(scanner.peek()).isEqualTo('c');
scanner.next();
- assertTrue(scanner.hasNext());
- assertEquals('c', scanner.peekPreviousCodePoint());
- assertEquals('d', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('c');
+ assertThat(scanner.peek()).isEqualTo('d');
scanner.next();
- assertTrue(scanner.hasNext());
- assertEquals('d', scanner.peekPreviousCodePoint());
- assertEquals('e', scanner.peek());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('d');
+ assertThat(scanner.peek()).isEqualTo('e');
scanner.next();
- assertFalse(scanner.hasNext());
- assertEquals('e', scanner.peekPreviousCodePoint());
- assertEquals('\0', scanner.peek());
+ assertThat(scanner.hasNext()).isFalse();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('e');
+ assertThat(scanner.peek()).isEqualTo('\0');
}
@Test
- public void testCodePoints() {
+ void testCodePoints() {
Scanner scanner = new Scanner(List.of(SourceLine.of("\uD83D\uDE0A", null)), 0, 0);
- assertTrue(scanner.hasNext());
- assertEquals('\0', scanner.peekPreviousCodePoint());
- assertEquals(128522, scanner.peekCodePoint());
+ assertThat(scanner.hasNext()).isTrue();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo('\0');
+ assertThat(scanner.peekCodePoint()).isEqualTo(128522);
scanner.next();
// This jumps chars, not code points. So jump two here
scanner.next();
- assertFalse(scanner.hasNext());
- assertEquals(128522, scanner.peekPreviousCodePoint());
- assertEquals('\0', scanner.peekCodePoint());
+ assertThat(scanner.hasNext()).isFalse();
+ assertThat(scanner.peekPreviousCodePoint()).isEqualTo(128522);
+ assertThat(scanner.peekCodePoint()).isEqualTo('\0');
}
@Test
- public void testTextBetween() {
+ void testTextBetween() {
Scanner scanner = new Scanner(List.of(
SourceLine.of("ab", SourceSpan.of(10, 3, 13, 2)),
SourceLine.of("cde", SourceSpan.of(11, 4, 20, 3))),
@@ -139,20 +139,20 @@ public void testTextBetween() {
}
private void assertSourceLines(SourceLines sourceLines, String expectedContent, SourceSpan... expectedSourceSpans) {
- assertEquals(expectedContent, sourceLines.getContent());
- assertEquals(List.of(expectedSourceSpans), sourceLines.getSourceSpans());
+ assertThat(sourceLines.getContent()).isEqualTo(expectedContent);
+ assertThat(sourceLines.getSourceSpans()).isEqualTo(List.of(expectedSourceSpans));
}
@Test
- public void nextString() {
+ void nextString() {
Scanner scanner = Scanner.of(SourceLines.of(List.of(
SourceLine.of("hey ya", null),
SourceLine.of("hi", null))));
- assertFalse(scanner.next("hoy"));
- assertTrue(scanner.next("hey"));
- assertTrue(scanner.next(' '));
- assertFalse(scanner.next("yo"));
- assertTrue(scanner.next("ya"));
- assertFalse(scanner.next(" "));
+ assertThat(scanner.next("hoy")).isFalse();
+ assertThat(scanner.next("hey")).isTrue();
+ assertThat(scanner.next(' ')).isTrue();
+ assertThat(scanner.next("yo")).isFalse();
+ assertThat(scanner.next("ya")).isTrue();
+ assertThat(scanner.next(" ")).isFalse();
}
}
diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java
index 36a5b528c..6a468a08e 100644
--- a/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java
+++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/MarkdownRendererTest.java
@@ -3,15 +3,12 @@
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.NodeRenderer;
-import org.commonmark.renderer.html.HtmlNodeRendererContext;
-import org.commonmark.renderer.html.HtmlNodeRendererFactory;
-import org.commonmark.renderer.html.HtmlRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.commonmark.testutil.Asserts.assertRendering;
-import static org.junit.Assert.assertEquals;
public class MarkdownRendererTest {
@@ -31,7 +28,7 @@ public void testThematicBreaks() {
// Apply fallback for null literal
ThematicBreak node = new ThematicBreak();
- assertEquals("___", render(node));
+ assertThat(render(node)).isEqualTo("___");
}
@Test
@@ -179,6 +176,13 @@ public void testOrderedListItemsFromAst() {
assertRendering("", "2) Test\n", render(doc));
}
+ @Test
+ public void testOrderedListItemsWithStartNumberLongerThanLaterNumber() {
+ var source = "10001.\n20.\n";
+ var doc = parse(source);
+ assertRendering(source, "10001. \n10002. \n", render(doc));
+ }
+
// Inlines
@Test
@@ -255,7 +259,7 @@ public void testEmphasis() {
Emphasis e2 = new Emphasis();
e1.appendChild(e2);
e2.appendChild(new Text("hi"));
- assertEquals("*_hi_*\n", render(doc));
+ assertThat(render(doc)).isEqualTo("*_hi_*\n");
}
@Test
@@ -332,12 +336,12 @@ public Set getSpecialCharacters() {
MarkdownRenderer renderer = MarkdownRenderer.builder().nodeRendererFactory(nodeRendererFactory).build();
String rendered = renderer.render(parse("# Hello"));
- assertEquals("# Custom heading\n", rendered);
+ assertThat(rendered).isEqualTo("# Custom heading\n");
}
private void assertRoundTrip(String input) {
String rendered = parseAndRender(input);
- assertEquals(input, rendered);
+ assertThat(rendered).isEqualTo(input);
}
private String parseAndRender(String source) {
diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
index 5df2e5c80..3b88df55d 100644
--- a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
+++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
@@ -6,15 +6,14 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests Markdown rendering using the examples in the spec like this:
@@ -63,9 +62,8 @@ public void testCoverage() {
System.out.println();
}
- int expectedPassed = 652;
- assertTrue("Expected at least " + expectedPassed + " examples to pass but was " + passes.size(), passes.size() >= expectedPassed);
- assertEquals(0, fails.size());
+ assertThat(passes).hasSizeGreaterThanOrEqualTo(652);
+ assertThat(fails).isEmpty();
}
private static void printCountsBySection(List examples) {
diff --git a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java
index b3b60fa3b..edb6936f4 100644
--- a/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/AbstractVisitorTest.java
@@ -1,10 +1,9 @@
package org.commonmark.test;
import org.commonmark.node.*;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.assertj.core.api.Assertions.assertThat;
public class AbstractVisitorTest {
@@ -26,13 +25,13 @@ public void visit(Text text) {
assertCode("foo", paragraph.getFirstChild());
assertCode("bar", paragraph.getFirstChild().getNext());
- assertNull(paragraph.getFirstChild().getNext().getNext());
+ assertThat(paragraph.getFirstChild().getNext().getNext()).isNull();
assertCode("bar", paragraph.getLastChild());
}
private static void assertCode(String expectedLiteral, Node node) {
- assertEquals("Expected node to be a Code node: " + node, Code.class, node.getClass());
+ assertThat(node).isInstanceOf(Code.class);
Code code = (Code) node;
- assertEquals(expectedLiteral, code.getLiteral());
+ assertThat(code.getLiteral()).isEqualTo(expectedLiteral);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java b/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java
new file mode 100644
index 000000000..b733d7970
--- /dev/null
+++ b/commonmark/src/test/java/org/commonmark/test/BlockParserFactoryTest.java
@@ -0,0 +1,127 @@
+package org.commonmark.test;
+
+import org.commonmark.node.*;
+import org.commonmark.parser.IncludeSourceSpans;
+import org.commonmark.parser.InlineParser;
+import org.commonmark.parser.Parser;
+import org.commonmark.parser.SourceLines;
+import org.commonmark.parser.block.*;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BlockParserFactoryTest {
+
+ @Test
+ public void customBlockParserFactory() {
+ var parser = Parser.builder().customBlockParserFactory(new DashBlockParser.Factory()).build();
+
+ // The dashes would normally be a ThematicBreak
+ var doc = parser.parse("hey\n\n---\n");
+
+ assertThat(doc.getFirstChild()).isInstanceOf(Paragraph.class);
+ assertThat(((Text) doc.getFirstChild().getFirstChild()).getLiteral()).isEqualTo("hey");
+ assertThat(doc.getLastChild()).isInstanceOf(DashBlock.class);
+ }
+
+ @Test
+ public void replaceActiveBlockParser() {
+ var parser = Parser.builder()
+ .customBlockParserFactory(new StarHeadingBlockParser.Factory())
+ .includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES)
+ .build();
+
+ var doc = parser.parse("a\nbc\n***\n");
+
+ var heading = doc.getFirstChild();
+ assertThat(heading).isInstanceOf(StarHeading.class);
+ assertThat(heading.getNext()).isNull();
+ var a = heading.getFirstChild();
+ assertThat(a).isInstanceOf(Text.class);
+ assertThat(((Text) a).getLiteral()).isEqualTo("a");
+ var bc = a.getNext().getNext();
+ assertThat(bc).isInstanceOf(Text.class);
+ assertThat(((Text) bc).getLiteral()).isEqualTo("bc");
+ assertThat(bc.getNext()).isNull();
+
+ assertThat(heading.getSourceSpans()).isEqualTo(List.of(
+ SourceSpan.of(0, 0, 0, 1),
+ SourceSpan.of(1, 0, 2, 2),
+ SourceSpan.of(2, 0, 5, 3)));
+ assertThat(a.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 1)));
+ assertThat(bc.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 2, 2)));
+ }
+
+ private static class DashBlock extends CustomBlock {
+ }
+
+ private static class DashBlockParser extends AbstractBlockParser {
+
+ private DashBlock dash = new DashBlock();
+
+ @Override
+ public Block getBlock() {
+ return dash;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState parserState) {
+ return BlockContinue.none();
+ }
+
+ static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ if (state.getLine().getContent().equals("---")) {
+ return BlockStart.of(new DashBlockParser());
+ }
+ return BlockStart.none();
+ }
+ }
+ }
+
+ private static class StarHeading extends CustomBlock {
+ }
+
+ private static class StarHeadingBlockParser extends AbstractBlockParser {
+
+ private final SourceLines content;
+ private final StarHeading heading = new StarHeading();
+
+ StarHeadingBlockParser(SourceLines content) {
+ this.content = content;
+ }
+
+ @Override
+ public Block getBlock() {
+ return heading;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState parserState) {
+ return BlockContinue.none();
+ }
+
+ @Override
+ public void parseInlines(InlineParser inlineParser) {
+ inlineParser.parse(content, heading);
+ }
+
+ static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ var lines = matchedBlockParser.getParagraphLines();
+ if (state.getLine().getContent().toString().startsWith("***")) {
+ return BlockStart.of(new StarHeadingBlockParser(lines))
+ .replaceActiveBlockParser();
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+ }
+}
diff --git a/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java b/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java
index 38f319e1c..2303d2617 100644
--- a/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java
+++ b/commonmark/src/test/java/org/commonmark/test/CoreRenderingTestCase.java
@@ -11,6 +11,7 @@ public class CoreRenderingTestCase extends RenderingTestCase {
@Override
protected String render(String source) {
- return RENDERER.render(PARSER.parse(source));
+ var node = PARSER.parse(source);
+ return RENDERER.render(node);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java b/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java
index a34a32c44..3f2f0d611 100644
--- a/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/DelimitedTest.java
@@ -2,12 +2,12 @@
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class DelimitedTest {
@@ -35,20 +35,20 @@ public void visit(StrongEmphasis node) {
};
document.accept(visitor);
- assertEquals(4, list.size());
+ assertThat(list).hasSize(4);
Delimited emphasis = list.get(0);
Delimited strong = list.get(1);
Delimited important = list.get(2);
Delimited critical = list.get(3);
- assertEquals("*", emphasis.getOpeningDelimiter());
- assertEquals("*", emphasis.getClosingDelimiter());
- assertEquals("**", strong.getOpeningDelimiter());
- assertEquals("**", strong.getClosingDelimiter());
- assertEquals("_", important.getOpeningDelimiter());
- assertEquals("_", important.getClosingDelimiter());
- assertEquals("__", critical.getOpeningDelimiter());
- assertEquals("__", critical.getClosingDelimiter());
+ assertThat(emphasis.getOpeningDelimiter()).isEqualTo("*");
+ assertThat(emphasis.getClosingDelimiter()).isEqualTo("*");
+ assertThat(strong.getOpeningDelimiter()).isEqualTo("**");
+ assertThat(strong.getClosingDelimiter()).isEqualTo("**");
+ assertThat(important.getOpeningDelimiter()).isEqualTo("_");
+ assertThat(important.getClosingDelimiter()).isEqualTo("_");
+ assertThat(critical.getOpeningDelimiter()).isEqualTo("__");
+ assertThat(critical.getClosingDelimiter()).isEqualTo("__");
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java b/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java
index 680c40bf2..e4920120d 100644
--- a/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/DelimiterProcessorTest.java
@@ -11,12 +11,13 @@
import org.commonmark.renderer.html.HtmlNodeRendererFactory;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Locale;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class DelimiterProcessorTest extends RenderingTestCase {
@@ -29,8 +30,8 @@ public void delimiterProcessorWithInvalidDelimiterUse() {
.customDelimiterProcessor(new CustomDelimiterProcessor(':', 0))
.customDelimiterProcessor(new CustomDelimiterProcessor(';', -1))
.build();
- assertEquals(":test:
\n", RENDERER.render(parser.parse(":test:")));
- assertEquals(";test;
\n", RENDERER.render(parser.parse(";test;")));
+ assertThat(RENDERER.render(parser.parse(":test:"))).isEqualTo(":test:
\n");
+ assertThat(RENDERER.render(parser.parse(";test;"))).isEqualTo(";test;
\n");
}
@Test
@@ -54,16 +55,17 @@ public void multipleDelimitersWithDifferentLengths() {
.customDelimiterProcessor(new OneDelimiterProcessor())
.customDelimiterProcessor(new TwoDelimiterProcessor())
.build();
- assertEquals("(1)one(/1) (2)two(/2)
\n", RENDERER.render(parser.parse("+one+ ++two++")));
- assertEquals("(1)(2)both(/2)(/1)
\n", RENDERER.render(parser.parse("+++both+++")));
+ assertThat(RENDERER.render(parser.parse("+one+ ++two++"))).isEqualTo("(1)one(/1) (2)two(/2)
\n");
+ assertThat(RENDERER.render(parser.parse("+++both+++"))).isEqualTo("(1)(2)both(/2)(/1)
\n");
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void multipleDelimitersWithSameLengthConflict() {
- Parser.builder()
- .customDelimiterProcessor(new OneDelimiterProcessor())
- .customDelimiterProcessor(new OneDelimiterProcessor())
- .build();
+ assertThatThrownBy(() ->
+ Parser.builder()
+ .customDelimiterProcessor(new OneDelimiterProcessor())
+ .customDelimiterProcessor(new OneDelimiterProcessor())
+ .build()).isInstanceOf(IllegalArgumentException.class);
}
@Override
diff --git a/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java b/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java
index 774c6ff0e..443b0fa51 100644
--- a/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/FencedCodeBlockParserTest.java
@@ -5,9 +5,9 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class FencedCodeBlockParserTest extends RenderingTestCase {
@@ -18,8 +18,8 @@ public class FencedCodeBlockParserTest extends RenderingTestCase {
public void backtickInfo() {
Node document = PARSER.parse("```info ~ test\ncode\n```");
FencedCodeBlock codeBlock = (FencedCodeBlock) document.getFirstChild();
- assertEquals("info ~ test", codeBlock.getInfo());
- assertEquals("code\n", codeBlock.getLiteral());
+ assertThat(codeBlock.getInfo()).isEqualTo("info ~ test");
+ assertThat(codeBlock.getLiteral()).isEqualTo("code\n");
}
@Test
diff --git a/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java b/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java
index a5b179a81..f7bf35a4c 100644
--- a/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/HeadingParserTest.java
@@ -3,7 +3,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.RenderingTestCase;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
public class HeadingParserTest extends RenderingTestCase {
diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java
index 965a2f181..8e1fd9790 100644
--- a/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/HtmlInlineParserTest.java
@@ -1,6 +1,6 @@
package org.commonmark.test;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
public class HtmlInlineParserTest extends CoreRenderingTestCase {
diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java
index 8e5a8f30d..02d970949 100644
--- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java
@@ -5,59 +5,54 @@
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.*;
import org.commonmark.testutil.TestResources;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class HtmlRendererTest {
@Test
public void htmlAllowingShouldNotEscapeInlineHtml() {
String rendered = htmlAllowingRenderer().render(parse("paragraph with inline & html "));
- assertEquals("paragraph with inline & html
\n", rendered);
+ assertThat(rendered).isEqualTo("paragraph with inline & html
\n");
}
@Test
public void htmlAllowingShouldNotEscapeBlockHtml() {
String rendered = htmlAllowingRenderer().render(parse("block &
"));
- assertEquals("block &
\n", rendered);
+ assertThat(rendered).isEqualTo("block &
\n");
}
@Test
public void htmlEscapingShouldEscapeInlineHtml() {
String rendered = htmlEscapingRenderer().render(parse("paragraph with inline & html "));
// Note that & is not escaped, as it's a normal text node, not part of the inline HTML.
- assertEquals("paragraph with <span id='foo' class="bar">inline & html</span>
\n", rendered);
+ assertThat(rendered).isEqualTo("paragraph with <span id='foo' class="bar">inline & html</span>
\n");
}
@Test
public void htmlEscapingShouldEscapeHtmlBlocks() {
String rendered = htmlEscapingRenderer().render(parse("block &
"));
- assertEquals("<div id='foo' class="bar">block &</div>
\n", rendered);
+ assertThat(rendered).isEqualTo("<div id='foo' class="bar">block &</div>
\n");
}
@Test
public void textEscaping() {
String rendered = defaultRenderer().render(parse("escaping: & < > \" '"));
- assertEquals("escaping: & < > " '
\n", rendered);
+ assertThat(rendered).isEqualTo("escaping: & < > " '
\n");
}
@Test
public void characterReferencesWithoutSemicolonsShouldNotBeParsedShouldBeEscaped() {
String input = "[example](javascript:alert('XSS'))";
String rendered = defaultRenderer().render(parse(input));
- assertEquals("example
\n", rendered);
+ assertThat(rendered).isEqualTo("example
\n");
}
@Test
@@ -66,7 +61,7 @@ public void attributeEscaping() {
Link link = new Link();
link.setDestination(":");
paragraph.appendChild(link);
- assertEquals("
\n", defaultRenderer().render(paragraph));
+ assertThat(defaultRenderer().render(paragraph)).isEqualTo("
\n");
}
@Test
@@ -75,7 +70,7 @@ public void rawUrlsShouldNotFilterDangerousProtocols() {
Link link = new Link();
link.setDestination("javascript:alert(5);");
paragraph.appendChild(link);
- assertEquals("
\n", rawUrlsRenderer().render(paragraph));
+ assertThat(rawUrlsRenderer().render(paragraph)).isEqualTo("
\n");
}
@Test
@@ -84,13 +79,13 @@ public void sanitizedUrlsShouldSetRelNoFollow() {
Link link = new Link();
link.setDestination("/exampleUrl");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
paragraph = new Paragraph();
link = new Link();
link.setDestination("https://google.com");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
}
@Test
@@ -99,26 +94,26 @@ public void sanitizedUrlsShouldAllowSafeProtocols() {
Link link = new Link();
link.setDestination("http://google.com");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
paragraph = new Paragraph();
link = new Link();
link.setDestination("https://google.com");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
paragraph = new Paragraph();
link = new Link();
link.setDestination("mailto:foo@bar.example.com");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
String image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAQSURBVBhXY/iPBVBf8P9/AG8TY51nJdgkAAAAAElFTkSuQmCC";
paragraph = new Paragraph();
link = new Link();
link.setDestination(image);
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
}
@Test
@@ -127,45 +122,42 @@ public void sanitizedUrlsShouldFilterDangerousProtocols() {
Link link = new Link();
link.setDestination("javascript:alert(5);");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
paragraph = new Paragraph();
link = new Link();
link.setDestination("ftp://google.com");
paragraph.appendChild(link);
- assertEquals("
\n", sanitizeUrlsRenderer().render(paragraph));
+ assertThat(sanitizeUrlsRenderer().render(paragraph)).isEqualTo("
\n");
}
@Test
public void percentEncodeUrlDisabled() {
- assertEquals("a
\n", defaultRenderer().render(parse("[a](foo&bar)")));
- assertEquals("a
\n", defaultRenderer().render(parse("[a](ä)")));
- assertEquals("a
\n", defaultRenderer().render(parse("[a](foo%20bar)")));
+ assertThat(defaultRenderer().render(parse("[a](foo&bar)"))).isEqualTo("a
\n");
+ assertThat(defaultRenderer().render(parse("[a](ä)"))).isEqualTo("a
\n");
+ assertThat(defaultRenderer().render(parse("[a](foo%20bar)"))).isEqualTo("a
\n");
}
@Test
public void percentEncodeUrl() {
// Entities are escaped anyway
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo&bar)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](foo&bar)"))).isEqualTo("a
\n");
// Existing encoding is preserved
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%20bar)")));
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%61)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%20bar)"))).isEqualTo("a
\n");
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%61)"))).isEqualTo("a
\n");
// Invalid encoding is escaped
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%)")));
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%a)")));
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%a_)")));
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](foo%xx)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%)"))).isEqualTo("a
\n");
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%a)"))).isEqualTo("a
\n");
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%a_)"))).isEqualTo("a
\n");
+ assertThat(percentEncodingRenderer().render(parse("[a](foo%xx)"))).isEqualTo("a
\n");
// Reserved characters are preserved, except for '[' and ']'
- assertEquals("a
\n", percentEncodingRenderer().render(parse("[a](!*'();:@&=+$,/?#[])")));
+ assertThat(percentEncodingRenderer().render(parse("[a](!*'();:@&=+$,/?#[])"))).isEqualTo("a
\n");
// Unreserved characters are preserved
- assertEquals("a
\n",
- percentEncodingRenderer().render(parse("[a](ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~)"))).isEqualTo("a
\n");
// Other characters are percent-encoded (LATIN SMALL LETTER A WITH DIAERESIS)
- assertEquals("a
\n",
- percentEncodingRenderer().render(parse("[a](ä)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](ä)"))).isEqualTo("a
\n");
// Other characters are percent-encoded (MUSICAL SYMBOL G CLEF, surrogate pair in UTF-16)
- assertEquals("a
\n",
- percentEncodingRenderer().render(parse("[a](\uD834\uDD1E)")));
+ assertThat(percentEncodingRenderer().render(parse("[a](\uD834\uDD1E)"))).isEqualTo("a
\n");
}
@Test
@@ -192,10 +184,10 @@ public void setAttributes(Node node, String tagName, Map attribu
HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(custom).build();
String rendered = renderer.render(parse("```info\ncontent\n```"));
- assertEquals("content\n \n", rendered);
+ assertThat(rendered).isEqualTo("content\n \n");
String rendered2 = renderer.render(parse("```evil\"\ncontent\n```"));
- assertEquals("content\n \n", rendered2);
+ assertThat(rendered2).isEqualTo("content\n \n");
}
@Test
@@ -217,7 +209,7 @@ public void setAttributes(Node node, String tagName, Map attribu
HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(custom).build();
String rendered = renderer.render(parse("\n"));
- assertEquals("
\n", rendered);
+ assertThat(rendered).isEqualTo("
\n");
}
@Test
@@ -240,7 +232,7 @@ public void setAttributes(Node node, String tagName, Map attribu
HtmlRenderer renderer = HtmlRenderer.builder().attributeProviderFactory(factory).build();
String rendered = renderer.render(parse("text node"));
String secondPass = renderer.render(parse("text node"));
- assertEquals(rendered, secondPass);
+ assertThat(secondPass).isEqualTo(rendered);
}
@Test
@@ -264,30 +256,37 @@ public void render(Node node) {
HtmlRenderer renderer = HtmlRenderer.builder().nodeRendererFactory(nodeRendererFactory).build();
String rendered = renderer.render(parse("foo [bar](/url)"));
- assertEquals("foo test
\n", rendered);
+ assertThat(rendered).isEqualTo("foo test
\n");
}
@Test
public void orderedListStartZero() {
- assertEquals("\nTest \n \n", defaultRenderer().render(parse("0. Test\n")));
+ assertThat(defaultRenderer().render(parse("0. Test\n"))).isEqualTo("\nTest \n \n");
}
@Test
public void imageAltTextWithSoftLineBreak() {
- assertEquals("
\n",
- defaultRenderer().render(parse("\n")));
+ assertThat(defaultRenderer().render(parse("\n"))).isEqualTo("
\n");
}
@Test
public void imageAltTextWithHardLineBreak() {
- assertEquals("
\n",
- defaultRenderer().render(parse("\n")));
+ assertThat(defaultRenderer().render(parse("\n"))).isEqualTo("
\n");
}
@Test
public void imageAltTextWithEntities() {
- assertEquals("
\n",
- defaultRenderer().render(parse("\n")));
+ assertThat(defaultRenderer().render(parse("\n"))).isEqualTo("
\n");
+ }
+
+ @Test
+ public void imageAltTextWithInlines() {
+ assertThat(defaultRenderer().render(parse("](/url)\n"))).isEqualTo("
\n");
+ }
+
+ @Test
+ public void imageAltTextWithCode() {
+ assertThat(defaultRenderer().render(parse("\n"))).isEqualTo("
\n");
}
@Test
@@ -304,41 +303,35 @@ public void canRenderContentsOfSingleParagraph() {
document.appendChild(current);
}
- assertEquals("Here I have a test link ",
- defaultRenderer().render(document));
+ assertThat(defaultRenderer().render(document)).isEqualTo("Here I have a test link ");
}
@Test
public void omitSingleParagraphP() {
var renderer = HtmlRenderer.builder().omitSingleParagraphP(true).build();
- assertEquals("hi there ", renderer.render(parse("hi *there*")));
+ assertThat(renderer.render(parse("hi *there*"))).isEqualTo("hi there ");
}
@Test
public void threading() throws Exception {
- Parser parser = Parser.builder().build();
- String spec = TestResources.readAsString(TestResources.getSpec());
- final Node document = parser.parse(spec);
+ var parser = Parser.builder().build();
+ var spec = TestResources.readAsString(TestResources.getSpec());
+ var document = parser.parse(spec);
- final HtmlRenderer htmlRenderer = HtmlRenderer.builder().build();
- String expectedRendering = htmlRenderer.render(document);
+ var htmlRenderer = HtmlRenderer.builder().build();
+ var expectedRendering = htmlRenderer.render(document);
// Render in parallel using the same HtmlRenderer instance.
- List> futures = new ArrayList<>();
- ExecutorService executorService = Executors.newFixedThreadPool(4);
+ var futures = new ArrayList>();
+ var executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 40; i++) {
- Future future = executorService.submit(new Callable() {
- @Override
- public String call() throws Exception {
- return htmlRenderer.render(document);
- }
- });
+ var future = executorService.submit(() -> htmlRenderer.render(document));
futures.add(future);
}
- for (Future future : futures) {
- String rendering = future.get();
- assertThat(rendering, is(expectedRendering));
+ for (var future : futures) {
+ var rendering = future.get();
+ assertThat(rendering).isEqualTo(expectedRendering);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java b/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java
index 2f2463ccb..c05cac2d2 100644
--- a/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/InlineParserContextTest.java
@@ -10,13 +10,13 @@
import org.commonmark.parser.Parser;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.renderer.html.HtmlRenderer;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class InlineParserContextTest {
@@ -30,10 +30,10 @@ public void labelShouldBeOriginalNotNormalized() {
String rendered = HtmlRenderer.builder().build().render(parser.parse(input));
// Lookup should pass original label to context
- assertEquals(List.of("FooBarBaz"), inlineParserFactory.lookups);
+ assertThat(inlineParserFactory.lookups).isEqualTo(List.of("FooBarBaz"));
// Context should normalize label for finding reference
- assertEquals("link with special label
\n", rendered);
+ assertThat(rendered).isEqualTo("link with special label
\n");
}
static class CapturingInlineParserFactory implements InlineParserFactory {
diff --git a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java
index 81a71ee69..8410ff028 100644
--- a/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/LinkReferenceDefinitionNodeTest.java
@@ -2,13 +2,11 @@
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.List;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
public class LinkReferenceDefinitionNodeTest {
@@ -17,12 +15,12 @@ public void testDefinitionWithoutParagraph() {
Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url 'title'");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(2));
- assertThat(nodes.get(0), instanceOf(Paragraph.class));
+ assertThat(nodes).hasSize(2);
+ assertThat(nodes.get(0)).isInstanceOf(Paragraph.class);
LinkReferenceDefinition definition = assertDef(nodes.get(1), "foo");
- assertThat(definition.getDestination(), is("/url"));
- assertThat(definition.getTitle(), is("title"));
+ assertThat(definition.getDestination()).isEqualTo("/url");
+ assertThat(definition.getTitle()).isEqualTo("title");
}
@Test
@@ -30,10 +28,10 @@ public void testDefinitionWithParagraph() {
Node document = parse("[foo]: /url\nThis is a paragraph with a [foo] link.");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(2));
+ assertThat(nodes).hasSize(2);
// Note that definition is not part of the paragraph, it's a sibling
- assertThat(nodes.get(0), instanceOf(LinkReferenceDefinition.class));
- assertThat(nodes.get(1), instanceOf(Paragraph.class));
+ assertThat(nodes.get(0)).isInstanceOf(LinkReferenceDefinition.class);
+ assertThat(nodes.get(1)).isInstanceOf(Paragraph.class);
}
@Test
@@ -41,8 +39,8 @@ public void testMultipleDefinitions() {
Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url\n[bar]: /url");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(3));
- assertThat(nodes.get(0), instanceOf(Paragraph.class));
+ assertThat(nodes).hasSize(3);
+ assertThat(nodes.get(0)).isInstanceOf(Paragraph.class);
assertDef(nodes.get(1), "foo");
assertDef(nodes.get(2), "bar");
}
@@ -52,14 +50,14 @@ public void testMultipleDefinitionsWithSameLabel() {
Node document = parse("This is a paragraph with a [foo] link.\n\n[foo]: /url1\n[foo]: /url2");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(3));
- assertThat(nodes.get(0), instanceOf(Paragraph.class));
+ assertThat(nodes).hasSize(3);
+ assertThat(nodes.get(0)).isInstanceOf(Paragraph.class);
LinkReferenceDefinition def1 = assertDef(nodes.get(1), "foo");
- assertThat(def1.getDestination(), is("/url1"));
+ assertThat(def1.getDestination()).isEqualTo("/url1");
// When there's multiple definitions with the same label, the first one "wins", as in reference links will use
// that. But we still want to preserve the original definitions in the document.
LinkReferenceDefinition def2 = assertDef(nodes.get(2), "foo");
- assertThat(def2.getDestination(), is("/url2"));
+ assertThat(def2.getDestination()).isEqualTo("/url2");
}
@Test
@@ -67,42 +65,42 @@ public void testDefinitionOfReplacedBlock() {
Node document = parse("[foo]: /url\nHeading\n=======");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(2));
+ assertThat(nodes).hasSize(2);
assertDef(nodes.get(0), "foo");
- assertThat(nodes.get(1), instanceOf(Heading.class));
+ assertThat(nodes.get(1)).isInstanceOf(Heading.class);
}
@Test
public void testDefinitionInListItem() {
Node document = parse("* [foo]: /url\n [foo]\n");
- assertThat(document.getFirstChild(), instanceOf(BulletList.class));
+ assertThat(document.getFirstChild()).isInstanceOf(BulletList.class);
Node item = document.getFirstChild().getFirstChild();
- assertThat(item, instanceOf(ListItem.class));
+ assertThat(item).isInstanceOf(ListItem.class);
List nodes = Nodes.getChildren(item);
- assertThat(nodes.size(), is(2));
+ assertThat(nodes).hasSize(2);
assertDef(nodes.get(0), "foo");
- assertThat(nodes.get(1), instanceOf(Paragraph.class));
+ assertThat(nodes.get(1)).isInstanceOf(Paragraph.class);
}
@Test
public void testDefinitionInListItem2() {
Node document = parse("* [foo]: /url\n* [foo]\n");
- assertThat(document.getFirstChild(), instanceOf(BulletList.class));
+ assertThat(document.getFirstChild()).isInstanceOf(BulletList.class);
List items = Nodes.getChildren(document.getFirstChild());
- assertThat(items.size(), is(2));
+ assertThat(items).hasSize(2);
Node item1 = items.get(0);
Node item2 = items.get(1);
- assertThat(item1, instanceOf(ListItem.class));
- assertThat(item2, instanceOf(ListItem.class));
+ assertThat(item1).isInstanceOf(ListItem.class);
+ assertThat(item2).isInstanceOf(ListItem.class);
- assertThat(Nodes.getChildren(item1).size(), is(1));
+ assertThat(Nodes.getChildren(item1)).hasSize(1);
assertDef(item1.getFirstChild(), "foo");
- assertThat(Nodes.getChildren(item2).size(), is(1));
- assertThat(item2.getFirstChild(), instanceOf(Paragraph.class));
+ assertThat(Nodes.getChildren(item2)).hasSize(1);
+ assertThat(item2.getFirstChild()).isInstanceOf(Paragraph.class);
}
@Test
@@ -110,8 +108,8 @@ public void testDefinitionLabelCaseIsPreserved() {
Node document = parse("This is a paragraph with a [foo] link.\n\n[fOo]: /url 'title'");
List nodes = Nodes.getChildren(document);
- assertThat(nodes.size(), is(2));
- assertThat(nodes.get(0), instanceOf(Paragraph.class));
+ assertThat(nodes).hasSize(2);
+ assertThat(nodes.get(0)).isInstanceOf(Paragraph.class);
assertDef(nodes.get(1), "fOo");
}
@@ -121,9 +119,9 @@ private static Node parse(String input) {
}
private static LinkReferenceDefinition assertDef(Node node, String label) {
- assertThat(node, instanceOf(LinkReferenceDefinition.class));
+ assertThat(node).isInstanceOf(LinkReferenceDefinition.class);
LinkReferenceDefinition def = (LinkReferenceDefinition) node;
- assertThat(def.getLabel(), is(label));
+ assertThat(def.getLabel()).isEqualTo(label);
return def;
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java
index 4a38bc412..02ac3abff 100644
--- a/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/ListBlockParserTest.java
@@ -3,9 +3,9 @@
import org.commonmark.node.ListItem;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class ListBlockParserTest {
@@ -59,7 +59,7 @@ public void testOrderedListIndents() {
private void assertListItemIndents(String input, int expectedMarkerIndent, int expectedContentIndent) {
Node doc = PARSER.parse(input);
ListItem listItem = Nodes.find(doc, ListItem.class);
- assertEquals(expectedMarkerIndent, (int) listItem.getMarkerIndent());
- assertEquals(expectedContentIndent, (int) listItem.getContentIndent());
+ assertThat((int) listItem.getMarkerIndent()).isEqualTo(expectedMarkerIndent);
+ assertThat((int) listItem.getContentIndent()).isEqualTo(expectedContentIndent);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java b/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java
index 4889bb9ab..c6bda31ed 100644
--- a/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/ListTightLooseTest.java
@@ -1,6 +1,6 @@
package org.commonmark.test;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
public class ListTightLooseTest extends CoreRenderingTestCase {
diff --git a/commonmark/src/test/java/org/commonmark/test/ParserTest.java b/commonmark/src/test/java/org/commonmark/test/ParserTest.java
index 447a50c3d..337196c56 100644
--- a/commonmark/src/test/java/org/commonmark/test/ParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/ParserTest.java
@@ -2,10 +2,10 @@
import org.commonmark.node.*;
import org.commonmark.parser.*;
-import org.commonmark.parser.block.*;
import org.commonmark.renderer.html.HtmlRenderer;
+import org.commonmark.renderer.markdown.MarkdownRenderer;
import org.commonmark.testutil.TestResources;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
@@ -15,14 +15,11 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class ParserTest {
@@ -40,19 +37,7 @@ public void ioReaderTest() throws IOException {
Node document2 = parser.parse(spec);
HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build();
- assertEquals(renderer.render(document2), renderer.render(document1));
- }
-
- @Test
- public void customBlockParserFactory() {
- Parser parser = Parser.builder().customBlockParserFactory(new DashBlockParserFactory()).build();
-
- // The dashes would normally be a ThematicBreak
- Node document = parser.parse("hey\n\n---\n");
-
- assertThat(document.getFirstChild(), instanceOf(Paragraph.class));
- assertEquals("hey", ((Text) document.getFirstChild().getFirstChild()).getLiteral());
- assertThat(document.getLastChild(), instanceOf(DashBlock.class));
+ assertThat(renderer.render(document1)).isEqualTo(renderer.render(document2));
}
@Test
@@ -61,24 +46,25 @@ public void enabledBlockTypes() {
Parser parser = Parser.builder().build(); // all core parsers by default
Node document = parser.parse(given);
- assertThat(document.getFirstChild(), instanceOf(Heading.class));
+ assertThat(document.getFirstChild()).isInstanceOf(Heading.class);
Set> headersOnly = new HashSet<>();
headersOnly.add(Heading.class);
parser = Parser.builder().enabledBlockTypes(headersOnly).build();
document = parser.parse(given);
- assertThat(document.getFirstChild(), instanceOf(Heading.class));
+ assertThat(document.getFirstChild()).isInstanceOf(Heading.class);
Set> noCoreTypes = new HashSet<>();
parser = Parser.builder().enabledBlockTypes(noCoreTypes).build();
document = parser.parse(given);
- assertThat(document.getFirstChild(), not(instanceOf(Heading.class)));
+ assertThat(document.getFirstChild()).isNotInstanceOf(Heading.class);
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void enabledBlockTypesThrowsWhenGivenUnknownClass() {
// BulletList can't be enabled separately at the moment, only all ListBlock types
- Parser.builder().enabledBlockTypes(Set.of(Heading.class, BulletList.class)).build();
+ assertThatThrownBy(() ->
+ Parser.builder().enabledBlockTypes(Set.of(Heading.class, BulletList.class)).build()).isInstanceOf(IllegalArgumentException.class);
}
@Test
@@ -87,19 +73,19 @@ public void indentation() {
Parser parser = Parser.builder().build();
Node document = parser.parse(given);
- assertThat(document.getFirstChild(), instanceOf(BulletList.class));
+ assertThat(document.getFirstChild()).isInstanceOf(BulletList.class);
Node list = document.getFirstChild(); // first level list
- assertEquals("expect one child", list.getFirstChild(), list.getLastChild());
- assertEquals("1 space", firstText(list.getFirstChild()));
+ assertThat(list.getLastChild()).as("expect one child").isEqualTo(list.getFirstChild());
+ assertThat(firstText(list.getFirstChild())).isEqualTo("1 space");
list = list.getFirstChild().getLastChild(); // second level list
- assertEquals("expect one child", list.getFirstChild(), list.getLastChild());
- assertEquals("3 spaces", firstText(list.getFirstChild()));
+ assertThat(list.getLastChild()).as("expect one child").isEqualTo(list.getFirstChild());
+ assertThat(firstText(list.getFirstChild())).isEqualTo("3 spaces");
list = list.getFirstChild().getLastChild(); // third level list
- assertEquals("5 spaces", firstText(list.getFirstChild()));
- assertEquals("tab + space", firstText(list.getFirstChild().getNext()));
+ assertThat(firstText(list.getFirstChild())).isEqualTo("5 spaces");
+ assertThat(firstText(list.getFirstChild().getNext())).isEqualTo("tab + space");
}
@Test
@@ -122,70 +108,146 @@ public InlineParser create(InlineParserContext inlineParserContext) {
Parser parser = Parser.builder().inlineParserFactory(fakeInlineParserFactory).build();
String input = "**bold** **bold** ~~strikethrough~~";
- assertThat(parser.parse(input).getFirstChild().getFirstChild(), instanceOf(ThematicBreak.class));
+ assertThat(parser.parse(input).getFirstChild().getFirstChild()).isInstanceOf(ThematicBreak.class);
}
@Test
public void threading() throws Exception {
- final Parser parser = Parser.builder().build();
- final String spec = TestResources.readAsString(TestResources.getSpec());
+ var parser = Parser.builder().build();
+ var spec = TestResources.readAsString(TestResources.getSpec());
- HtmlRenderer renderer = HtmlRenderer.builder().build();
- String expectedRendering = renderer.render(parser.parse(spec));
+ var renderer = HtmlRenderer.builder().build();
+ var expectedRendering = renderer.render(parser.parse(spec));
// Parse in parallel using the same Parser instance.
- List> futures = new ArrayList<>();
- ExecutorService executorService = Executors.newFixedThreadPool(4);
+ var futures = new ArrayList>();
+ var executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 40; i++) {
- Future future = executorService.submit(new Callable() {
- @Override
- public Node call() throws Exception {
- return parser.parse(spec);
- }
- });
+ var future = executorService.submit(() -> parser.parse(spec));
futures.add(future);
}
- for (Future future : futures) {
- Node node = future.get();
- assertThat(renderer.render(node), is(expectedRendering));
+ for (var future : futures) {
+ var node = future.get();
+ assertThat(renderer.render(node)).isEqualTo(expectedRendering);
}
}
+ @Test
+ public void maxOpenBlockParsersMustBeZeroOrGreater() {
+ assertThatThrownBy(() ->
+ Parser.builder().maxOpenBlockParsers(-1)).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void maxOpenBlockParsersIsOptIn() {
+ var parser = Parser.builder().build();
+
+ var document = parser.parse(alternatingNestedList(9));
+
+ assertThat(renderText(deepestStructuredParagraph(document, 9))).isEqualTo("level9");
+ }
+
+ @Test
+ public void maxOpenBlockParsersPreservesSevenLogicalListLevelsAtSeventeenBlocks() {
+ var parser = Parser.builder().maxOpenBlockParsers(17).build();
+
+ var document = parser.parse(alternatingNestedList(7));
+
+ assertThat(renderText(deepestStructuredParagraph(document, 7))).isEqualTo("level7");
+ }
+
+ @Test
+ public void maxOpenBlockParsersPreservesEightLogicalListLevelsAtSeventeenBlocks() {
+ var parser = Parser.builder().maxOpenBlockParsers(17).build();
+
+ var document = parser.parse(alternatingNestedList(8));
+
+ assertThat(renderText(deepestStructuredParagraph(document, 8))).isEqualTo("level8");
+ }
+
+ @Test
+ public void maxOpenBlockParsersDegradesTheNinthLogicalListLevelToPlainText() {
+ var parser = Parser.builder().maxOpenBlockParsers(17).build();
+
+ var document = parser.parse(alternatingNestedList(9));
+ var deepestParagraph = deepestStructuredParagraph(document, 8);
+
+ assertThat(renderText(deepestParagraph)).isEqualTo("level8\n\\- level9");
+ assertThat(deepestParagraph.getNext()).isNull();
+ }
+
+ @Test
+ public void maxOpenBlockParsersAlsoLimitsMixedListAndBlockQuoteNesting() {
+ var parser = Parser.builder().maxOpenBlockParsers(5).build();
+
+ var document = parser.parse(String.join("\n",
+ "- level1",
+ " > level2",
+ " > > level3",
+ " > > > level4"));
+
+ var listBlock = document.getFirstChild();
+ assertThat(listBlock).isInstanceOf(BulletList.class);
+
+ var listItem = listBlock.getFirstChild();
+ var blockQuote1 = listItem.getLastChild();
+ assertThat(blockQuote1).isInstanceOf(BlockQuote.class);
+
+ var blockQuote2 = blockQuote1.getLastChild();
+ assertThat(blockQuote2).isInstanceOf(BlockQuote.class);
+
+ var deepestParagraph = blockQuote2.getLastChild();
+ assertThat(deepestParagraph).isInstanceOf(Paragraph.class);
+ assertThat(renderText(deepestParagraph)).isEqualTo("level3\n\\> level4");
+ assertThat(deepestParagraph.getNext()).isNull();
+ }
+
private String firstText(Node n) {
while (!(n instanceof Text)) {
- assertThat(n, notNullValue());
+ assertThat(n).isNotNull();
n = n.getFirstChild();
}
return ((Text) n).getLiteral();
}
- private static class DashBlock extends CustomBlock {
+ private Paragraph deepestStructuredParagraph(Node document, int levels) {
+ Node node = document.getFirstChild();
+ for (int level = 1; level <= levels; level++) {
+ assertThat(node).isInstanceOf(ListBlock.class);
+ var listItem = node.getFirstChild();
+ assertThat(listItem).isNotNull();
+ if (level == levels) {
+ assertThat(listItem.getFirstChild()).isInstanceOf(Paragraph.class);
+ return (Paragraph) listItem.getFirstChild();
+ }
+ node = listItem.getLastChild();
+ }
+ throw new AssertionError("unreachable");
}
- private static class DashBlockParser extends AbstractBlockParser {
-
- private DashBlock dash = new DashBlock();
-
- @Override
- public Block getBlock() {
- return dash;
- }
+ private String renderText(Node node) {
+ return MarkdownRenderer.builder().build().render(node).trim();
+ }
- @Override
- public BlockContinue tryContinue(ParserState parserState) {
- return BlockContinue.none();
+ private String alternatingNestedList(int levels) {
+ int indent = 0;
+ var lines = new ArrayList();
+ for (int level = 1; level <= levels; level++) {
+ var ordered = level % 2 == 0;
+ var marker = ordered ? "1. " : "- ";
+ lines.add(" ".repeat(indent) + marker + "level" + level);
+ indent += marker.length();
}
+ return String.join("\n", lines);
}
- private static class DashBlockParserFactory extends AbstractBlockParserFactory {
-
- @Override
- public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
- if (state.getLine().getContent().equals("---")) {
- return BlockStart.of(new DashBlockParser());
- }
- return BlockStart.none();
+ private int depth(Node node) {
+ int depth = 0;
+ while (node.getParent() != null) {
+ node = node.getParent();
+ depth++;
}
+ return depth;
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java b/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java
index ae1310ed2..66d39de23 100644
--- a/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/PathologicalTest.java
@@ -1,34 +1,21 @@
package org.commonmark.test;
-import org.junit.FixMethodOrder;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.Stopwatch;
-import org.junit.rules.Timeout;
-import org.junit.runner.Description;
-import org.junit.runners.MethodSorters;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.Timeout;
import java.util.concurrent.TimeUnit;
/**
* Pathological input cases (from commonmark.js).
*/
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Timeout(value = 3, unit = TimeUnit.SECONDS)
+@TestMethodOrder(MethodOrderer.MethodName.class)
public class PathologicalTest extends CoreRenderingTestCase {
private int x = 100_000;
- @Rule
- public Timeout timeout = new Timeout(3, TimeUnit.SECONDS);
-
- @Rule
- public Stopwatch stopwatch = new Stopwatch() {
- @Override
- protected void finished(long nanos, Description description) {
- System.err.println(description.getDisplayName() + " took " + (nanos / 1000000) + " ms");
- }
- };
-
@Test
public void nestedStrongEmphasis() {
// this is limited by the stack size because visitor is recursive
diff --git a/commonmark/src/test/java/org/commonmark/test/RegressionTest.java b/commonmark/src/test/java/org/commonmark/test/RegressionTest.java
index 94b3a7439..900a6518c 100644
--- a/commonmark/src/test/java/org/commonmark/test/RegressionTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/RegressionTest.java
@@ -6,18 +6,18 @@
import org.commonmark.testutil.TestResources;
import org.commonmark.testutil.example.Example;
import org.commonmark.testutil.example.ExampleReader;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.Parameter;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
-import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-@RunWith(Parameterized.class)
+@ParameterizedClass
+@MethodSource("data")
public class RegressionTest extends RenderingTestCase {
private static final Parser PARSER = Parser.builder().build();
@@ -26,20 +26,13 @@ public class RegressionTest extends RenderingTestCase {
private static final Map OVERRIDDEN_EXAMPLES = getOverriddenExamples();
- private final Example example;
+ @Parameter
+ Example example;
- public RegressionTest(Example example) {
- this.example = example;
- }
-
- @Parameters(name = "{0}")
- public static List data() {
- List data = new ArrayList<>();
- for (URL regressionResource : TestResources.getRegressions()) {
- List examples = ExampleReader.readExamples(regressionResource);
- for (Example example : examples) {
- data.add(new Object[]{example});
- }
+ static List data() {
+ var data = new ArrayList();
+ for (var regressionResource : TestResources.getRegressions()) {
+ data.addAll(ExampleReader.readExamples(regressionResource));
}
return data;
}
diff --git a/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java b/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java
index 3fb95d386..5d34bf410 100644
--- a/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SourceLineTest.java
@@ -2,9 +2,10 @@
import org.commonmark.node.SourceSpan;
import org.commonmark.parser.SourceLine;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class SourceLineTest {
@@ -25,18 +26,20 @@ public void testSubstring() {
assertSourceLine(line.substring(4, 4), "", null);
}
- @Test(expected = StringIndexOutOfBoundsException.class)
+ @Test
public void testSubstringBeginOutOfBounds() {
- SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)).substring(3, 2);
+ var sourceLine = SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4));
+ assertThatThrownBy(() -> sourceLine.substring(3, 2)).isInstanceOf(StringIndexOutOfBoundsException.class);
}
- @Test(expected = StringIndexOutOfBoundsException.class)
+ @Test
public void testSubstringEndOutOfBounds() {
- SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4)).substring(0, 5);
+ var sourceLine = SourceLine.of("abcd", SourceSpan.of(3, 10, 13, 4));
+ assertThatThrownBy(() -> sourceLine.substring(0, 5)).isInstanceOf(StringIndexOutOfBoundsException.class);
}
private static void assertSourceLine(SourceLine sourceLine, String expectedContent, SourceSpan expectedSourceSpan) {
- assertEquals(expectedContent, sourceLine.getContent());
- assertEquals(expectedSourceSpan, sourceLine.getSourceSpan());
+ assertThat(sourceLine.getContent()).isEqualTo(expectedContent);
+ assertThat(sourceLine.getSourceSpan()).isEqualTo(expectedSourceSpan);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java b/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java
index 57048b90e..f1bb231f4 100644
--- a/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SourceSpanTest.java
@@ -1,10 +1,10 @@
package org.commonmark.test;
import org.commonmark.node.SourceSpan;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class SourceSpanTest {
@@ -12,52 +12,57 @@ public class SourceSpanTest {
public void testSubSpan() {
var span = SourceSpan.of(1, 2, 3, 5);
- assertSame(span.subSpan(0), span);
- assertSame(span.subSpan(0, 5), span);
+ assertThat(span.subSpan(0)).isSameAs(span);
+ assertThat(span.subSpan(0, 5)).isSameAs(span);
- assertEquals(SourceSpan.of(1, 3, 4, 4), span.subSpan(1));
- assertEquals(SourceSpan.of(1, 4, 5, 3), span.subSpan(2));
- assertEquals(SourceSpan.of(1, 5, 6, 2), span.subSpan(3));
- assertEquals(SourceSpan.of(1, 6, 7, 1), span.subSpan(4));
+ assertThat(span.subSpan(1)).isEqualTo(SourceSpan.of(1, 3, 4, 4));
+ assertThat(span.subSpan(2)).isEqualTo(SourceSpan.of(1, 4, 5, 3));
+ assertThat(span.subSpan(3)).isEqualTo(SourceSpan.of(1, 5, 6, 2));
+ assertThat(span.subSpan(4)).isEqualTo(SourceSpan.of(1, 6, 7, 1));
// Not sure if empty spans are useful, but it probably makes sense to mirror how substrings work
- assertEquals(SourceSpan.of(1, 7, 8, 0), span.subSpan(5));
- assertEquals("", "abcde".substring(5));
-
- assertEquals(SourceSpan.of(1, 2, 3, 5), span.subSpan(0, 5));
- assertEquals(SourceSpan.of(1, 2, 3, 4), span.subSpan(0, 4));
- assertEquals(SourceSpan.of(1, 2, 3, 3), span.subSpan(0, 3));
- assertEquals(SourceSpan.of(1, 2, 3, 2), span.subSpan(0, 2));
- assertEquals(SourceSpan.of(1, 2, 3, 1), span.subSpan(0, 1));
- assertEquals(SourceSpan.of(1, 2, 3, 0), span.subSpan(0, 0));
- assertEquals("a", "abcde".substring(0, 1));
- assertEquals("", "abcde".substring(0, 0));
-
- assertEquals(SourceSpan.of(1, 3, 4, 3), span.subSpan(1, 4));
- assertEquals(SourceSpan.of(1, 4, 5, 1), span.subSpan(2, 3));
+ assertThat(span.subSpan(5)).isEqualTo(SourceSpan.of(1, 7, 8, 0));
+ assertThat("abcde".substring(5)).isEqualTo("");
+
+ assertThat(span.subSpan(0, 5)).isEqualTo(SourceSpan.of(1, 2, 3, 5));
+ assertThat(span.subSpan(0, 4)).isEqualTo(SourceSpan.of(1, 2, 3, 4));
+ assertThat(span.subSpan(0, 3)).isEqualTo(SourceSpan.of(1, 2, 3, 3));
+ assertThat(span.subSpan(0, 2)).isEqualTo(SourceSpan.of(1, 2, 3, 2));
+ assertThat(span.subSpan(0, 1)).isEqualTo(SourceSpan.of(1, 2, 3, 1));
+ assertThat(span.subSpan(0, 0)).isEqualTo(SourceSpan.of(1, 2, 3, 0));
+ assertThat("abcde".substring(0, 1)).isEqualTo("a");
+ assertThat("abcde".substring(0, 0)).isEqualTo("");
+
+ assertThat(span.subSpan(1, 4)).isEqualTo(SourceSpan.of(1, 3, 4, 3));
+ assertThat(span.subSpan(2, 3)).isEqualTo(SourceSpan.of(1, 4, 5, 1));
}
- @Test(expected = IndexOutOfBoundsException.class)
+ @Test
public void testSubSpanBeginIndexNegative() {
- SourceSpan.of(1, 2, 3, 5).subSpan(-1);
+ var sourceSpan = SourceSpan.of(1, 2, 3, 5);
+ assertThatThrownBy(() -> sourceSpan.subSpan(-1)).isInstanceOf(IndexOutOfBoundsException.class);
}
- @Test(expected = IndexOutOfBoundsException.class)
+ @Test
public void testSubSpanBeginIndexOutOfBounds() {
- SourceSpan.of(1, 2, 3, 5).subSpan(6);
+ var sourceSpan = SourceSpan.of(1, 2, 3, 5);
+ assertThatThrownBy(() -> sourceSpan.subSpan(6)).isInstanceOf(IndexOutOfBoundsException.class);
}
- @Test(expected = IndexOutOfBoundsException.class)
+ @Test
public void testSubSpanEndIndexNegative() {
- SourceSpan.of(1, 2, 3, 5).subSpan(0, -1);
+ var sourceSpan = SourceSpan.of(1, 2, 3, 5);
+ assertThatThrownBy(() -> sourceSpan.subSpan(0, -1)).isInstanceOf(IndexOutOfBoundsException.class);
}
- @Test(expected = IndexOutOfBoundsException.class)
+ @Test
public void testSubSpanEndIndexOutOfBounds() {
- SourceSpan.of(1, 2, 3, 5).subSpan(0, 6);
+ var sourceSpan = SourceSpan.of(1, 2, 3, 5);
+ assertThatThrownBy(() -> sourceSpan.subSpan(0, 6)).isInstanceOf(IndexOutOfBoundsException.class);
}
- @Test(expected = IndexOutOfBoundsException.class)
+ @Test
public void testSubSpanBeginIndexGreaterThanEndIndex() {
- SourceSpan.of(1, 2, 3, 5).subSpan(2, 1);
+ var sourceSpan = SourceSpan.of(1, 2, 3, 5);
+ assertThatThrownBy(() -> sourceSpan.subSpan(2, 1)).isInstanceOf(IndexOutOfBoundsException.class);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java b/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java
index 23249d930..f4e9d0a17 100644
--- a/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java
@@ -3,7 +3,7 @@
import org.commonmark.node.*;
import org.commonmark.parser.IncludeSourceSpans;
import org.commonmark.parser.Parser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.StringReader;
@@ -11,7 +11,7 @@
import java.util.Deque;
import java.util.List;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class SourceSpansTest {
@@ -90,7 +90,7 @@ public void fencedCodeBlock() {
Node document = PARSER.parse("```\nfoo\n```\nbar\n");
Paragraph paragraph = (Paragraph) document.getLastChild();
- assertEquals(List.of(SourceSpan.of(3, 0, 12, 3)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(3, 0, 12, 3)));
}
@Test
@@ -133,7 +133,7 @@ public void listBlock() {
Node document = PARSER.parse("* foo\n * bar\n");
ListBlock listBlock = (ListBlock) document.getFirstChild().getFirstChild().getLastChild();
- assertEquals(List.of(SourceSpan.of(1, 2, 8, 5)), listBlock.getSourceSpans());
+ assertThat(listBlock.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 2, 8, 5)));
}
@Test
@@ -158,10 +158,10 @@ public void linkReferenceDefinition() {
Node document = PARSER.parse("[foo]: /url\ntext\n");
LinkReferenceDefinition linkReferenceDefinition = (LinkReferenceDefinition) document.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), linkReferenceDefinition.getSourceSpans());
+ assertThat(linkReferenceDefinition.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11)));
Paragraph paragraph = (Paragraph) document.getLastChild();
- assertEquals(List.of(SourceSpan.of(1, 0, 12, 4)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 4)));
}
@Test
@@ -169,8 +169,8 @@ public void linkReferenceDefinitionMultiple() {
var doc = PARSER.parse("[foo]: /foo\n[bar]: /bar\n");
var def1 = (LinkReferenceDefinition) doc.getFirstChild();
var def2 = (LinkReferenceDefinition) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), def1.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(1, 0, 12, 11)), def2.getSourceSpans());
+ assertThat(def1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11)));
+ assertThat(def2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 11)));
}
@Test
@@ -178,8 +178,8 @@ public void linkReferenceDefinitionWithTitle() {
var doc = PARSER.parse("[1]: #not-code \"Text\"\n[foo]: /foo\n");
var def1 = (LinkReferenceDefinition) doc.getFirstChild();
var def2 = (LinkReferenceDefinition) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 21)), def1.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(1, 0, 22, 11)), def2.getSourceSpans());
+ assertThat(def1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 21)));
+ assertThat(def2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 22, 11)));
}
@Test
@@ -187,8 +187,8 @@ public void linkReferenceDefinitionWithTitleInvalid() {
var doc = PARSER.parse("[foo]: /url\n\"title\" ok\n");
var def = Nodes.find(doc, LinkReferenceDefinition.class);
var paragraph = Nodes.find(doc, Paragraph.class);
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), def.getSourceSpans());
- assertEquals(List.of(SourceSpan.of(1, 0, 12, 10)), paragraph.getSourceSpans());
+ assertThat(def.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11)));
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 10)));
}
@Test
@@ -198,10 +198,10 @@ public void linkReferenceDefinitionHeading() {
Node document = PARSER.parse("[foo]: /url\nHeading\n===\n");
LinkReferenceDefinition linkReferenceDefinition = (LinkReferenceDefinition) document.getFirstChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 11)), linkReferenceDefinition.getSourceSpans());
+ assertThat(linkReferenceDefinition.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 11)));
Heading heading = (Heading) document.getLastChild();
- assertEquals(List.of(SourceSpan.of(1, 0, 12, 7), SourceSpan.of(2, 0, 20, 3)), heading.getSourceSpans());
+ assertThat(heading.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(1, 0, 12, 7), SourceSpan.of(2, 0, 20, 3)));
}
@Test
@@ -212,13 +212,13 @@ public void lazyContinuationLines() {
var doc = PARSER.parse("> > > foo\nbar\n");
var bq1 = (BlockQuote) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 3)), bq1.getSourceSpans());
+ assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 3)));
var bq2 = (BlockQuote) bq1.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 3)), bq2.getSourceSpans());
+ assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 3)));
var bq3 = (BlockQuote) bq2.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 3)), bq3.getSourceSpans());
+ assertThat(bq3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 3)));
var paragraph = (Paragraph) bq3.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 3)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 3)));
}
{
@@ -226,13 +226,13 @@ public void lazyContinuationLines() {
var doc = PARSER.parse("> > > foo\nbars\n");
var bq1 = (BlockQuote) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 4)), bq1.getSourceSpans());
+ assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 9), SourceSpan.of(1, 0, 10, 4)));
var bq2 = (BlockQuote) bq1.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 4)), bq2.getSourceSpans());
+ assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 7), SourceSpan.of(1, 0, 10, 4)));
var bq3 = (BlockQuote) bq2.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 4)), bq3.getSourceSpans());
+ assertThat(bq3.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 5), SourceSpan.of(1, 0, 10, 4)));
var paragraph = (Paragraph) bq3.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 4)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 6, 6, 3), SourceSpan.of(1, 0, 10, 4)));
}
{
@@ -240,15 +240,15 @@ public void lazyContinuationLines() {
var doc = PARSER.parse("> 1. > Blockquote\ncontinued here.");
var bq1 = (BlockQuote) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 17), SourceSpan.of(1, 0, 18, 15)), bq1.getSourceSpans());
+ assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 17), SourceSpan.of(1, 0, 18, 15)));
var orderedList = (OrderedList) bq1.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)), orderedList.getSourceSpans());
+ assertThat(orderedList.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)));
var listItem = (ListItem) orderedList.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)), listItem.getSourceSpans());
+ assertThat(listItem.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 15), SourceSpan.of(1, 0, 18, 15)));
var bq2 = (BlockQuote) listItem.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 5, 5, 12), SourceSpan.of(1, 0, 18, 15)), bq2.getSourceSpans());
+ assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 5, 5, 12), SourceSpan.of(1, 0, 18, 15)));
var paragraph = (Paragraph) bq2.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 7, 7, 10), SourceSpan.of(1, 0, 18, 15)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 7, 7, 10), SourceSpan.of(1, 0, 18, 15)));
}
{
@@ -256,11 +256,11 @@ public void lazyContinuationLines() {
var doc = PARSER.parse("> > foo\n> bar\n");
var bq1 = (BlockQuote) doc.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 5)), bq1.getSourceSpans());
+ assertThat(bq1.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 0, 0, 7), SourceSpan.of(1, 0, 8, 5)));
var bq2 = (BlockQuote) bq1.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 2, 2, 5), SourceSpan.of(1, 2, 10, 3)), bq2.getSourceSpans());
+ assertThat(bq2.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 2, 2, 5), SourceSpan.of(1, 2, 10, 3)));
var paragraph = (Paragraph) bq2.getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 4, 4, 3), SourceSpan.of(1, 2, 10, 3)), paragraph.getSourceSpans());
+ assertThat(paragraph.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 4, 4, 3), SourceSpan.of(1, 2, 10, 3)));
}
}
@@ -354,7 +354,7 @@ public void inlineEmphasis() {
Node document = INLINES_PARSER.parse("*hey**");
Node lastText = document.getFirstChild().getLastChild();
- assertEquals(List.of(SourceSpan.of(0, 5, 5, 1)), lastText.getSourceSpans());
+ assertThat(lastText.getSourceSpans()).isEqualTo(List.of(SourceSpan.of(0, 5, 5, 1)));
}
@Test
@@ -381,8 +381,8 @@ public void differentLineTerminators() {
private void assertVisualize(String source, String expected) {
var doc = PARSER.parse(source);
- assertEquals(expected, SourceSpanRenderer.renderWithLineColumn(doc, source));
- assertEquals(expected, SourceSpanRenderer.renderWithInputIndex(doc, source));
+ assertThat(SourceSpanRenderer.renderWithLineColumn(doc, source)).isEqualTo(expected);
+ assertThat(SourceSpanRenderer.renderWithInputIndex(doc, source)).isEqualTo(expected);
}
private static void assertSpans(String input, Class extends Node> nodeClass, SourceSpan... expectedSourceSpans) {
@@ -405,7 +405,7 @@ private static void assertInlineSpans(String input, Class extends Node> nodeCl
private static void assertSpans(Node rootNode, Class extends Node> nodeClass, SourceSpan... expectedSourceSpans) {
Node node = findNode(rootNode, nodeClass);
- assertEquals(List.of(expectedSourceSpans), node.getSourceSpans());
+ assertThat(node.getSourceSpans()).isEqualTo(List.of(expectedSourceSpans));
}
private static Node findNode(Node rootNode, Class extends Node> nodeClass) {
diff --git a/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java b/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java
index 8d284c6f9..fefd8fb30 100644
--- a/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SpecCoreTest.java
@@ -7,10 +7,10 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.SpecTestCase;
import org.commonmark.testutil.example.Example;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.fail;
import static org.commonmark.testutil.Asserts.assertRendering;
-import static org.junit.Assert.fail;
public class SpecCoreTest extends SpecTestCase {
@@ -18,10 +18,6 @@ public class SpecCoreTest extends SpecTestCase {
// The spec says URL-escaping is optional, but the examples assume that it's enabled.
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().percentEncodeUrls(true).build();
- public SpecCoreTest(Example example) {
- super(example);
- }
-
@Test
public void testTextNodesContiguous() {
final String source = example.getSource();
diff --git a/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java b/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java
index ab0103e5a..47ca3da4e 100644
--- a/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SpecCrLfCoreTest.java
@@ -4,7 +4,7 @@
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.testutil.SpecTestCase;
import org.commonmark.testutil.example.Example;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.commonmark.testutil.Asserts.assertRendering;
@@ -17,10 +17,6 @@ public class SpecCrLfCoreTest extends SpecTestCase {
// The spec says URL-escaping is optional, but the examples assume that it's enabled.
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().percentEncodeUrls(true).build();
- public SpecCrLfCoreTest(Example example) {
- super(example);
- }
-
@Test
public void testHtmlRendering() {
assertRendering(example.getSource(), example.getHtml(), render(example.getSource()));
diff --git a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java
index 770a1aa21..45cd3aea2 100644
--- a/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/SpecialInputTest.java
@@ -1,6 +1,6 @@
package org.commonmark.test;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
public class SpecialInputTest extends CoreRenderingTestCase {
@@ -210,4 +210,19 @@ public void htmlBlockInterruptingList() {
"\n" +
"\n");
}
+
+ @Test
+ public void emphasisAfterHardLineBreak() {
+ assertRendering("Hello \n" +
+ "**Bar**\n" +
+ "Foo\n", "Hello \n" +
+ "Bar \n" +
+ "Foo
\n");
+
+ assertRendering("Hello \n" +
+ "**Bar** \n" +
+ "Foo\n", "Hello \n" +
+ "Bar \n" +
+ "Foo
\n");
+ }
}
diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java
index 93ca87d93..46757e0c3 100644
--- a/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/TextContentRendererTest.java
@@ -9,7 +9,7 @@
import org.commonmark.renderer.text.TextContentRenderer;
import org.commonmark.parser.Parser;
import org.commonmark.testutil.Asserts;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.Set;
@@ -116,14 +116,14 @@ public void textContentLists() {
assertSeparate(s, "bar\n\n1. foo\n 1. bar\n2. foo");
assertStripped(s, "bar 1. foo 1. bar 2. foo");
- s = "bar\n* foo\n - bar\n* foo";
- assertCompact(s, "bar\n* foo\n - bar\n* foo");
- assertSeparate(s, "bar\n\n* foo\n - bar\n* foo");
+ s = "bar\n* foo\n - bar\n* foo";
+ assertCompact(s, "bar\n* foo\n - bar\n* foo");
+ assertSeparate(s, "bar\n\n* foo\n - bar\n* foo");
assertStripped(s, "bar foo bar foo");
- s = "bar\n* foo\n 1. bar\n 2. bar\n* foo";
- assertCompact(s, "bar\n* foo\n 1. bar\n 2. bar\n* foo");
- assertSeparate(s, "bar\n\n* foo\n 1. bar\n 2. bar\n* foo");
+ s = "bar\n* foo\n 1. bar\n 2. bar\n* foo";
+ assertCompact(s, "bar\n* foo\n 1. bar\n 2. bar\n* foo");
+ assertSeparate(s, "bar\n\n* foo\n 1. bar\n 2. bar\n* foo");
assertStripped(s, "bar foo 1. bar 2. bar foo");
s = "bar\n1. foo\n * bar\n * bar\n2. foo";
@@ -196,6 +196,17 @@ public void textContentHtml() {
assertAll(html, html);
}
+ @Test
+ public void testContentNestedLists() {
+ var s = "List:\n" +
+ "1. 2) 3. \n" +
+ "end";
+ assertCompact(s, s);
+
+ var s2 = "1. A\n 1) B\n 1. Test";
+ assertCompact(s2, s2);
+ }
+
@Test
public void testOverrideNodeRendering() {
var nodeRendererFactory = new TextContentNodeRendererFactory() {
diff --git a/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java b/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java
index 0be668a70..a9f37792e 100644
--- a/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/TextContentWriterTest.java
@@ -1,9 +1,9 @@
package org.commonmark.test;
import org.commonmark.renderer.text.TextContentWriter;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
public class TextContentWriterTest {
@@ -14,7 +14,7 @@ public void whitespace() throws Exception {
writer.write("foo");
writer.whitespace();
writer.write("bar");
- assertEquals("foo bar", stringBuilder.toString());
+ assertThat(stringBuilder.toString()).isEqualTo("foo bar");
}
@Test
@@ -24,7 +24,7 @@ public void colon() throws Exception {
writer.write("foo");
writer.colon();
writer.write("bar");
- assertEquals("foo:bar", stringBuilder.toString());
+ assertThat(stringBuilder.toString()).isEqualTo("foo:bar");
}
@Test
@@ -34,7 +34,7 @@ public void line() throws Exception {
writer.write("foo");
writer.line();
writer.write("bar");
- assertEquals("foo\nbar", stringBuilder.toString());
+ assertThat(stringBuilder.toString()).isEqualTo("foo\nbar");
}
@Test
@@ -42,7 +42,7 @@ public void writeStripped() throws Exception {
StringBuilder stringBuilder = new StringBuilder();
TextContentWriter writer = new TextContentWriter(stringBuilder);
writer.writeStripped("foo\n bar");
- assertEquals("foo bar", stringBuilder.toString());
+ assertThat(stringBuilder.toString()).isEqualTo("foo bar");
}
@Test
@@ -50,6 +50,6 @@ public void write() throws Exception {
StringBuilder stringBuilder = new StringBuilder();
TextContentWriter writer = new TextContentWriter(stringBuilder);
writer.writeStripped("foo bar");
- assertEquals("foo bar", stringBuilder.toString());
+ assertThat(stringBuilder.toString()).isEqualTo("foo bar");
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java
index 2b15f8add..1d564cca2 100644
--- a/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/ThematicBreakParserTest.java
@@ -2,9 +2,9 @@
import org.commonmark.node.ThematicBreak;
import org.commonmark.parser.Parser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class ThematicBreakParserTest {
@@ -20,6 +20,6 @@ public void testLiteral() {
private static void assertLiteral(String expected, String input) {
var tb = Nodes.find(PARSER.parse(input), ThematicBreak.class);
- assertEquals(expected, tb.getLiteral());
+ assertThat(tb.getLiteral()).isEqualTo(expected);
}
}
diff --git a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java
index 63901a49b..20cd9f5ab 100644
--- a/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java
+++ b/commonmark/src/test/java/org/commonmark/test/UsageExampleTest.java
@@ -6,8 +6,8 @@
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.*;
import org.commonmark.renderer.markdown.MarkdownRenderer;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
@@ -16,7 +16,7 @@
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
public class UsageExampleTest {
@@ -25,7 +25,7 @@ public void parseAndRender() {
Parser parser = Parser.builder().build();
Node document = parser.parse("This is *Markdown*");
HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build();
- assertEquals("This is Markdown
\n", renderer.render(document));
+ assertThat(renderer.render(document)).isEqualTo("This is Markdown
\n");
}
@Test
@@ -37,11 +37,11 @@ public void renderToMarkdown() {
heading.appendChild(new Text("My title"));
document.appendChild(heading);
- assertEquals("## My title\n", renderer.render(document));
+ assertThat(renderer.render(document)).isEqualTo("## My title\n");
}
@Test
- @Ignore
+ @Disabled
public void parseReaderRender() throws IOException {
Parser parser = Parser.builder().build();
try (InputStreamReader reader = new InputStreamReader(new FileInputStream("file.md"), StandardCharsets.UTF_8)) {
@@ -56,7 +56,7 @@ public void visitor() {
Node node = parser.parse("Example\n=======\n\nSome more text");
WordCountVisitor visitor = new WordCountVisitor();
node.accept(visitor);
- assertEquals(4, visitor.wordCount);
+ assertThat(visitor.wordCount).isEqualTo(4);
}
@Test
@@ -67,11 +67,11 @@ public void sourcePositions() {
var doc = parser.parse(source);
var emphasis = doc.getLastChild().getLastChild();
var s = emphasis.getSourceSpans().get(0);
- assertEquals(2, s.getLineIndex());
- assertEquals(4, s.getColumnIndex());
- assertEquals(9, s.getInputIndex());
- assertEquals(5, s.getLength());
- assertEquals("*baz*", source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()));
+ assertThat(s.getLineIndex()).isEqualTo(2);
+ assertThat(s.getColumnIndex()).isEqualTo(4);
+ assertThat(s.getInputIndex()).isEqualTo(9);
+ assertThat(s.getLength()).isEqualTo(5);
+ assertThat(source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength())).isEqualTo("*baz*");
}
@Test
@@ -87,8 +87,7 @@ public AttributeProvider create(AttributeProviderContext context) {
.build();
Node document = parser.parse("");
- assertEquals("
\n",
- renderer.render(document));
+ assertThat(renderer.render(document)).isEqualTo("
\n");
}
@Test
@@ -104,7 +103,7 @@ public NodeRenderer create(HtmlNodeRendererContext context) {
.build();
Node document = parser.parse("Example:\n\n code");
- assertEquals("Example:
\ncode\n \n", renderer.render(document));
+ assertThat(renderer.render(document)).isEqualTo("Example:
\ncode\n \n");
}
class WordCountVisitor extends AbstractVisitor {
diff --git a/commonmark/src/test/java/org/commonmark/text/CharactersTest.java b/commonmark/src/test/java/org/commonmark/text/CharactersTest.java
index a362cf53c..99f510cb7 100644
--- a/commonmark/src/test/java/org/commonmark/text/CharactersTest.java
+++ b/commonmark/src/test/java/org/commonmark/text/CharactersTest.java
@@ -1,9 +1,8 @@
package org.commonmark.text;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
public class CharactersTest {
@@ -18,17 +17,17 @@ public void isPunctuation() {
};
for (char c : chars) {
- assertTrue("Expected to be punctuation: " + c, Characters.isPunctuationCodePoint(c));
+ assertThat(Characters.isPunctuationCodePoint(c)).as("Expected to be punctuation: " + c).isTrue();
}
}
@Test
public void isBlank() {
- assertTrue(Characters.isBlank(""));
- assertTrue(Characters.isBlank(" "));
- assertTrue(Characters.isBlank("\t"));
- assertTrue(Characters.isBlank(" \t"));
- assertFalse(Characters.isBlank("a"));
- assertFalse(Characters.isBlank("\f"));
+ assertThat(Characters.isBlank("")).isTrue();
+ assertThat(Characters.isBlank(" ")).isTrue();
+ assertThat(Characters.isBlank("\t")).isTrue();
+ assertThat(Characters.isBlank(" \t")).isTrue();
+ assertThat(Characters.isBlank("a")).isFalse();
+ assertThat(Characters.isBlank("\f")).isFalse();
}
}
diff --git a/pom.xml b/pom.xml
index 031869f6e..f12805316 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.commonmark
commonmark-parent
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
commonmark-java parent
Java implementation of CommonMark, a specification of the Markdown format for turning plain text into formatted
@@ -17,6 +17,7 @@
commonmark
commonmark-ext-autolink
commonmark-ext-footnotes
+ commonmark-ext-gfm-alerts
commonmark-ext-gfm-strikethrough
commonmark-ext-gfm-tables
commonmark-ext-heading-anchor
@@ -39,7 +40,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.13.0
+ 3.14.0
11
@@ -47,17 +48,22 @@
org.apache.maven.plugins
maven-jar-plugin
- 3.4.1
+ 3.4.2
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
org.apache.maven.plugins
maven-install-plugin
- 3.1.1
+ 3.1.4
org.apache.maven.plugins
maven-javadoc-plugin
- 3.6.3
+ 3.11.2
*.internal,*.internal.*
@@ -74,28 +80,28 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.2.5
+ 3.5.3
+
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.13
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.8.0
true
- ossrh
- https://oss.sonatype.org/
- true
- 10
+ central
+ true
+ published
org.apache.maven.plugins
maven-release-plugin
- 3.0.1
+ 3.1.1
true
false
@@ -103,6 +109,21 @@
deploy
+
+ org.apache.felix
+ maven-bundle-plugin
+
+ 5.1.9
+
+
+ bundle-manifest
+ process-classes
+
+ manifest
+
+
+
+
@@ -112,59 +133,74 @@
org.commonmark
commonmark
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-autolink
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
+
+
+ org.commonmark
+ commonmark-ext-footnotes
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-image-attributes
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-ins
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
+
+
+ org.commonmark
+ commonmark-ext-gfm-alerts
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-gfm-strikethrough
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-gfm-tables
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-heading-anchor
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-task-list-items
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-ext-yaml-front-matter
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
org.commonmark
commonmark-test-util
- 0.24.1-SNAPSHOT
+ 0.28.1-SNAPSHOT
- junit
- junit
- 4.13.1
+ org.junit.jupiter
+ junit-jupiter
+ 5.13.1
+
+
+ org.assertj
+ assertj-core
+ 3.27.7
org.openjdk.jmh
@@ -212,7 +248,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.2.4
+ 3.2.7
sign-artifacts
@@ -239,7 +275,7 @@
org.jacoco
jacoco-maven-plugin
- 0.8.12
+ 0.8.13
@@ -282,15 +318,4 @@
HEAD
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-