Working with test format
This commit is contained in:
parent
4ae310595a
commit
808349d565
11
build.gradle
11
build.gradle
@ -78,9 +78,14 @@ ext {
|
||||
]
|
||||
|
||||
deps['test'] = [
|
||||
'junit' : 'junit:junit:4.12',
|
||||
'robolectric': 'org.robolectric:robolectric:3.8',
|
||||
'ix-java' : 'com.github.akarnokd:ixjava:1.0.0'
|
||||
'junit' : 'junit:junit:4.12',
|
||||
'robolectric' : 'org.robolectric:robolectric:3.8',
|
||||
'ix-java' : 'com.github.akarnokd:ixjava:1.0.0',
|
||||
'jackson-yaml' : 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.9.0',
|
||||
'jackson-databind': 'com.fasterxml.jackson.core:jackson-databind:2.9.6',
|
||||
'gson' : 'com.google.code.gson:gson:2.8.5',
|
||||
'commons-io' : 'commons-io:commons-io:2.6',
|
||||
'mockito' : 'org.mockito:mockito-core:2.21.0'
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,11 @@ dependencies {
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['ix-java']
|
||||
testImplementation it['jackson-yaml']
|
||||
testImplementation it['jackson-databind']
|
||||
testImplementation it['gson']
|
||||
testImplementation it['commons-io']
|
||||
testImplementation it['mockito']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import ix.Ix;
|
||||
import ix.IxPredicate;
|
||||
import ru.noties.markwon.LinkResolverDef;
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.SpannableBuilder;
|
||||
import ru.noties.markwon.SpannableConfiguration;
|
||||
import ru.noties.markwon.SpannableFactory;
|
||||
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)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class SpannableMarkdownVisitorTest {
|
||||
|
||||
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
|
||||
public static Collection<Object> parameters() {
|
||||
return TestDataReader.testFiles();
|
||||
}
|
||||
|
||||
private final String file;
|
||||
|
||||
public SpannableMarkdownVisitorTest(@NonNull String file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
final TestData data = TestDataReader.readTest(file);
|
||||
|
||||
final SpannableConfiguration configuration = configuration(data.config());
|
||||
final SpannableBuilder builder = new SpannableBuilder();
|
||||
final SpannableMarkdownVisitor visitor = new SpannableMarkdownVisitor(configuration, builder);
|
||||
final Node node = Markwon.createParser().parse(data.input());
|
||||
node.accept(visitor);
|
||||
|
||||
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
|
||||
final String raw = stringBuilder.toString();
|
||||
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
|
||||
for (TestEntry entry : data.output()) {
|
||||
|
||||
final String expected = entry.text();
|
||||
|
||||
final boolean isText = "text".equals(entry.name());
|
||||
|
||||
final int start;
|
||||
final int end;
|
||||
|
||||
if (isText) {
|
||||
start = lastIndex;
|
||||
end = start + expected.length();
|
||||
index = lastIndex = end;
|
||||
} else {
|
||||
start = raw.indexOf(expected, index);
|
||||
if (start < 0) {
|
||||
throw new AssertionError(String.format("Cannot find `%s` starting at index: %d, raw: %n###%n%s%n###",
|
||||
expected, start, raw
|
||||
));
|
||||
}
|
||||
end = start + expected.length();
|
||||
lastIndex = Math.max(end, lastIndex);
|
||||
}
|
||||
|
||||
if (!expected.equals(raw.substring(start, end))) {
|
||||
throw new AssertionError(String.format("Expected: `%s`, actual: `%s`, start: %d, raw: %n###%n%s%n###",
|
||||
expected, raw.substring(start, end), start, raw
|
||||
));
|
||||
}
|
||||
|
||||
final Object[] spans = stringBuilder.getSpans(start, end, Object.class);
|
||||
final int length = spans != null ? spans.length : 0;
|
||||
|
||||
if (isText) {
|
||||
// validate no spans
|
||||
assertEquals(Arrays.toString(spans), 0, length);
|
||||
} else {
|
||||
assertTrue(length > 0);
|
||||
final Object span = Ix.fromArray(spans)
|
||||
.filter(new IxPredicate<Object>() {
|
||||
@Override
|
||||
public boolean test(Object o) {
|
||||
return start == stringBuilder.getSpanStart(o)
|
||||
&& end == stringBuilder.getSpanEnd(o);
|
||||
}
|
||||
})
|
||||
.first(null);
|
||||
assertNotNull(span);
|
||||
assertTrue(span instanceof TestSpan);
|
||||
final TestSpan testSpan = (TestSpan) span;
|
||||
assertEquals(entry.name(), testSpan.name());
|
||||
assertEquals(entry.attributes(), testSpan.attributes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SpannableConfiguration configuration(@NonNull TestConfig config) {
|
||||
|
||||
final SpannableFactory factory = new TestFactory(config.hasOption(TestConfig.USE_PARAGRAPHS));
|
||||
|
||||
// todo: rest omitted for now
|
||||
return SpannableConfiguration.builder(null)
|
||||
.theme(mock(SpannableTheme.class))
|
||||
.linkResolver(mock(LinkResolverDef.class))
|
||||
.factory(factory)
|
||||
.build();
|
||||
|
||||
// return configuration;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class TestConfig {
|
||||
|
||||
static final String USE_PARAGRAPHS = "use-paragraphs";
|
||||
static final String USE_HTML = "use-html";
|
||||
static final String SOFT_BREAK_ADDS_NEW_LINE = "soft-break-adds-new-line";
|
||||
static final String HTML_ALLOW_NON_CLOSED_TAGS = "html-allow-non-closed-tags";
|
||||
|
||||
private final Map<String, Boolean> map;
|
||||
|
||||
TestConfig(@NonNull Map<String, Boolean> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
boolean hasOption(@NonNull String option) {
|
||||
final Boolean value = map.get(option);
|
||||
return value != null && value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestConfig{" +
|
||||
"map=" + map +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class TestData {
|
||||
|
||||
private final String description;
|
||||
private final String input;
|
||||
private final TestConfig config;
|
||||
private final List<TestEntry> output;
|
||||
|
||||
TestData(
|
||||
@Nullable String description,
|
||||
@NonNull String input,
|
||||
@NonNull TestConfig config,
|
||||
@NonNull List<TestEntry> output) {
|
||||
this.description = description;
|
||||
this.input = input;
|
||||
this.config = config;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String input() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TestConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<TestEntry> output() {
|
||||
return output;
|
||||
}
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ix.Ix;
|
||||
import ix.IxFunction;
|
||||
import ix.IxPredicate;
|
||||
|
||||
abstract class TestDataReader {
|
||||
|
||||
private static final String FOLDER = "tests/";
|
||||
|
||||
@NonNull
|
||||
static Collection<Object> testFiles() {
|
||||
|
||||
final InputStream in = TestDataReader.class.getClassLoader().getResourceAsStream(FOLDER);
|
||||
if (in == null) {
|
||||
throw new RuntimeException("Cannot access test cases folder");
|
||||
}
|
||||
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (Collection) Ix.from(IOUtils.readLines(in, StandardCharsets.UTF_8))
|
||||
.filter(new IxPredicate<String>() {
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
return s.endsWith(".yaml");
|
||||
}
|
||||
})
|
||||
.map(new IxFunction<String, String>() {
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return FOLDER + s;
|
||||
}
|
||||
})
|
||||
.map(new IxFunction<String, Object[]>() {
|
||||
@Override
|
||||
public Object[] apply(String s) {
|
||||
return new Object[]{
|
||||
s
|
||||
};
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static TestData readTest(@NonNull String file) {
|
||||
return new Reader(file).read();
|
||||
}
|
||||
|
||||
private TestDataReader() {
|
||||
}
|
||||
|
||||
static class Reader {
|
||||
|
||||
private final String file;
|
||||
|
||||
Reader(@NonNull String file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
TestData read() {
|
||||
return testData(jsonObject());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private JsonObject jsonObject() {
|
||||
try {
|
||||
final String input = IOUtils.resourceToString(file, StandardCharsets.UTF_8, TestDataReader.class.getClassLoader());
|
||||
final ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
|
||||
final Object object = objectMapper.readValue(input, Object.class);
|
||||
final ObjectMapper jsonWriter = new ObjectMapper();
|
||||
final String json = jsonWriter.writeValueAsString(object);
|
||||
return new Gson().fromJson(json, JsonObject.class);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TestData testData(@NonNull JsonObject jsonObject) {
|
||||
|
||||
final String description = jsonObject.get("description").getAsString();
|
||||
|
||||
final String input = jsonObject.get("input").getAsString();
|
||||
if (TextUtils.isEmpty(input)) {
|
||||
throw new RuntimeException(String.format("Test case file `%s` is missing input parameter", file));
|
||||
}
|
||||
|
||||
final List<TestEntry> testSpans = testEntries(jsonObject.get("output").getAsJsonArray());
|
||||
if (testSpans.size() == 0) {
|
||||
throw new RuntimeException(String.format("Test case file `%s` has no output specified", file));
|
||||
}
|
||||
|
||||
return new TestData(
|
||||
description,
|
||||
input,
|
||||
testConfig(jsonObject.get("config")),
|
||||
testSpans
|
||||
);
|
||||
}
|
||||
|
||||
// todo: rename TestNode -> it's not a node... but... what?
|
||||
|
||||
@NonNull
|
||||
private List<TestEntry> testEntries(@NonNull JsonArray array) {
|
||||
|
||||
// all items are contained in this array
|
||||
// key is the name of an item
|
||||
// item can be defined like this:
|
||||
// link: "text-content-of-the-link"
|
||||
// link:
|
||||
// attributes:
|
||||
// - href: "my-href-attribute"
|
||||
// text: "and here is the text content"
|
||||
// text node can be found only at root level
|
||||
|
||||
final int length = array.size();
|
||||
|
||||
final List<TestEntry> testSpans = new ArrayList<>(length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
final JsonElement element = array.get(i);
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
|
||||
final JsonObject object = element.getAsJsonObject();
|
||||
|
||||
// objects must have exactly 1 key: name of the node
|
||||
// it's value can be different: JsonPrimitive (String) or an JsonObject (with attributes and text)
|
||||
|
||||
final String name = object.keySet().iterator().next();
|
||||
final JsonElement value = object.get(name);
|
||||
|
||||
final String text;
|
||||
final Map<String, String> attributes;
|
||||
|
||||
if (value.isJsonObject()) {
|
||||
|
||||
final JsonObject valueObject = value.getAsJsonObject();
|
||||
text = valueObject.get("text").getAsString();
|
||||
attributes = attributes(valueObject.get("attrs"));
|
||||
|
||||
} else {
|
||||
|
||||
final JsonPrimitive primitive;
|
||||
|
||||
if (value.isJsonPrimitive()) {
|
||||
primitive = value.getAsJsonPrimitive();
|
||||
} else {
|
||||
primitive = null;
|
||||
}
|
||||
|
||||
if (primitive == null
|
||||
|| !primitive.isString()) {
|
||||
throw new RuntimeException(String.format("Unexpected json element at index: `%d` in array: `%s`",
|
||||
i, array
|
||||
));
|
||||
}
|
||||
|
||||
text = primitive.getAsString();
|
||||
attributes = Collections.emptyMap();
|
||||
}
|
||||
|
||||
testSpans.add(new TestEntry(name, text, attributes));
|
||||
|
||||
} else {
|
||||
throw new RuntimeException(String.format("Unexpected json element at index: `%d` in array: `%s`",
|
||||
i, array
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return testSpans;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TestConfig testConfig(@Nullable JsonElement element) {
|
||||
|
||||
final JsonObject object = element != null && element.isJsonObject()
|
||||
? element.getAsJsonObject()
|
||||
: null;
|
||||
|
||||
final Map<String, Boolean> map;
|
||||
|
||||
if (object != null) {
|
||||
|
||||
map = new HashMap<>(object.size());
|
||||
|
||||
for (String key : object.keySet()) {
|
||||
|
||||
final JsonElement value = object.get(key);
|
||||
|
||||
if (value.isJsonPrimitive()) {
|
||||
|
||||
final JsonPrimitive jsonPrimitive = value.getAsJsonPrimitive();
|
||||
|
||||
Boolean b = null;
|
||||
|
||||
if (jsonPrimitive.isBoolean()) {
|
||||
b = jsonPrimitive.getAsBoolean();
|
||||
} else if (jsonPrimitive.isString()) {
|
||||
final String s = jsonPrimitive.getAsString();
|
||||
if ("true".equalsIgnoreCase(s)) {
|
||||
b = Boolean.TRUE;
|
||||
} else if ("false".equalsIgnoreCase(s)) {
|
||||
b = Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (b != null) {
|
||||
map.put(key, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
|
||||
return new TestConfig(map);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Map<String, String> attributes(@NonNull JsonElement element) {
|
||||
|
||||
final JsonObject object = element.isJsonObject()
|
||||
? element.getAsJsonObject()
|
||||
: null;
|
||||
|
||||
final Map<String, String> attributes;
|
||||
|
||||
if (object != null) {
|
||||
attributes = new HashMap<>(object.size());
|
||||
for (String key : object.keySet()) {
|
||||
attributes.put(key, object.get(key).getAsString());
|
||||
}
|
||||
} else {
|
||||
attributes = Collections.emptyMap();
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TestEntry {
|
||||
|
||||
private final String name;
|
||||
private final String text;
|
||||
private final Map<String, String> attributes;
|
||||
|
||||
TestEntry(@NonNull String name, @NonNull String text, @NonNull Map<String, String> attributes) {
|
||||
this.name = name;
|
||||
this.text = text;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String text() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, String> attributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestEntry{" +
|
||||
"name='" + name + '\'' +
|
||||
", text='" + text + '\'' +
|
||||
", attributes=" + attributes +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.noties.markwon.SpannableFactory;
|
||||
import ru.noties.markwon.renderer.ImageSize;
|
||||
import ru.noties.markwon.renderer.ImageSizeResolver;
|
||||
import ru.noties.markwon.spans.AsyncDrawable;
|
||||
import ru.noties.markwon.spans.LinkSpan;
|
||||
import ru.noties.markwon.spans.SpannableTheme;
|
||||
import ru.noties.markwon.spans.TableRowSpan;
|
||||
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.BLOCK_QUOTE;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.BULLET_LIST;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.CODE;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.EMPHASIS;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.HEADING;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.IMAGE;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.LINK;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.ORDERED_LIST;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.PARAGRAPH;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.STRIKE_THROUGH;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.STRONG_EMPHASIS;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.SUB_SCRIPT;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.SUPER_SCRIPT;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.TABLE_ROW;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.TASK_LIST;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.THEMATIC_BREAK;
|
||||
import static ru.noties.markwon.renderer.visitor.TestSpan.UNDERLINE;
|
||||
|
||||
class TestFactory implements SpannableFactory {
|
||||
|
||||
private final boolean useParagraphs;
|
||||
|
||||
TestFactory(boolean useParagraphs) {
|
||||
this.useParagraphs = useParagraphs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object strongEmphasis() {
|
||||
return new TestSpan(STRONG_EMPHASIS);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object emphasis() {
|
||||
return new TestSpan(EMPHASIS);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object blockQuote(@NonNull SpannableTheme theme) {
|
||||
return new TestSpan(BLOCK_QUOTE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object code(@NonNull SpannableTheme theme, boolean multiline) {
|
||||
return new TestSpan(CODE, map("multiline", multiline));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object orderedListItem(@NonNull SpannableTheme theme, int startNumber) {
|
||||
return new TestSpan(ORDERED_LIST, map("startNumber", startNumber));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object bulletListItem(@NonNull SpannableTheme theme, int level) {
|
||||
return new TestSpan(BULLET_LIST, map("level", level));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object thematicBreak(@NonNull SpannableTheme theme) {
|
||||
return new TestSpan(THEMATIC_BREAK);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object heading(@NonNull SpannableTheme theme, int level) {
|
||||
return new TestSpan(HEADING, map("level", level));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object strikethrough() {
|
||||
return new TestSpan(STRIKE_THROUGH);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object taskListItem(@NonNull SpannableTheme theme, int blockIndent, boolean isDone) {
|
||||
return new TestSpan(TASK_LIST, map(
|
||||
Pair.of("blockIdent", blockIndent),
|
||||
Pair.of("isDone", isDone)
|
||||
));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object tableRow(@NonNull SpannableTheme theme, @NonNull List<TableRowSpan.Cell> cells, boolean isHeader, boolean isOdd) {
|
||||
return new TestSpan(TABLE_ROW, map(
|
||||
Pair.of("cells", cells),
|
||||
Pair.of("isHeader", isHeader),
|
||||
Pair.of("isOdd", isOdd)
|
||||
));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object paragraph(boolean inTightList) {
|
||||
return !useParagraphs
|
||||
? null
|
||||
: new TestSpan(PARAGRAPH);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object image(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull AsyncDrawable.Loader loader, @NonNull ImageSizeResolver imageSizeResolver, @Nullable ImageSize imageSize, boolean replacementTextIsLink) {
|
||||
return new TestSpan(IMAGE, map(
|
||||
Pair.of("destination", destination),
|
||||
Pair.of("imageSize", imageSize),
|
||||
Pair.of("replacementTextIsLink", replacementTextIsLink)
|
||||
));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object link(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull LinkSpan.Resolver resolver) {
|
||||
return new TestSpan(LINK, map("href", destination));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object superScript(@NonNull SpannableTheme theme) {
|
||||
return new TestSpan(SUPER_SCRIPT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object subScript(@NonNull SpannableTheme theme) {
|
||||
return new TestSpan(SUB_SCRIPT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object underline() {
|
||||
return new TestSpan(UNDERLINE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Map<String, String> map(@NonNull String key, @Nullable Object value) {
|
||||
return Collections.singletonMap(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
private static class Pair {
|
||||
|
||||
static Pair of(@NonNull String key, @Nullable Object value) {
|
||||
return new Pair(key, value);
|
||||
}
|
||||
|
||||
final String key;
|
||||
final Object value;
|
||||
|
||||
Pair(@NonNull String key, @Nullable Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Map<String, String> map(Pair... pairs) {
|
||||
final int length = pairs.length;
|
||||
final Map<String, String> map = new HashMap<>(length);
|
||||
for (Pair pair : pairs) {
|
||||
map.put(pair.key, String.valueOf(pair.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package ru.noties.markwon.renderer.visitor;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
class TestSpan {
|
||||
|
||||
static final String STRONG_EMPHASIS = "b";
|
||||
static final String EMPHASIS = "i";
|
||||
static final String BLOCK_QUOTE = "blockquote";
|
||||
static final String CODE = "code";
|
||||
static final String ORDERED_LIST = "ol";
|
||||
static final String BULLET_LIST = "ul";
|
||||
static final String THEMATIC_BREAK = "hr";
|
||||
static final String HEADING = "h";
|
||||
static final String STRIKE_THROUGH = "s";
|
||||
static final String TASK_LIST = "task-list";
|
||||
static final String TABLE_ROW = "tr";
|
||||
static final String PARAGRAPH = "p";
|
||||
static final String IMAGE = "img";
|
||||
static final String LINK = "a";
|
||||
static final String SUPER_SCRIPT = "sup";
|
||||
static final String SUB_SCRIPT = "sub";
|
||||
static final String UNDERLINE = "u";
|
||||
|
||||
|
||||
private final String name;
|
||||
private final Map<String, String> attributes;
|
||||
|
||||
TestSpan(@NonNull String name) {
|
||||
this(name, Collections.<String, String>emptyMap());
|
||||
}
|
||||
|
||||
TestSpan(@NonNull String name, @NonNull Map<String, String> attributes) {
|
||||
this.name = name;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, String> attributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestSpan{" +
|
||||
"name='" + name + '\'' +
|
||||
", attributes=" + attributes +
|
||||
'}';
|
||||
}
|
||||
}
|
23
markwon/src/test/resources/tests/first.yaml
Normal file
23
markwon/src/test/resources/tests/first.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
description: Defining test case format
|
||||
|
||||
input: |-
|
||||
Here is some [link](https://my.href)
|
||||
**bold _bold italic_ bold** normal
|
||||
|
||||
config:
|
||||
use-paragraphs: false
|
||||
use-html: false
|
||||
soft-break-adds-new-line: false
|
||||
html-allow-non-closed-tags: false
|
||||
|
||||
output:
|
||||
- text: "Here is some "
|
||||
- a:
|
||||
attrs:
|
||||
href: "https://my.href"
|
||||
text: "link"
|
||||
- text: " "
|
||||
- b: "bold bold italic bold"
|
||||
- i: "bold italic"
|
||||
- text: " normal"
|
||||
|
Loading…
x
Reference in New Issue
Block a user