From 9958f345749abd611f7a212ba769cb3e829ba245 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Thu, 3 Jan 2019 14:28:41 +0300 Subject: [PATCH] MarkwonImpl tests --- .../ru/noties/markwon/MarkwonImplTest.java | 232 ++++++++++++++++++ .../markwon/syntax/SyntaxHighlightTest.java | 3 +- 2 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 markwon/src/test/java/ru/noties/markwon/MarkwonImplTest.java diff --git a/markwon/src/test/java/ru/noties/markwon/MarkwonImplTest.java b/markwon/src/test/java/ru/noties/markwon/MarkwonImplTest.java new file mode 100644 index 00000000..62ab3de0 --- /dev/null +++ b/markwon/src/test/java/ru/noties/markwon/MarkwonImplTest.java @@ -0,0 +1,232 @@ +package ru.noties.markwon; + +import android.text.Spanned; +import android.widget.TextView; + +import org.commonmark.node.Node; +import org.commonmark.node.Visitor; +import org.commonmark.parser.Parser; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class MarkwonImplTest { + + @Test + public void parse_calls_plugin_process_markdown() { + + final MarkwonPlugin plugin = mock(MarkwonPlugin.class); + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.SPANNABLE, + mock(Parser.class), + mock(MarkwonVisitor.class), + Collections.singletonList(plugin)); + + impl.parse("whatever"); + + verify(plugin, times(1)).processMarkdown(eq("whatever")); + } + + @Test + public void parse_markwon_processed() { + + final Parser parser = mock(Parser.class); + + final MarkwonPlugin first = mock(MarkwonPlugin.class); + final MarkwonPlugin second = mock(MarkwonPlugin.class); + + when(first.processMarkdown(anyString())).thenReturn("first"); + when(second.processMarkdown(anyString())).thenReturn("second"); + + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.SPANNABLE, + parser, + mock(MarkwonVisitor.class), + Arrays.asList(first, second)); + + impl.parse("zero"); + + verify(first, times(1)).processMarkdown(eq("zero")); + verify(second, times(1)).processMarkdown(eq("first")); + + // verify parser has `second` as input + verify(parser, times(1)).parse(eq("second")); + } + + @Test + public void render_calls_plugins() { + // before parsing each plugin is called `configureRenderProps` and `beforeRender` + // after parsing each plugin is called `afterRender` + + final MarkwonPlugin plugin = mock(MarkwonPlugin.class); + + final MarkwonVisitor visitor = mock(MarkwonVisitor.class); + final SpannableBuilder builder = mock(SpannableBuilder.class); + + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.SPANNABLE, + mock(Parser.class), + visitor, + Collections.singletonList(plugin)); + + when(visitor.builder()).thenReturn(builder); + + final Node node = mock(Node.class); + + final AtomicBoolean flag = new AtomicBoolean(false); + + // we will validate _before_ part here + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + + // mark this flag (we must ensure that this method body is executed) + flag.set(true); + + //noinspection ConstantConditions + verify(plugin, times(1)).configureRenderProps(null); + verify(plugin, times(1)).beforeRender(eq(node)); + verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class)); + + return null; + } + }).when(node).accept(any(Visitor.class)); + + impl.render(node); + + // validate that Answer was called (it has assertions about _before_ part + assertTrue(flag.get()); + + verify(plugin, times(1)).afterRender(eq(node), eq(visitor)); + } + + @Test + public void render_clears_visitor() { + // each render call should have empty-state visitor (no previous rendering info) + + final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); + + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.SPANNABLE, + mock(Parser.class), + visitor, + Collections.emptyList()); + + impl.render(mock(Node.class)); + + verify(visitor, times(1)).clear(); + } + + @Test + public void render_props() { + // render props are configured properly and cleared after render function + + final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); + + final RenderProps renderProps = mock(RenderProps.class); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + renderProps.clearAll(); + return null; + } + }).when(visitor).clear(); + + when(visitor.renderProps()).thenReturn(renderProps); + + final MarkwonPlugin plugin = mock(MarkwonPlugin.class); + + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.SPANNABLE, + mock(Parser.class), + visitor, + Collections.singletonList(plugin)); + + final AtomicBoolean flag = new AtomicBoolean(false); + final Node node = mock(Node.class); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + + flag.set(true); + + verify(visitor, times(1)).renderProps(); + verify(plugin, times(1)).configureRenderProps(eq(renderProps)); + verify(renderProps, times(0)).clearAll(); + + return null; + } + }).when(node).accept(any(Visitor.class)); + + impl.render(node); + + assertTrue(flag.get()); + + verify(renderProps, times(1)).clearAll(); + } + + @Test + public void set_parsed_markdown() { + // calls `beforeSetText` on plugins + // calls `TextView#setText(text, BUFFER_TYPE)` + // calls `afterSetText` on plugins + + final MarkwonPlugin plugin = mock(MarkwonPlugin.class); + final MarkwonImpl impl = new MarkwonImpl( + TextView.BufferType.EDITABLE, + mock(Parser.class), + mock(MarkwonVisitor.class, RETURNS_MOCKS), + Collections.singletonList(plugin)); + + final TextView textView = mock(TextView.class); + final AtomicBoolean flag = new AtomicBoolean(false); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + + flag.set(true); + + verify(plugin, times(1)).beforeSetText(eq(textView), any(Spanned.class)); + verify(plugin, times(0)).afterSetText(any(TextView.class)); + + final ArgumentCaptor captor = + ArgumentCaptor.forClass(TextView.BufferType.class); + + verify(textView).setText(any(CharSequence.class), captor.capture()); + assertEquals(TextView.BufferType.EDITABLE, captor.getValue()); + + return null; + } + }).when(textView).setText(any(CharSequence.class), any(TextView.BufferType.class)); + + impl.setParsedMarkdown(textView, mock(Spanned.class)); + + assertTrue(flag.get()); + + verify(plugin, times(1)).afterSetText(eq(textView)); + } +} \ No newline at end of file diff --git a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java index 7e0a2af2..38d36720 100644 --- a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java +++ b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java @@ -22,7 +22,6 @@ import ru.noties.markwon.MarkwonConfiguration; import ru.noties.markwon.MarkwonSpansFactory; import ru.noties.markwon.MarkwonVisitor; import ru.noties.markwon.RenderProps; -import ru.noties.markwon.RenderPropsImpl; import ru.noties.markwon.SpanFactory; import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.core.CorePluginBridge; @@ -90,7 +89,7 @@ public class SyntaxHighlightTest { final MarkwonVisitor visitor = new AbstractMarkwonVisitorImpl( configuration, - new RenderPropsImpl(), + mock(RenderProps.class), visitorMap); final SpannableBuilder builder = visitor.builder();