| Index: utils/markdown/test/markdown_tests.dart
|
| diff --git a/utils/markdown/test/markdown_tests.dart b/utils/markdown/test/markdown_tests.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..10e5b9e25ba2fe9cba6bce5f42c73e240eb600cf
|
| --- /dev/null
|
| +++ b/utils/markdown/test/markdown_tests.dart
|
| @@ -0,0 +1,787 @@
|
| +// 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_tests');
|
| +
|
| +#import('../lib.dart');
|
| +
|
| +// TODO(rnystrom): Better path to unittest.
|
| +#import('../../../client/testing/unittest/unittest_vm.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>
|
| + ''');
|
| + });
|
| +
|
| + 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>
|
| + ''');
|
| +
|
| + // 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('escape HTML characters', '''
|
| + <&>
|
| + ''', '''
|
| + <pre><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('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('ignore markup inside code', '''
|
| + before `*b* _c_` after
|
| + ''', '''
|
| + <p>before <code>*b* _c_</code> after</p>
|
| + ''');
|
| +
|
| + validate('escape HTML characters', '''
|
| + `<&>`
|
| + ''', '''
|
| + <p><code><&></code></p>
|
| + ''');
|
| + });
|
| +
|
| + group('HTML encoding', () {
|
| + validate('less than and ampersand are escaped', '''
|
| + < &
|
| + ''', '''
|
| + <p>< &</p>
|
| + ''');
|
| + validate('greater than is not escaped', '''
|
| + not you >
|
| + ''', '''
|
| + <p>not you ></p>
|
| + ''');
|
| + validate('existing entities are untouched', '''
|
| + &
|
| + ''', '''
|
| + <p>&</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&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>
|
| + ''');
|
| + });
|
| +
|
| + 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>
|
| + ''');
|
| + });
|
| +}
|
| +
|
| +validate(String description, String markdown, String html) {
|
| + test(description, () {
|
| + markdown = cleanUpLiteral(markdown);
|
| + html = cleanUpLiteral(html);
|
| +
|
| + var result = markdownToHtml(markdown);
|
| + var passed = compareOutput(html, result);
|
| +
|
| + if (!passed) {
|
| + // Remove trailing newline.
|
| + html = html.substring(0, html.length - 1);
|
| +
|
| + print('FAIL: $description');
|
| + print(' expect: ${html.replaceAll("\n", "\n ")}');
|
| + print(' actual: ${result.replaceAll("\n", "\n ")}');
|
| + print('');
|
| + }
|
| +
|
| + expect(passed).isTrue();
|
| + });
|
| +}
|
| +
|
| +/// The tests uses triple-quoted strings, which will include leading indentation
|
| +/// to make them look nice in code. But we don't want the markdown parser to
|
| +/// actually see that, so this cleans it all up.
|
| +///
|
| +/// Note that this is very sensitive to how the literals are styled. They should
|
| +/// be:
|
| +/// '''
|
| +/// Text starts on own line. Lines up with subsequent lines.
|
| +/// Lines are indented exactly 8 characters from the left margin.
|
| +/// Close is on the same line.'''
|
| +///
|
| +cleanUpLiteral(String text) {
|
| + var lines = text.split('\n');
|
| + for (var j = 0; j < lines.length; j++) {
|
| + if (lines[j].length > 8) {
|
| + lines[j] = lines[j].substring(8, lines[j].length);
|
| + } else {
|
| + lines[j] = '';
|
| + }
|
| + }
|
| +
|
| + return Strings.join(lines, '\n');
|
| +}
|
| +
|
| +/// Does a loose comparison of the two strings of HTML. Ignores differences in
|
| +/// newlines and indentation.
|
| +compareOutput(String a, String b) {
|
| + int i = 0;
|
| + int j = 0;
|
| +
|
| + skipIgnored(String s, int i) {
|
| + // Ignore newlines.
|
| + while ((i < s.length) && (s[i] == '\n')) {
|
| + i++;
|
| + // Ignore indentation.
|
| + while ((i < s.length) && (s[i] == ' ')) i++;
|
| + }
|
| +
|
| + return i;
|
| + }
|
| +
|
| + while (true) {
|
| + i = skipIgnored(a, i);
|
| + j = skipIgnored(b, j);
|
| +
|
| + // If one string runs out of non-ignored strings, the other must too.
|
| + if (i == a.length) return j == b.length;
|
| + if (j == b.length) return i == a.length;
|
| +
|
| + if (a[i] != b[j]) return false;
|
| + i++;
|
| + j++;
|
| + }
|
| +}
|
|
|