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'] = [
|
deps['test'] = [
|
||||||
'junit' : 'junit:junit:4.12',
|
'junit' : 'junit:junit:4.12',
|
||||||
'robolectric': 'org.robolectric:robolectric:3.8',
|
'robolectric' : 'org.robolectric:robolectric:3.8',
|
||||||
'ix-java' : 'com.github.akarnokd:ixjava:1.0.0'
|
'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['junit']
|
||||||
testImplementation it['robolectric']
|
testImplementation it['robolectric']
|
||||||
testImplementation it['ix-java']
|
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