blob: e0e8e118c4602d3d9f7372156c5627c115cce12f [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Unit tests for markdown.
library markdown.test.markdown_test;
import 'package:unittest/unittest.dart';
import 'package:markdown/markdown.dart';
import 'util.dart';
/// Most of these tests are based on observing how showdown behaves:
/// http://softwaremaniacs.org/playground/showdown-highlight/
void main() {
group('Paragraphs', () {
validate(
'consecutive lines form a single paragraph',
'''
This is the first line.
This is the second line.
''',
'''
<p>This is the first line.
This is the second line.</p>
''');
// TODO(rnystrom): The rules here for what happens to lines following a
// paragraph appear to be completely arbitrary in markdown. If it makes the
// code significantly cleaner, we should consider ourselves free to change
// these tests.
validate(
'are terminated by a header',
'''
para
# header
''',
'''
<p>para</p>
<h1>header</h1>
''');
validate(
'are terminated by a setext header',
'''
para
header
==
''',
'''
<p>para</p>
<h1>header</h1>
''');
validate(
'are terminated by a hr',
'''
para
___
''',
'''
<p>para</p>
<hr />
''');
validate(
'consume an unordered list',
'''
para
* list
''',
'''
<p>para
* list</p>
''');
validate(
'consume an ordered list',
'''
para
1. list
''',
'''
<p>para
1. list</p>
''');
// Windows line endings have a \r\n format
// instead of the unix \n format.
validate(
'take account of windows line endings',
'''
line1\r\n\r\n line2\r\n
''',
'''
<p>line1</p>
<p>line2</p>
''');
});
group('Setext headers', () {
validate(
'h1',
'''
text
===
''',
'''
<h1>text</h1>
''');
validate(
'h2',
'''
text
---
''',
'''
<h2>text</h2>
''');
validate(
'h1 on first line becomes text',
'''
===
''',
'''
<p>===</p>
''');
validate(
'h2 on first line becomes text',
'''
-
''',
'''
<p>-</p>
''');
validate(
'h1 turns preceding list into text',
'''
- list
===
''',
'''
<h1>- list</h1>
''');
validate(
'h2 turns preceding list into text',
'''
- list
===
''',
'''
<h1>- list</h1>
''');
validate(
'h1 turns preceding blockquote into text',
'''
> quote
===
''',
'''
<h1>> quote</h1>
''');
validate(
'h2 turns preceding blockquote into text',
'''
> quote
===
''',
'''
<h1>> quote</h1>
''');
});
group('Headers', () {
validate(
'h1',
'''
# header
''',
'''
<h1>header</h1>
''');
validate(
'h2',
'''
## header
''',
'''
<h2>header</h2>
''');
validate(
'h3',
'''
### header
''',
'''
<h3>header</h3>
''');
validate(
'h4',
'''
#### header
''',
'''
<h4>header</h4>
''');
validate(
'h5',
'''
##### header
''',
'''
<h5>header</h5>
''');
validate(
'h6',
'''
###### header
''',
'''
<h6>header</h6>
''');
validate(
'trailing "#" are removed',
'''
# header ######
''',
'''
<h1>header</h1>
''');
});
group('Unordered lists', () {
validate(
'asterisk, plus and hyphen',
'''
* star
- dash
+ plus
''',
'''
<ul>
<li>star</li>
<li>dash</li>
<li>plus</li>
</ul>
''');
validate(
'allow numbered lines after first',
'''
* a
1. b
''',
'''
<ul>
<li>a</li>
<li>b</li>
</ul>
''');
validate(
'allow a tab after the marker',
'''
*\ta
+\tb
-\tc
1.\td
''',
'''
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul>
''');
validate(
'wrap items in paragraphs if blank lines separate',
'''
* one
* two
''',
'''
<ul>
<li><p>one</p></li>
<li><p>two</p></li>
</ul>
''');
validate(
'force paragraph on item before and after blank lines',
'''
* one
* two
* three
''',
'''
<ul>
<li>one</li>
<li>
<p>two</p>
</li>
<li>
<p>three</p>
</li>
</ul>
''');
validate(
'do not force paragraph if item is already block',
'''
* > quote
* # header
''',
'''
<ul>
<li><blockquote><p>quote</p></blockquote></li>
<li><h1>header</h1></li>
</ul>
''');
validate(
'can contain multiple paragraphs',
'''
* one
two
* three
''',
'''
<ul>
<li>
<p>one</p>
<p>two</p>
</li>
<li>
<p>three</p>
</li>
</ul>
''');
validate(
'can span newlines',
'''
* one
two
* three
''',
'''
<ul>
<li>
<p>one
two</p>
</li>
<li>
three
</li>
</ul>
''');
// TODO(rnystrom): This is how most other markdown parsers handle
// this but that seems like a nasty special case. For now, let's not
// worry about it.
/*
validate('can nest using indentation', '''
* parent
* child
''', '''
<ul>
<li>parent
<ul><li>child</li></ul></li>
</ul>
''');
*/
});
group('Ordered lists', () {
validate(
'start with numbers',
'''
1. one
45. two
12345. three
''',
'''
<ol>
<li>one</li>
<li>two</li>
<li>three</li>
</ol>
''');
validate(
'allow unordered lines after first',
'''
1. a
* b
''',
'''
<ol>
<li>a</li>
<li>b</li>
</ol>
''');
});
group('Blockquotes', () {
validate(
'single line',
'''
> blah
''',
'''
<blockquote>
<p>blah</p>
</blockquote>
''');
validate(
'with two paragraphs',
'''
> first
>
> second
''',
'''
<blockquote>
<p>first</p>
<p>second</p>
</blockquote>
''');
validate(
'nested',
'''
> one
>> two
> > > three
''',
'''
<blockquote>
<p>one</p>
<blockquote>
<p>two</p>
<blockquote>
<p>three</p>
</blockquote>
</blockquote>
</blockquote>
''');
});
group('Code blocks', () {
validate(
'single line',
'''
code
''',
'''
<pre><code>code</code></pre>
''');
validate(
'include leading whitespace after indentation',
'''
zero
one
two
three
''',
'''
<pre><code>zero
one
two
three</code></pre>
''');
validate(
'code blocks separated by newlines form one block',
'''
zero
one
two
three
''',
'''
<pre><code>zero
one
two
three</code></pre>
''');
validate(
'code blocks separated by two newlines form multiple blocks',
'''
zero
one
two
three
''',
'''
<pre><code>zero
one</code></pre>
<pre><code>two</code></pre>
<pre><code>three</code></pre>
''');
validate(
'escape HTML characters',
'''
<&>
''',
'''
<pre><code>&lt;&amp;&gt;</code></pre>
''');
});
group('Fenced code blocks', () {
validate(
'without an optional language identifier',
'''
```
code
```
''',
'''
<pre><code>code
</code></pre>
''');
validate(
'with an optional language identifier',
'''
```dart
code
```
''',
'''
<pre class="dart"><code>code
</code></pre>
''');
validate(
'escape HTML characters',
'''
```
<&>
```
''',
'''
<pre><code>&lt;&amp;&gt;
</code></pre>
''');
validate(
'Pandoc style without language identifier',
'''
~~~~~
code
~~~~~
''',
'''
<pre><code>code
</code></pre>
''');
validate(
'Pandoc style with language identifier',
'''
~~~~~dart
code
~~~~~
''',
'''
<pre class="dart"><code>code
</code></pre>
''');
validate(
'Pandoc style with inner tildes row',
'''
~~~~~
~~~
code
~~~
~~~~~
''',
'''
<pre><code>~~~
code
~~~
</code></pre>
''');
});
group('Horizontal rules', () {
validate(
'from dashes',
'''
---
''',
'''
<hr />
''');
validate(
'from asterisks',
'''
***
''',
'''
<hr />
''');
validate(
'from underscores',
'''
___
''',
'''
<hr />
''');
validate(
'can include up to two spaces',
'''
_ _ _
''',
'''
<hr />
''');
});
group('Block-level HTML', () {
validate(
'single line',
'''
<table></table>
''',
'''
<table></table>
''');
validate(
'multi-line',
'''
<table>
blah
</table>
''',
'''
<table>
blah
</table>
''');
validate(
'blank line ends block',
'''
<table>
blah
</table>
para
''',
'''
<table>
blah
</table>
<p>para</p>
''');
validate(
'HTML can be bogus',
'''
<bogus>
blah
</weird>
para
''',
'''
<bogus>
blah
</weird>
<p>para</p>
''');
});
group('Strong', () {
validate(
'using asterisks',
'''
before **strong** after
''',
'''
<p>before <strong>strong</strong> after</p>
''');
validate(
'using underscores',
'''
before __strong__ after
''',
'''
<p>before <strong>strong</strong> after</p>
''');
validate(
'unmatched asterisks',
'''
before ** after
''',
'''
<p>before ** after</p>
''');
validate(
'unmatched underscores',
'''
before __ after
''',
'''
<p>before __ after</p>
''');
validate(
'multiple spans in one text',
'''
a **one** b __two__ c
''',
'''
<p>a <strong>one</strong> b <strong>two</strong> c</p>
''');
validate(
'multi-line',
'''
before **first
second** after
''',
'''
<p>before <strong>first
second</strong> after</p>
''');
});
group('Emphasis and strong', () {
validate(
'single asterisks',
'''
before *em* after
''',
'''
<p>before <em>em</em> after</p>
''');
validate(
'single underscores',
'''
before _em_ after
''',
'''
<p>before <em>em</em> after</p>
''');
validate(
'double asterisks',
'''
before **strong** after
''',
'''
<p>before <strong>strong</strong> after</p>
''');
validate(
'double underscores',
'''
before __strong__ after
''',
'''
<p>before <strong>strong</strong> after</p>
''');
validate(
'unmatched asterisk',
'''
before *after
''',
'''
<p>before *after</p>
''');
validate(
'unmatched underscore',
'''
before _after
''',
'''
<p>before _after</p>
''');
validate(
'multiple spans in one text',
'''
a *one* b _two_ c
''',
'''
<p>a <em>one</em> b <em>two</em> c</p>
''');
validate(
'multi-line',
'''
before *first
second* after
''',
'''
<p>before <em>first
second</em> after</p>
''');
validate(
'not processed when surrounded by spaces',
'''
a * b * c _ d _ e
''',
'''
<p>a * b * c _ d _ e</p>
''');
validate(
'strong then emphasis',
'''
**strong***em*
''',
'''
<p><strong>strong</strong><em>em</em></p>
''');
validate(
'emphasis then strong',
'''
*em***strong**
''',
'''
<p><em>em</em><strong>strong</strong></p>
''');
validate(
'emphasis inside strong',
'''
**strong *em***
''',
'''
<p><strong>strong <em>em</em></strong></p>
''');
validate(
'mismatched in nested',
'''
*a _b* c_
''',
'''
<p><em>a _b</em> c_</p>
''');
validate(
'cannot nest tags of same type',
'''
*a _b *c* d_ e*
''',
'''
<p><em>a _b </em>c<em> d_ e</em></p>
''');
});
group('Inline code', () {
validate(
'simple case',
'''
before `source` after
''',
'''
<p>before <code>source</code> after</p>
''');
validate(
'unmatched backtick',
'''
before ` after
''',
'''
<p>before ` after</p>
''');
validate(
'multiple spans in one text',
'''
a `one` b `two` c
''',
'''
<p>a <code>one</code> b <code>two</code> c</p>
''');
validate(
'multi-line',
'''
before `first
second` after
''',
'''
<p>before <code>first
second</code> after</p>
''');
validate(
'simple double backticks',
'''
before ``source`` after
''',
'''
<p>before <code>source</code> after</p>
''');
validate(
'double backticks',
'''
before ``can `contain` backticks`` after
''',
'''
<p>before <code>can `contain` backticks</code> after</p>
''');
validate(
'double backticks with spaces',
'''
before `` `tick` `` after
''',
'''
<p>before <code>`tick`</code> after</p>
''');
validate(
'multiline double backticks with spaces',
'''
before ``in `tick`
another`` after
''',
'''
<p>before <code>in `tick`
another</code> after</p>
''');
validate(
'ignore markup inside code',
'''
before `*b* _c_` after
''',
'''
<p>before <code>*b* _c_</code> after</p>
''');
validate(
'escape HTML characters',
'''
`<&>`
''',
'''
<p><code>&lt;&amp;&gt;</code></p>
''');
validate(
'escape HTML tags',
'''
'*' `<em>`
''',
'''
<p>'*' <code>&lt;em&gt;</code></p>
''');
});
group('HTML encoding', () {
validate(
'less than and ampersand are escaped',
'''
< &
''',
'''
<p>&lt; &amp;</p>
''');
validate(
'greater than is not escaped',
'''
not you >
''',
'''
<p>not you ></p>
''');
validate(
'existing entities are untouched',
'''
&amp;
''',
'''
<p>&amp;</p>
''');
});
group('Autolinks', () {
validate(
'basic link',
'''
before <http://foo.com/> after
''',
'''
<p>before <a href="http://foo.com/">http://foo.com/</a> after</p>
''');
validate(
'handles ampersand in url',
'''
<http://foo.com/?a=1&b=2>
''',
'''
<p><a href="http://foo.com/?a=1&b=2">http://foo.com/?a=1&amp;b=2</a></p>
''');
});
group('Reference links', () {
validate(
'double quotes for title',
'''
links [are] [a] awesome
[a]: http://foo.com "woo"
''',
'''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate(
'single quoted title',
"""
links [are] [a] awesome
[a]: http://foo.com 'woo'
""",
'''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate(
'parentheses for title',
'''
links [are] [a] awesome
[a]: http://foo.com (woo)
''',
'''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate(
'no title',
'''
links [are] [a] awesome
[a]: http://foo.com
''',
'''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate(
'unknown link becomes plaintext',
'''
[not] [known]
''',
'''
<p>[not] [known]</p>
''');
validate(
'can style link contents',
'''
links [*are*] [a] awesome
[a]: http://foo.com
''',
'''
<p>links <a href="http://foo.com"><em>are</em></a> awesome</p>
''');
validate(
'inline styles after a bad link are processed',
'''
[bad] `code`
''',
'''
<p>[bad] <code>code</code></p>
''');
validate(
'empty reference uses text from link',
'''
links [are][] awesome
[are]: http://foo.com
''',
'''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate(
'references are case-insensitive',
'''
links [ARE][] awesome
[are]: http://foo.com
''',
'''
<p>links <a href="http://foo.com">ARE</a> awesome</p>
''');
});
group('Inline links', () {
validate(
'double quotes for title',
'''
links [are](http://foo.com "woo") awesome
''',
'''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate(
'no title',
'''
links [are] (http://foo.com) awesome
''',
'''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate(
'can style link contents',
'''
links [*are*](http://foo.com) awesome
''',
'''
<p>links <a href="http://foo.com"><em>are</em></a> awesome</p>
''');
});
group('Inline Images', () {
validate(
'image',
'''
![](http://foo.com/foo.png)
''',
'''
<p>
<a href="http://foo.com/foo.png">
<img src="http://foo.com/foo.png"></img>
</a>
</p>
''');
validate(
'alternate text',
'''
![alternate text](http://foo.com/foo.png)
''',
'''
<p>
<a href="http://foo.com/foo.png">
<img alt="alternate text" src="http://foo.com/foo.png"></img>
</a>
</p>
''');
validate(
'title',
'''
![](http://foo.com/foo.png "optional title")
''',
'''
<p>
<a href="http://foo.com/foo.png" title="optional title">
<img src="http://foo.com/foo.png" title="optional title"></img>
</a>
</p>
''');
validate(
'invalid alt text',
'''
![`alt`](http://foo.com/foo.png)
''',
'''
<p>
<a href="http://foo.com/foo.png">
<img src="http://foo.com/foo.png"></img>
</a>
</p>
''');
});
group('Reference Images', () {
validate(
'image',
'''
![][foo]
[foo]: http://foo.com/foo.png
''',
'''
<p>
<a href="http://foo.com/foo.png">
<img src="http://foo.com/foo.png"></img>
</a>
</p>
''');
validate(
'alternate text',
'''
![alternate text][foo]
[foo]: http://foo.com/foo.png
''',
'''
<p>
<a href="http://foo.com/foo.png">
<img alt="alternate text" src="http://foo.com/foo.png"></img>
</a>
</p>
''');
validate(
'title',
'''
![][foo]
[foo]: http://foo.com/foo.png "optional title"
''',
'''
<p>
<a href="http://foo.com/foo.png" title="optional title">
<img src="http://foo.com/foo.png" title="optional title"></img>
</a>
</p>
''');
validate(
'invalid alt text',
'''
![`alt`][foo]
[foo]: http://foo.com/foo.png "optional title"
''',
'''
<p>
<a href="http://foo.com/foo.png" title="optional title">
<img src="http://foo.com/foo.png" title="optional title"></img>
</a>
</p>
''');
});
group('Resolver', () {
nyanResolver(text) => new Text('~=[,,_${text}_,,]:3');
validate(
'simple link resolver',
'''
resolve [this] thing
''',
'''
<p>resolve ~=[,,_this_,,]:3 thing</p>
''',
linkResolver: nyanResolver);
validate(
'simple image resolver',
'''
resolve ![this] thing
''',
'''
<p>resolve ~=[,,_this_,,]:3 thing</p>
''',
imageLinkResolver: nyanResolver);
validate(
'can resolve link containing inline tags',
'''
resolve [*star* _underline_] thing
''',
'''
<p>resolve ~=[,,_*star* _underline__,,]:3 thing</p>
''',
linkResolver: nyanResolver);
});
group('Custom inline syntax', () {
var nyanSyntax = [new TextSyntax('nyan', sub: '~=[,,_,,]:3')];
validate(
'simple inline syntax',
'''
nyan
''',
'''
<p>~=[,,_,,]:3</p>
''',
inlineSyntaxes: nyanSyntax);
validate('dart custom links', 'links [are<foo>] awesome',
'<p>links <a>are&lt;foo></a> awesome</p>',
linkResolver: (text) =>
new Element.text('a', text.replaceAll('<', '&lt;')));
// TODO(amouravski): need more tests here for custom syntaxes, as some
// things are not quite working properly. The regexps are sometime a little
// too greedy, I think.
});
group('Inline only', () {
validate(
'simple line',
'''
This would normally create a paragraph.
''',
'''
This would normally create a paragraph.
''',
inlineOnly: true);
validate(
'strong and em',
'''
This would _normally_ create a **paragraph**.
''',
'''
This would <em>normally</em> create a <strong>paragraph</strong>.
''',
inlineOnly: true);
validate(
'link',
'''
This [link](http://www.example.com/) will work normally.
''',
'''
This <a href="http://www.example.com/">link</a> will work normally.
''',
inlineOnly: true);
validate(
'references do not work',
'''
[This][] shouldn't work, though.
''',
'''
[This][] shouldn't work, though.
''',
inlineOnly: true);
validate(
'less than and ampersand are escaped',
'''
< &
''',
'''
&lt; &amp;
''',
inlineOnly: true);
validate(
'keeps newlines',
'''
This paragraph
continues after a newline.
''',
'''
This paragraph
continues after a newline.
''',
inlineOnly: true);
validate(
'ignores block-level markdown syntax',
'''
1. This will not be an <ol>.
''',
'''
1. This will not be an &lt;ol>.
''',
inlineOnly: true);
});
}