/*
Copyright 2013-2016 Jason Leyba
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.jsdossier.markdown;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import org.commonmark.Extension;
import org.commonmark.html.HtmlRenderer;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.List;
/**
* Tests for {@link MarkdownTableExtension}.
*/
@RunWith(JUnit4.class)
public class MarkdownTableExtensionTest {
private static final List<? extends Extension> EXTENSIONS =
ImmutableList.of(new MarkdownTableExtension());
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
private static final HtmlRenderer RENDERER = HtmlRenderer.builder()
.escapeHtml(false)
.extensions(EXTENSIONS)
.build();
@Test
public void requiresASeparatorLine() {
assertHtml("A|B", "<p>A|B</p>");
assertHtml("|A|B", "<p>|A|B</p>");
assertHtml("|A|B|", "<p>|A|B|</p>");
assertHtml(" |A|B|", "<p>|A|B|</p>");
assertHtml("A | B", "<p>A | B</p>");
assertHtml("| A | B ", "<p>| A | B</p>");
}
@Test
public void firstLineMayHaveUpTo3SpacesBeforeFirstCharacter() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
assertHtml("A|B\n-|-", html);
assertHtml(" A|B\n-|-", html);
assertHtml(" A|B\n-|-", html);
assertHtml(" A|B\n-|-", html);
assertHtml(" A|B\n-|-",
"<pre><code>A|B",
"</code></pre>",
"<p>-|-</p>");
}
@Test
public void separatorLineMayHaveUpTo3SpacesBeforeFirstCharacter() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
assertHtml("A|B\n-|-", html);
assertHtml("A|B\n -|-", html);
assertHtml("A|B\n -|-", html);
assertHtml("A|B\n -|-", html);
assertHtml("A|B\n -|-", "<p>A|B\n-|-</p>");
assertHtml("A|B\n|-|-", html);
assertHtml("A|B\n |-|-", html);
assertHtml("A|B\n |-|-", html);
assertHtml("A|B\n |-|-", html);
assertHtml("A|B\n |-|-", "<p>A|B\n|-|-</p>");
}
@Test
public void singleColumnRequiresAtLeastOnePipe() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
assertHtml("|A\n-", "<h2>|A</h2>");
assertHtml("A\n-|", "<p>A\n-|</p>");
assertHtml("|A\n|-", html);
assertHtml("A|\n|-", html);
assertHtml("|A|\n|-", html);
assertHtml("|A\n|-", html);
assertHtml("|A\n-|", html);
assertHtml("|A\n|-|", html);
assertHtml("|A|\n|-", html);
assertHtml("|A|\n-|", html);
assertHtml("|A|\n|-|", html);
}
@Test
public void singleColumnWithBody() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>B</td></tr>",
"</tbody>",
"</table>");
assertHtml("A|\n-|\n|B|", html);
assertHtml("A|\n-|\nB|", html);
assertHtml("A|\n-|\n|B", html);
assertHtml("A|\n|-|\n|B|", html);
assertHtml("A|\n|-|\nB|", html);
assertHtml("A|\n|-|\n|B", html);
assertHtml("|A|\n-|\n|B|", html);
assertHtml("A|\n-|\nB|", html);
assertHtml("|A\n-|\n|B", html);
// No pipe for body
assertHtml("|A\n|-\nbody",
"<table>",
"<thead>",
"<tr><th>A</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>",
"<p>body</p>");
}
@Test
public void headerCellsCanSpanMultipleColumns() {
assertHtml("|A|B||C|||\n|-|-|-|-|-|-",
"<table>",
"<thead>",
"<tr><th>A</th><th colspan=\"2\">B</th><th colspan=\"3\">C</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void alignmentAppliesToHeaderCells() {
assertHtml("A|B|C|D\n-|:-|:-:|-:",
"<table>",
"<thead>",
"<tr><th>A</th><th align=\"left\">B</th>" +
"<th align=\"center\">C</th><th align=\"right\">D</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void pullsAlignmentFromLeftmostCellInSpan() {
assertHtml("A||B\n-|-|-:",
"<table>",
"<thead>",
"<tr><th colspan=\"2\">A</th><th align=\"right\">B</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void moreSeparatorsThanHeaderCells() {
assertHtml("A|B\n-|-|-",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void moreHeaderCellsThanSeparators() {
assertHtml("A|B|C|D\n-|-",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th><th>C</th><th>D</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void moreHeaderCellsThanSeparatorsWithColspans() {
assertHtml("A|B|C||D|||\n-|-",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th><th colspan=\"2\">C</th><th colspan=\"3\">D</th></tr>",
"</thead>",
"<tbody></tbody>",
"</table>");
}
@Test
public void twoByTwo() {
assertHtml("A|B\n-|-\nc|d",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void twoColumnsThreeRows() {
assertHtml("A|B\n-|-\nc|d\n|e|f|",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"<tr><td>e</td><td>f</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void aRowWithExtraColumns() {
assertHtml("A|B\n-|-\nc|d\n|e|f|g|h",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"<tr><td>e</td><td>f</td><td>g</td><td>h</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void cellsCanHaveWhitespacePadding() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
assertHtml("A|B\n-|-\nc|d", html);
assertHtml("A| B\n-|-\nc|d", html);
assertHtml("A| B\n-|-\nc|d", html);
assertHtml("A| B\n-|-\nc|d", html);
assertHtml("A| B\n-|-\nc|d", html);
assertHtml("A|B\n-|-\nc| d", html);
assertHtml("A|B\n-|-\nc| d", html);
assertHtml("A|B\n-|-\nc| d", html);
assertHtml("A|B\n-|-\nc| d", html);
assertHtml("A |B\n-|-\nc| d", html);
assertHtml("A |B\n-|-\nc| d", html);
assertHtml("A |B\n-|-\nc| d", html);
assertHtml("A|B\n-|-\nc | d", html);
assertHtml("A|B\n-|-\nc | d", html);
assertHtml("A|B\n-|-\nc | d", html);
assertHtml("A|B\n-|-\nc | d", html);
}
@Test
public void leadingCellInBodyMayBeIndentedPastCodeBlockLimit() {
assertHtml("A|B\n-|-\n c|d",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void parsesInlineContentWithinCells() {
assertHtml(
"*A* | `B`\n --- | ---\n __c__ | ***d***",
"<table>",
"<thead>",
"<tr><th><em>A</em></th><th><code>B</code></th></tr>",
"</thead>",
"<tbody>",
"<tr><td><strong>c</strong></td><td><strong><em>d</em></strong></td></tr>",
"</tbody>",
"</table>");
}
@Test
public void respectsEscapedPipes() {
assertHtml("A\\||B\\\\|\n-|-\nc|\\|d",
"<table>",
"<thead>",
"<tr><th>A|</th><th>B\\</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>|d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void tableInsideBlockQuote() {
assertHtml("> A|B\n> -|-\n> c|d",
"<blockquote>",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>",
"</blockquote>");
}
@Test
public void separatorCanHaveUpToThreeLeadingSpacesInsideBlockQuote() {
String html = Joiner.on('\n').join(
"<blockquote>",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>",
"</blockquote>");
assertHtml("> A|B\n>-|-\n> c|d", html);
assertHtml("> A|B\n> -|-\n> c|d", html);
assertHtml("> A|B\n> -|-\n> c|d", html);
assertHtml("> A|B\n> -|-\n> c|d", html);
assertHtml("> A|B\n> -|-\n> c|d", html);
// Too many spaces on the separator line.
assertHtml("> A|B\n> -|-\n> c|d",
"<blockquote>",
"<p>A|B",
"-|-",
"c|d</p>",
"</blockquote>");
}
@Test
public void tableInsideNestedBlockQuote() {
assertHtml("> > A|B\n> > -|-\n> > c|d",
"<blockquote>",
"<blockquote>",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>",
"</blockquote>",
"</blockquote>");
}
@Test
public void tableInsideList() {
assertHtml("1. A|B\n -|-\n c|d",
"<ol>",
"<li>",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>",
"</li>",
"</ol>");
}
@Test
public void tableWithCaption() {
assertHtml("A|B\n-|-\nc|d\n[hello, world]",
"<table>",
"<caption>hello, world</caption>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void tableWithCaptionWithInlineFormatting() {
assertHtml("A|B\n-|-\nc|d\n[*hello*, `world`]",
"<table>",
"<caption><em>hello</em>, <code>world</code></caption>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void tableWithCaption_usesLastCaptionEncountered() {
assertHtml("A|B\n-|-\n[first caption]\nc|d\n[hello, world]\n[final caption]",
"<table>",
"<caption>final caption</caption>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
}
@Test
public void tableCaptionMayBeIndentedUpToThreeSpaces() {
String html = Joiner.on('\n').join(
"<table>",
"<caption>hello, world</caption>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>");
assertHtml("A|B\n-|-\nc|d\n[hello, world]", html);
assertHtml("A|B\n-|-\nc|d\n [hello, world]", html);
assertHtml("A|B\n-|-\nc|d\n [hello, world]", html);
assertHtml("A|B\n-|-\nc|d\n [hello, world]", html);
assertHtml("A|B\n-|-\nc|d\n [hello, world]",
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>c</td><td>d</td></tr>",
"</tbody>",
"</table>",
"<pre><code>[hello, world]",
"</code></pre>");
}
@Test
public void tableParsingIsConsistentInThePresenceOfLeadingWhitespace() {
String html = Joiner.on('\n').join(
"<table>",
"<thead>",
"<tr><th>A</th><th>B</th><th>C</th></tr>",
"</thead>",
"<tbody>",
"<tr><td>a</td><td>b</td><td>c</td></tr>",
"<tr><td>d</td><td colspan=\"2\">e</td></tr>",
"</tbody>",
"</table>");
assertHtml(
Joiner.on('\n').join(
"| A | B | C |",
"| - | - | - |",
"| a | b | c |",
"| d | e ||"),
html);
assertHtml(
Joiner.on('\n').join(
" | A | B | C |",
" | - | - | - |",
" | a | b | c |",
" | d | e ||"),
html);
assertHtml(
Joiner.on('\n').join(
" | A | B | C |",
" | - | - | - |",
" | a | b | c |",
" | d | e ||"),
html);
assertHtml(
Joiner.on('\n').join(
" | A | B | C |",
" | - | - | - |",
" | a | b | c |",
" | d | e ||"),
html);
}
private static void assertHtml(String input, String... output) {
Node root = PARSER.parse(input);
String html = RENDERER.render(root).trim();
assertThat(html).isEqualTo(Joiner.on('\n').join(output));
}
}