Add more tests
This commit is contained in:
parent
fddeb885a2
commit
7caa376bcb
@ -1,11 +1,9 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.junit.ComparisonFailure;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||
@ -13,11 +11,7 @@ import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
import ix.Ix;
|
||||
import ix.IxPredicate;
|
||||
import ru.noties.markwon.LinkResolverDef;
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.SpannableBuilder;
|
||||
@ -28,8 +22,6 @@ import ru.noties.markwon.renderer.SpannableMarkdownVisitor;
|
||||
import ru.noties.markwon.spans.SpannableTheme;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@RunWith(ParameterizedRobolectricTestRunner.class)
|
||||
@ -60,99 +52,23 @@ public class SpannableMarkdownVisitorTest {
|
||||
|
||||
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
|
||||
|
||||
// System.out.printf("%s: %s%n", file, Arrays.toString(stringBuilder.getSpans(0, stringBuilder.length(), Object.class)));
|
||||
final TestValidator validator = TestValidator.create(file);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (TestNode testNode : data.output()) {
|
||||
index = validate(stringBuilder, index, testNode);
|
||||
index = validator.validate(stringBuilder, index, testNode);
|
||||
}
|
||||
|
||||
// assert that the whole thing is processed
|
||||
assertEquals(stringBuilder.length(), index);
|
||||
}
|
||||
|
||||
private int validate(@NonNull final SpannableStringBuilder builder, final int index, @NonNull TestNode node) {
|
||||
final Object[] spans = stringBuilder.getSpans(0, stringBuilder.length(), Object.class);
|
||||
final int length = spans != null
|
||||
? spans.length
|
||||
: 0;
|
||||
|
||||
if (node.isText()) {
|
||||
|
||||
final String text;
|
||||
{
|
||||
final String content = node.getAsText().text();
|
||||
|
||||
// code is a special case as we wrap it around non-breakable spaces
|
||||
final TestNode parent = node.parent();
|
||||
if (parent != null) {
|
||||
final TestNode.Span span = parent.getAsSpan();
|
||||
if (TestSpan.CODE.equals(span.name())) {
|
||||
text = "\u00a0" + content + "\u00a0";
|
||||
} else if (TestSpan.CODE_BLOCK.equals(span.name())) {
|
||||
text = "\u00a0\n" + content + "\n\u00a0";
|
||||
} else {
|
||||
text = content;
|
||||
}
|
||||
} else {
|
||||
text = content;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(text, builder.subSequence(index, index + text.length()).toString());
|
||||
|
||||
return index + text.length();
|
||||
}
|
||||
|
||||
final TestNode.Span span = node.getAsSpan();
|
||||
|
||||
int out = index;
|
||||
|
||||
for (TestNode child : span.children()) {
|
||||
out = validate(builder, out, child);
|
||||
}
|
||||
|
||||
final int end = out;
|
||||
|
||||
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
|
||||
final Object[] spans = builder.getSpans(index, out, Object.class);
|
||||
assertTrue(info, spans != null);
|
||||
|
||||
final TestSpan testSpan = Ix.fromArray(spans)
|
||||
.filter(new IxPredicate<Object>() {
|
||||
@Override
|
||||
public boolean test(Object o) {
|
||||
return o instanceof TestSpan;
|
||||
}
|
||||
})
|
||||
.cast(TestSpan.class)
|
||||
.filter(new IxPredicate<TestSpan>() {
|
||||
@Override
|
||||
public boolean test(TestSpan testSpan) {
|
||||
return span.name().equals(testSpan.name())
|
||||
&& index == builder.getSpanStart(testSpan)
|
||||
&& end == builder.getSpanEnd(testSpan);
|
||||
}
|
||||
})
|
||||
.first(null);
|
||||
|
||||
assertNotNull(
|
||||
format("info: %s, spans: %s", info, Arrays.toString(spans)),
|
||||
testSpan
|
||||
);
|
||||
|
||||
assertEquals(info, span.name(), testSpan.name());
|
||||
|
||||
// for correct tracking of nested blocks we must validate expected start/end
|
||||
assertEquals(info, index, builder.getSpanStart(testSpan));
|
||||
assertEquals(info, out, builder.getSpanEnd(testSpan));
|
||||
|
||||
System.out.printf("%s: expected: %s, actual: %s%n", file, span.attributes(), testSpan.attributes());
|
||||
|
||||
assertMapEquals(info, span.attributes(), testSpan.attributes());
|
||||
|
||||
return out;
|
||||
assertEquals(Arrays.toString(spans), validator.processedSpanNodesCount(), length);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@ -164,57 +80,16 @@ public class SpannableMarkdownVisitorTest {
|
||||
? null
|
||||
: MarkwonHtmlParser.noOp();
|
||||
|
||||
// todo: rest omitted for now
|
||||
final boolean softBreakAddsNewLine = config.hasOption(TestConfig.SOFT_BREAK_ADDS_NEW_LINE);
|
||||
final boolean htmlAllowNonClosedTags = config.hasOption(TestConfig.HTML_ALLOW_NON_CLOSED_TAGS);
|
||||
|
||||
return SpannableConfiguration.builder(null)
|
||||
.theme(mock(SpannableTheme.class))
|
||||
.linkResolver(mock(LinkResolverDef.class))
|
||||
.htmlParser(htmlParser)
|
||||
.factory(factory)
|
||||
.softBreakAddsNewLine(softBreakAddsNewLine)
|
||||
.htmlAllowNonClosedTags(htmlAllowNonClosedTags)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static void assertMapEquals(
|
||||
@NonNull String message,
|
||||
@NonNull Map<String, String> expected,
|
||||
@NonNull Map<String, String> actual) {
|
||||
boolean result = expected.size() == actual.size();
|
||||
if (result) {
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import ix.Ix;
|
||||
import ix.IxPredicate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
abstract class TestValidator {
|
||||
|
||||
abstract int validate(
|
||||
@NonNull SpannableStringBuilder builder,
|
||||
int index,
|
||||
@NonNull TestNode node);
|
||||
|
||||
abstract int processedSpanNodesCount();
|
||||
|
||||
|
||||
@NonNull
|
||||
static TestValidator create(@NonNull String id) {
|
||||
return new Impl(id);
|
||||
}
|
||||
|
||||
static class Impl extends TestValidator {
|
||||
|
||||
private final String id;
|
||||
|
||||
private int processedCount;
|
||||
|
||||
Impl(@NonNull String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
int validate(
|
||||
@NonNull final SpannableStringBuilder builder,
|
||||
final int index,
|
||||
@NonNull TestNode node) {
|
||||
|
||||
if (node.isText()) {
|
||||
|
||||
final String text;
|
||||
{
|
||||
final String content = node.getAsText().text();
|
||||
|
||||
// code is a special case as we wrap it around non-breakable spaces
|
||||
final TestNode parent = node.parent();
|
||||
if (parent != null) {
|
||||
final TestNode.Span span = parent.getAsSpan();
|
||||
if (TestSpan.CODE.equals(span.name())) {
|
||||
text = "\u00a0" + content + "\u00a0";
|
||||
} else if (TestSpan.CODE_BLOCK.equals(span.name())) {
|
||||
text = "\u00a0\n" + content + "\n\u00a0";
|
||||
} else {
|
||||
text = content;
|
||||
}
|
||||
} else {
|
||||
text = content;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(text, builder.subSequence(index, index + text.length()).toString());
|
||||
|
||||
return index + text.length();
|
||||
}
|
||||
|
||||
final TestNode.Span span = node.getAsSpan();
|
||||
processedCount += 1;
|
||||
|
||||
int out = index;
|
||||
|
||||
for (TestNode child : span.children()) {
|
||||
out = validate(builder, out, child);
|
||||
}
|
||||
|
||||
final int end = out;
|
||||
|
||||
// we can possibly have parent spans here, should filter them
|
||||
final Object[] spans = builder.getSpans(index, out, Object.class);
|
||||
|
||||
// expected span{name, attributes} at position{start-end}, with text: `%s`, spans: []
|
||||
|
||||
|
||||
assertTrue(
|
||||
message(span, index, end, builder, spans),
|
||||
spans != null
|
||||
);
|
||||
|
||||
final TestSpan testSpan = Ix.fromArray(spans)
|
||||
.filter(new IxPredicate<Object>() {
|
||||
@Override
|
||||
public boolean test(Object o) {
|
||||
return o instanceof TestSpan;
|
||||
}
|
||||
})
|
||||
.cast(TestSpan.class)
|
||||
.filter(new IxPredicate<TestSpan>() {
|
||||
@Override
|
||||
public boolean test(TestSpan testSpan) {
|
||||
// in case of nested spans with the same name (lists)
|
||||
// we also must validate attributes
|
||||
// and thus we are moving most of assertions to this filter method
|
||||
return span.name().equals(testSpan.name())
|
||||
&& index == builder.getSpanStart(testSpan)
|
||||
&& end == builder.getSpanEnd(testSpan)
|
||||
&& mapEquals(span.attributes(), testSpan.attributes());
|
||||
}
|
||||
})
|
||||
.first(null);
|
||||
|
||||
assertNotNull(
|
||||
message(span, index, end, builder, spans),
|
||||
testSpan
|
||||
);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
int processedSpanNodesCount() {
|
||||
return processedCount;
|
||||
}
|
||||
|
||||
private static boolean mapEquals(
|
||||
@NonNull Map<String, String> expected,
|
||||
@NonNull Map<String, String> actual) {
|
||||
|
||||
if (expected.size() != actual.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = true;
|
||||
|
||||
for (Map.Entry<String, String> entry : expected.entrySet()) {
|
||||
if (!actual.containsKey(entry.getKey())
|
||||
|| !equals(entry.getValue(), actual.get(entry.getKey()))) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean equals(@Nullable Object o1, @Nullable Object o2) {
|
||||
return o1 != null
|
||||
? o1.equals(o2)
|
||||
: o2 == null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String message(
|
||||
@NonNull TestNode.Span span,
|
||||
int start,
|
||||
int end,
|
||||
@NonNull Spanned text,
|
||||
@Nullable Object[] spans) {
|
||||
final String spansText;
|
||||
if (spans == null
|
||||
|| spans.length == 0) {
|
||||
spansText = "[]";
|
||||
} else {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (Object o: spans) {
|
||||
final TestSpan testSpan = (TestSpan) o;
|
||||
if (builder.length() > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder
|
||||
.append("{name: '").append(testSpan.name()).append('\'')
|
||||
.append(", position{").append(start).append(", ").append(end).append('}')
|
||||
.append(", attributes: ").append(testSpan.attributes())
|
||||
.append('}');
|
||||
}
|
||||
spansText = builder.toString();
|
||||
}
|
||||
return String.format("Expected span: %s at position{%d-%d} with text `%s`, spans: %s",
|
||||
span, start, end, text.subSequence(start, end), spansText
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
5
markwon/src/test/resources/tests/bold-italic.yaml
Normal file
5
markwon/src/test/resources/tests/bold-italic.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
input: "**_bold italic_**"
|
||||
|
||||
output:
|
||||
- b:
|
||||
- i: "bold italic"
|
@ -0,0 +1,19 @@
|
||||
input: |-
|
||||
<i>italic
|
||||
<b>bold italic
|
||||
<u>underline bold italic
|
||||
<s>strike underline bold italic
|
||||
|
||||
config:
|
||||
use-html: true
|
||||
html-allow-non-closed-tags: true
|
||||
|
||||
output:
|
||||
- i:
|
||||
- text: "italic "
|
||||
- b:
|
||||
- text: "bold italic "
|
||||
- u:
|
||||
- text: "underline bold italic "
|
||||
- s:
|
||||
- text: "strike underline bold italic"
|
12
markwon/src/test/resources/tests/html-non-closed-ignore.yaml
Normal file
12
markwon/src/test/resources/tests/html-non-closed-ignore.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
input: |-
|
||||
<i>no italic here
|
||||
<b>bold yeah</b>
|
||||
<u>no underline
|
||||
|
||||
config:
|
||||
use-html: true
|
||||
|
||||
output:
|
||||
- text: "no italic here "
|
||||
- b: "bold yeah"
|
||||
- text: " no underline"
|
12
markwon/src/test/resources/tests/no-paragraphs.yaml
Normal file
12
markwon/src/test/resources/tests/no-paragraphs.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
input: |-
|
||||
This could be a paragraph
|
||||
|
||||
But it is not and this one is not also
|
||||
|
||||
config:
|
||||
use-paragraphs: false
|
||||
|
||||
output:
|
||||
- text: "This could be a paragraph"
|
||||
- text: "\n\n"
|
||||
- text: "But it is not and this one is not also"
|
16
markwon/src/test/resources/tests/ol-2-spaces.yaml
Normal file
16
markwon/src/test/resources/tests/ol-2-spaces.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
description: "Will be rendered as simple flat list"
|
||||
|
||||
input: |-
|
||||
1. First
|
||||
2. Second
|
||||
3. Third
|
||||
|
||||
output:
|
||||
- ol: "First"
|
||||
start: 1
|
||||
- text: "\n"
|
||||
- ol: "Second"
|
||||
start: 2
|
||||
- text: "\n"
|
||||
- ol: "Third"
|
||||
start: 3
|
14
markwon/src/test/resources/tests/ol-starts-with-5.yaml
Normal file
14
markwon/src/test/resources/tests/ol-starts-with-5.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
input: |-
|
||||
5. Five
|
||||
6. Six
|
||||
7. Seven
|
||||
|
||||
output:
|
||||
- ol: "Five"
|
||||
start: 5
|
||||
- text: "\n"
|
||||
- ol: "Six"
|
||||
start: 6
|
||||
- text: "\n"
|
||||
- ol: "Seven"
|
||||
start: 7
|
14
markwon/src/test/resources/tests/ol.yaml
Normal file
14
markwon/src/test/resources/tests/ol.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
input: |-
|
||||
1. First
|
||||
1. Second
|
||||
1. Third
|
||||
|
||||
output:
|
||||
- ol:
|
||||
- text: "First\n"
|
||||
- ol:
|
||||
- text: "Second\n"
|
||||
- ol: "Third"
|
||||
start: 1
|
||||
start: 1
|
||||
start: 1
|
12
markwon/src/test/resources/tests/paragraph.yaml
Normal file
12
markwon/src/test/resources/tests/paragraph.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
input: |-
|
||||
So, this is a paragraph
|
||||
|
||||
And this one is another
|
||||
|
||||
config:
|
||||
use-paragraphs: true
|
||||
|
||||
output:
|
||||
- p: "So, this is a paragraph"
|
||||
- text: "\n\n"
|
||||
- p: "And this one is another"
|
@ -0,0 +1,10 @@
|
||||
input: |-
|
||||
hello there!
|
||||
this one is on the next line
|
||||
hard break to the full extend
|
||||
|
||||
config:
|
||||
soft-break-adds-new-line: true
|
||||
|
||||
output:
|
||||
- text: "hello there!\nthis one is on the next line\nhard break to the full extend"
|
7
markwon/src/test/resources/tests/soft-break.yaml
Normal file
7
markwon/src/test/resources/tests/soft-break.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
input: |-
|
||||
First line
|
||||
same line but with space between
|
||||
this is also the first line
|
||||
|
||||
output:
|
||||
- text: "First line same line but with space between this is also the first line"
|
20
markwon/src/test/resources/tests/ul-levels.yaml
Normal file
20
markwon/src/test/resources/tests/ul-levels.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
input: |-
|
||||
* First
|
||||
* * Second
|
||||
* * * Third
|
||||
|
||||
output:
|
||||
- ul: "First"
|
||||
level: 0
|
||||
- text: "\n"
|
||||
- ul:
|
||||
- ul: "Second"
|
||||
level: 1
|
||||
- text: "\n"
|
||||
level: 0
|
||||
- ul:
|
||||
- ul:
|
||||
- ul: "Third"
|
||||
level: 2
|
||||
level: 1
|
||||
level: 0
|
14
markwon/src/test/resources/tests/ul.yaml
Normal file
14
markwon/src/test/resources/tests/ul.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
input: |-
|
||||
* First
|
||||
* Second
|
||||
* Third
|
||||
|
||||
output:
|
||||
- ul:
|
||||
- text: "First\n"
|
||||
- ul:
|
||||
- text: "Second\n"
|
||||
- ul: "Third"
|
||||
level: 2
|
||||
level: 1
|
||||
level: 0
|
Loading…
x
Reference in New Issue
Block a user