Add html file test
This commit is contained in:
parent
ba30654728
commit
7881420cbd
@ -119,9 +119,9 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
|
|
||||||
private boolean isInsidePreTag;
|
private boolean isInsidePreTag;
|
||||||
|
|
||||||
private Tokeniser tokeniser;
|
// the thing is: we ensure a new line BEFORE block tag
|
||||||
|
// but not after, so another tag will be placed on the same line (which is wrong)
|
||||||
private CharacterReader reader;
|
private boolean previousIsBlock;
|
||||||
|
|
||||||
|
|
||||||
MarkwonHtmlParserImpl(
|
MarkwonHtmlParserImpl(
|
||||||
@ -242,6 +242,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
|
|
||||||
final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag));
|
final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag));
|
||||||
|
|
||||||
|
ensureNewLineIfPreviousWasBlock(output);
|
||||||
|
|
||||||
if (isVoidTag(name)
|
if (isVoidTag(name)
|
||||||
|| startTag.selfClosing) {
|
|| startTag.selfClosing) {
|
||||||
|
|
||||||
@ -305,6 +307,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
if (isBlockTag(name)) {
|
if (isBlockTag(name)) {
|
||||||
isInsidePreTag = "pre".equals(name);
|
isInsidePreTag = "pre".equals(name);
|
||||||
ensureNewLine(output);
|
ensureNewLine(output);
|
||||||
|
} else {
|
||||||
|
ensureNewLineIfPreviousWasBlock(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int start = output.length();
|
final int start = output.length();
|
||||||
@ -350,6 +354,11 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
|
|
||||||
block.closeAt(output.length());
|
block.closeAt(output.length());
|
||||||
|
|
||||||
|
// if it's empty -> we do no care about if it's block or not
|
||||||
|
if (!block.isEmpty()) {
|
||||||
|
previousIsBlock = isBlockTag(block.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (TAG_PARAGRAPH.equals(name)) {
|
if (TAG_PARAGRAPH.equals(name)) {
|
||||||
appendQuietly(output, '\n');
|
appendQuietly(output, '\n');
|
||||||
}
|
}
|
||||||
@ -368,6 +377,7 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
if (isInsidePreTag) {
|
if (isInsidePreTag) {
|
||||||
appendQuietly(output, character.getData());
|
appendQuietly(output, character.getData());
|
||||||
} else {
|
} else {
|
||||||
|
ensureNewLineIfPreviousWasBlock(output);
|
||||||
trimmingAppender.append(output, character.getData());
|
trimmingAppender.append(output, character.getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,6 +420,13 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
return blockTag;
|
return blockTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected <T extends Appendable & CharSequence> void ensureNewLineIfPreviousWasBlock(@NonNull T output) {
|
||||||
|
if (previousIsBlock) {
|
||||||
|
ensureNewLine(output);
|
||||||
|
previousIsBlock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// name here must lower case
|
// name here must lower case
|
||||||
protected static boolean isInlineTag(@NonNull String name) {
|
protected static boolean isInlineTag(@NonNull String name) {
|
||||||
return INLINE_TAGS.contains(name);
|
return INLINE_TAGS.contains(name);
|
||||||
|
@ -833,6 +833,30 @@ public class MarkwonHtmlParserImplTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newLineAfterBlockTag() {
|
||||||
|
|
||||||
|
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
|
||||||
|
final StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
final String[] fragments = {
|
||||||
|
"<h1>head #1</h1>just text",
|
||||||
|
"<h2>head #2</h2><span>in span tag</span>",
|
||||||
|
"<h3>head #3</h3><custom-tag>in custom-tag</custom-tag>"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String fragment: fragments) {
|
||||||
|
impl.processFragment(output, fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String expected = "" +
|
||||||
|
"head #1\njust text\n" +
|
||||||
|
"head #2\nin span tag\n" +
|
||||||
|
"head #3\nin custom-tag";
|
||||||
|
|
||||||
|
assertEquals(expected, output.toString());
|
||||||
|
}
|
||||||
|
|
||||||
private static class CaptureTagsAction<T> implements MarkwonHtmlParser.FlushAction<T> {
|
private static class CaptureTagsAction<T> implements MarkwonHtmlParser.FlushAction<T> {
|
||||||
|
|
||||||
boolean called;
|
boolean called;
|
||||||
|
@ -13,6 +13,7 @@ import ru.noties.markwon.SpannableConfiguration;
|
|||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||||
import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler;
|
import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler;
|
||||||
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
|
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.HeadingHandler;
|
||||||
import ru.noties.markwon.renderer.html2.tag.ImageHandler;
|
import ru.noties.markwon.renderer.html2.tag.ImageHandler;
|
||||||
import ru.noties.markwon.renderer.html2.tag.LinkHandler;
|
import ru.noties.markwon.renderer.html2.tag.LinkHandler;
|
||||||
import ru.noties.markwon.renderer.html2.tag.ListHandler;
|
import ru.noties.markwon.renderer.html2.tag.ListHandler;
|
||||||
@ -69,7 +70,13 @@ public abstract class MarkwonHtmlRenderer {
|
|||||||
.handler("ul", listHandler)
|
.handler("ul", listHandler)
|
||||||
.handler("ol", listHandler)
|
.handler("ol", listHandler)
|
||||||
.handler("img", ImageHandler.create())
|
.handler("img", ImageHandler.create())
|
||||||
.handler("blockquote", new BlockquoteHandler());
|
.handler("blockquote", new BlockquoteHandler())
|
||||||
|
.handler("h1", new HeadingHandler(1))
|
||||||
|
.handler("h2", new HeadingHandler(2))
|
||||||
|
.handler("h3", new HeadingHandler(3))
|
||||||
|
.handler("h4", new HeadingHandler(4))
|
||||||
|
.handler("h5", new HeadingHandler(5))
|
||||||
|
.handler("h6", new HeadingHandler(6));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class HeadingHandler extends SimpleTagHandler {
|
||||||
|
|
||||||
|
private final int level;
|
||||||
|
|
||||||
|
public HeadingHandler(int level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().heading(configuration.theme(), level);
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ public class LinkHandler extends SimpleTagHandler {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
final String destination = tag.attributes().get("src");
|
final String destination = tag.attributes().get("href");
|
||||||
if (!TextUtils.isEmpty(destination)) {
|
if (!TextUtils.isEmpty(destination)) {
|
||||||
return configuration.factory().link(
|
return configuration.factory().link(
|
||||||
configuration.theme(),
|
configuration.theme(),
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
package ru.noties.markwon.renderer.visitor;
|
package ru.noties.markwon.renderer.visitor;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import org.commonmark.node.Node;
|
||||||
|
import org.junit.ComparisonFailure;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.ParameterizedRobolectricTestRunner;
|
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import ix.Ix;
|
import ix.Ix;
|
||||||
import ix.IxPredicate;
|
import ix.IxPredicate;
|
||||||
@ -55,13 +60,16 @@ public class SpannableMarkdownVisitorTest {
|
|||||||
|
|
||||||
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
|
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
|
||||||
|
|
||||||
System.out.printf("%n%s%n", stringBuilder);
|
System.out.printf("%s: %s%n", file, Arrays.toString(stringBuilder.getSpans(0, stringBuilder.length(), Object.class)));
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
for (TestNode testNode : data.output()) {
|
for (TestNode testNode : data.output()) {
|
||||||
index = validate(stringBuilder, index, testNode);
|
index = validate(stringBuilder, index, testNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assert that the whole thing is processed
|
||||||
|
assertEquals(stringBuilder.length(), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int validate(@NonNull SpannableStringBuilder builder, int index, @NonNull TestNode node) {
|
private int validate(@NonNull SpannableStringBuilder builder, int index, @NonNull TestNode node) {
|
||||||
@ -103,6 +111,8 @@ public class SpannableMarkdownVisitorTest {
|
|||||||
|
|
||||||
final String info = node.toString();
|
final String info = node.toString();
|
||||||
|
|
||||||
|
System.out.printf("%s: %s%n", file, builder.subSequence(index, out));
|
||||||
|
|
||||||
// we can possibly have parent spans here, should filter them
|
// we can possibly have parent spans here, should filter them
|
||||||
final Object[] spans = builder.getSpans(index, out, Object.class);
|
final Object[] spans = builder.getSpans(index, out, Object.class);
|
||||||
assertTrue(info, spans != null);
|
assertTrue(info, spans != null);
|
||||||
@ -123,14 +133,18 @@ public class SpannableMarkdownVisitorTest {
|
|||||||
})
|
})
|
||||||
.first(null);
|
.first(null);
|
||||||
|
|
||||||
assertNotNull(info, testSpan);
|
assertNotNull(
|
||||||
|
format("info: %s, spans: %s", info, Arrays.toString(spans)),
|
||||||
|
testSpan
|
||||||
|
);
|
||||||
|
|
||||||
assertEquals(info, span.name(), testSpan.name());
|
assertEquals(info, span.name(), testSpan.name());
|
||||||
assertEquals(info, span.attributes(), testSpan.attributes());
|
assertMapEquals(info, span.attributes(), testSpan.attributes());
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
@NonNull
|
@NonNull
|
||||||
private SpannableConfiguration configuration(@NonNull TestConfig config) {
|
private SpannableConfiguration configuration(@NonNull TestConfig config) {
|
||||||
|
|
||||||
@ -147,4 +161,49 @@ public class SpannableMarkdownVisitorTest {
|
|||||||
.factory(factory)
|
.factory(factory)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void assertMapEquals(
|
||||||
|
@NonNull String message,
|
||||||
|
@NonNull Map<String, String> expected,
|
||||||
|
@NonNull Map<String, String> actual) {
|
||||||
|
boolean result = true;
|
||||||
|
if (expected.size() == actual.size()) {
|
||||||
|
for (Map.Entry<String, String> entry : expected.entrySet()) {
|
||||||
|
if (!actual.containsKey(entry.getKey())
|
||||||
|
|| !equals(entry.getValue(), actual.get(entry.getKey()))) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
final Comparator<Map.Entry<String, String>> comparator = new Comparator<Map.Entry<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
|
||||||
|
return o1.getKey().compareTo(o2.getKey());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final String e = Ix.from(expected.entrySet())
|
||||||
|
.orderBy(comparator)
|
||||||
|
.toList()
|
||||||
|
.toString();
|
||||||
|
final String a = Ix.from(actual.entrySet())
|
||||||
|
.orderBy(comparator)
|
||||||
|
.toList()
|
||||||
|
.toString();
|
||||||
|
throw new ComparisonFailure(message, e, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean equals(@Nullable Object o1, @Nullable Object o2) {
|
||||||
|
return o1 != null
|
||||||
|
? o1.equals(o2)
|
||||||
|
: o2 == null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static String format(@NonNull String message, Object... args) {
|
||||||
|
return String.format(message, args);
|
||||||
|
}
|
||||||
}
|
}
|
@ -186,7 +186,7 @@ class TestFactory implements SpannableFactory {
|
|||||||
final int length = pairs.length;
|
final int length = pairs.length;
|
||||||
final Map<String, String> map = new HashMap<>(length);
|
final Map<String, String> map = new HashMap<>(length);
|
||||||
for (Pair pair : pairs) {
|
for (Pair pair : pairs) {
|
||||||
map.put(pair.key, String.valueOf(pair.value));
|
map.put(pair.key, pair.value == null ? null : String.valueOf(pair.value));
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
105
markwon/src/test/resources/tests/html.yaml
Normal file
105
markwon/src/test/resources/tests/html.yaml
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
input: |-
|
||||||
|
<h1>html</h1>
|
||||||
|
<h2>emphasis</h2>
|
||||||
|
<i>i</i><em>em</em><cite>cite</cite><dfn>dfn</dfn>
|
||||||
|
<h2>strong-emphasis</h2>
|
||||||
|
<b>b</b><strong>strong</strong>
|
||||||
|
<h2>super-script</h2>
|
||||||
|
<sup>sup</sup>
|
||||||
|
<h2>sub-script</h2>
|
||||||
|
<sub>sub</sub>
|
||||||
|
<h2>underline</h2>
|
||||||
|
<u>u</u><ins>ins</ins>
|
||||||
|
<h2>strike</h2>
|
||||||
|
<s>s</s><del>del</del>
|
||||||
|
<h2>link</h2>
|
||||||
|
<a href="a://href">a</a>
|
||||||
|
<h2>unordered-list</h2>
|
||||||
|
<ul><li>ul1<li>ul2</ul>
|
||||||
|
<h2>ordered-list</h2>
|
||||||
|
<ol><li>ol1<li>ol2</ol>
|
||||||
|
<h2>image</h2>
|
||||||
|
<img src="img://src" alt="img">
|
||||||
|
<h2>blockquote</h2>
|
||||||
|
<blockquote>blockquote</blockquote>
|
||||||
|
<h3>3</h3>
|
||||||
|
<h4>4</h4>
|
||||||
|
<h5>5</h5>
|
||||||
|
<h6>6</h6>
|
||||||
|
|
||||||
|
config:
|
||||||
|
use-html: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- h1: "html"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "emphasis"
|
||||||
|
- text: "\n"
|
||||||
|
- i: "i"
|
||||||
|
- i: "em"
|
||||||
|
- i: "cite"
|
||||||
|
- i: "dfn"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "strong-emphasis"
|
||||||
|
- text: "\n"
|
||||||
|
- b: "b"
|
||||||
|
- b: "strong"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "super-script"
|
||||||
|
- text: "\n"
|
||||||
|
- sup: "sup"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "sub-script"
|
||||||
|
- text: "\n"
|
||||||
|
- sub: "sub"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "underline"
|
||||||
|
- text: "\n"
|
||||||
|
- u: "u"
|
||||||
|
- u: "ins"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "strike"
|
||||||
|
- text: "\n"
|
||||||
|
- s: "s"
|
||||||
|
- s: "del"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "link"
|
||||||
|
- text: "\n"
|
||||||
|
- a: "a"
|
||||||
|
attrs:
|
||||||
|
href: "a://href"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "unordered-list"
|
||||||
|
- text: "\n"
|
||||||
|
- ul: "ul1"
|
||||||
|
- text: "\n"
|
||||||
|
- ul: "ul2"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "ordered-list"
|
||||||
|
- text: "\n"
|
||||||
|
- ol: "ol1"
|
||||||
|
attrs:
|
||||||
|
start: 1
|
||||||
|
- text: "\n"
|
||||||
|
- ol: "ol2"
|
||||||
|
attrs:
|
||||||
|
start: 2
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "image"
|
||||||
|
- text: "\n"
|
||||||
|
- img: "img"
|
||||||
|
attrs:
|
||||||
|
src: "img://src"
|
||||||
|
- text: "\n"
|
||||||
|
- h2: "blockquote"
|
||||||
|
- text: "\n"
|
||||||
|
- blockquote: "blockquote"
|
||||||
|
- text: "\n"
|
||||||
|
- h3: "3"
|
||||||
|
- text: "\n"
|
||||||
|
- h4: "4"
|
||||||
|
- text: "\n"
|
||||||
|
- h5: "5"
|
||||||
|
- text: "\n"
|
||||||
|
- h6: "6"
|
||||||
|
|
@ -3,6 +3,6 @@ input: ""
|
|||||||
output:
|
output:
|
||||||
- img: "image"
|
- img: "image"
|
||||||
attrs:
|
attrs:
|
||||||
scr: "#href"
|
src: "#href"
|
||||||
imageSize: null
|
imageSize: null
|
||||||
replacementTextIsLink: false
|
replacementTextIsLink: false
|
Loading…
x
Reference in New Issue
Block a user