Explorar el Código

Adding PHP InlineTests and TokenTests

main
Rocketsoup hace 1 año
padre
commit
86ddf89016
Se han modificado 6 ficheros con 434 adiciones y 61 borrados
  1. 2
    3
      jstest/InlineTests.js
  2. 38
    16
      php/markdown.php
  3. 250
    0
      phptest/InlineTests.php
  4. 139
    0
      phptest/TokenTests.php
  5. 0
    41
      phptest/UtilsTests.php
  6. 5
    1
      runphptests.sh

+ 2
- 3
jstest/InlineTests.js Ver fichero

46
 
46
 
47
 	test_strongEmphasis_tightNesting() {
47
 	test_strongEmphasis_tightNesting() {
48
 		let markdown = 'Lorem ***ipsum*** dolor';
48
 		let markdown = 'Lorem ***ipsum*** dolor';
49
-		let expected1 = 'Lorem <strong><em>ipsum</em></strong> dolor';
50
-		let expected2 = 'Lorem <em><strong>ipsum</strong></em> dolor';
49
+		let expected = 'Lorem <strong><em>ipsum</em></strong> dolor';
51
 		let actual = this.md(markdown);
50
 		let actual = this.md(markdown);
52
-		this.assertTrue(actual == expected1 || actual == expected2);
51
+		this.assertEqual(actual, expected);
53
 	}
52
 	}
54
 
53
 
55
 	test_strongEmphasis_lopsidedNesting1() {
54
 	test_strongEmphasis_lopsidedNesting1() {

+ 38
- 16
php/markdown.php Ver fichero

11
 	public const baseEmailRegex = '(?:(?:[^<>()\\[\\]\\\\.,;:\\s@"]+(?:\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(?:".+"))@(?:(?:\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(?:(?:[a-z\\-0-9]+\\.)+[a-z]{2,}))';
11
 	public const baseEmailRegex = '(?:(?:[^<>()\\[\\]\\\\.,;:\\s@"]+(?:\\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(?:".+"))@(?:(?:\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(?:(?:[a-z\\-0-9]+\\.)+[a-z]{2,}))';
12
 
12
 
13
 	/**
13
 	/**
14
+	 * Escapes special HTML characters.
15
+	 *
16
+	 * @param string $str  string to escape
17
+	 * @param bool $encodeNewlinesAsBreaks  whether to convert newline characters to `<br>` tags
18
+	 * @return string  escaped HTML
19
+	 */
20
+	public static function escapeHTML($str, $encodeNewlinesAsBreaks=false) {
21
+		if (!is_string($str)) return '';
22
+		$html = $str;
23
+		$html = mb_ereg_replace('&', '&amp;', $html);
24
+		$html = mb_ereg_replace('<', '&lt;', $html);
25
+		$html = mb_ereg_replace('>', '&gt;', $html);
26
+		$html = mb_ereg_replace('"', '&quot;', $html);
27
+		if ($encodeNewlinesAsBreaks) {
28
+			$html = str_replace("\n", "<br>\n", $html);
29
+		}
30
+		return $html;
31
+	}
32
+
33
+	/**
14
 	 * Encodes characters as HTML numeric entities to make it marginally more
34
 	 * Encodes characters as HTML numeric entities to make it marginally more
15
 	 * difficult for web scrapers to grab sensitive info. If `$text` starts with
35
 	 * difficult for web scrapers to grab sensitive info. If `$text` starts with
16
 	 * `mailto:` only the email address following it will be obfuscated.
36
 	 * `mailto:` only the email address following it will be obfuscated.
257
 				if ($bracketCount > 0) {
277
 				if ($bracketCount > 0) {
258
 					$bracketCount--;
278
 					$bracketCount--;
259
 				} else {
279
 				} else {
260
-					return [ mb_substr($line, 0, $p + 1), mb_substr($line, 1, $p - 1) ];
280
+					$all = mb_substr($line, 0, $p + 1);
281
+					$content = mb_substr($line, 1, $p - 1);
282
+					return [ $all, $content ];
261
 				}
283
 				}
262
 			}
284
 			}
263
 		}
285
 		}
617
 		 * Flushes accumulated content in `$text` to `$tokens`.
639
 		 * Flushes accumulated content in `$text` to `$tokens`.
618
 		 */
640
 		 */
619
 		$endText = function() use (&$tokens, &$text) {
641
 		$endText = function() use (&$tokens, &$text) {
620
-			if (mb_strlen($text) == 0) return;
642
+			if (mb_strlen($text) === 0) return;
621
 			$textGroups = [];
643
 			$textGroups = [];
622
 			if (mb_eregi('^(\\s+)(.*?)$', $text, $textGroups)) {
644
 			if (mb_eregi('^(\\s+)(.*?)$', $text, $textGroups)) {
623
 				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Whitespace, $textGroups[1]));
645
 				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Whitespace, $textGroups[1]));
626
 			if (mb_eregi('^(.*?)(\\s+)$', $text, $textGroups)) {
648
 			if (mb_eregi('^(.*?)(\\s+)$', $text, $textGroups)) {
627
 				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Text, $textGroups[1]));
649
 				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Text, $textGroups[1]));
628
 				array_push($tokens, new MDToken($textGroups[2], MDTokenType::Whitespace, $textGroups[2]));
650
 				array_push($tokens, new MDToken($textGroups[2], MDTokenType::Whitespace, $textGroups[2]));
629
-			} else {
651
+			} elseif (mb_strlen($text) > 0) {
630
 				array_push($tokens, new MDToken($text, MDTokenType::Text, $text));
652
 				array_push($tokens, new MDToken($text, MDTokenType::Text, $text));
631
 			}
653
 			}
632
 			$text = '';
654
 			$text = '';
1150
 			if ($value === true) {
1172
 			if ($value === true) {
1151
 				$html .= " {$safeName}";
1173
 				$html .= " {$safeName}";
1152
 			} else {
1174
 			} else {
1153
-				$escapedValue = htmlentities("{$value}");
1175
+				$escapedValue = MDUtils::escapeHTML("{$value}");
1154
 				$html .= " {$safeName}=\"{$escapedValue}\"";
1176
 				$html .= " {$safeName}=\"{$escapedValue}\"";
1155
 			}
1177
 			}
1156
 		}
1178
 		}
3087
 	protected function htmlAttributes(): string {
3109
 	protected function htmlAttributes(): string {
3088
 		$html = '';
3110
 		$html = '';
3089
 		if (sizeof($this->cssClasses) > 0) {
3111
 		if (sizeof($this->cssClasses) > 0) {
3090
-			$classlist = htmlentities(implode(' ', $this->cssClasses));
3112
+			$classlist = MDUtils::escapeHTML(implode(' ', $this->cssClasses));
3091
 			$html .= " class=\"{$classlist}\"";
3113
 			$html .= " class=\"{$classlist}\"";
3092
 		}
3114
 		}
3093
 		if ($this->cssId !== null && mb_strlen($this->cssId) > 0) {
3115
 		if ($this->cssId !== null && mb_strlen($this->cssId) > 0) {
3094
-			$html .= " id=\"" . htmlentities($this->cssId) . "\"";
3116
+			$html .= " id=\"" . MDUtils::escapeHTML($this->cssId) . "\"";
3095
 		}
3117
 		}
3096
 		$styles = [];
3118
 		$styles = [];
3097
 		foreach ($this->cssStyles as $key => $value) {
3119
 		foreach ($this->cssStyles as $key => $value) {
3098
 			array_push($styles, "{$key}: {$value};");
3120
 			array_push($styles, "{$key}: {$value};");
3099
 		}
3121
 		}
3100
 		if (sizeof($styles) > 0) {
3122
 		if (sizeof($styles) > 0) {
3101
-			$escaped = htmlentities(implode(' ', $styles));
3123
+			$escaped = MDUtils::escapeHTML(implode(' ', $styles));
3102
 			$html .= " style=\"{$escaped}\"";
3124
 			$html .= " style=\"{$escaped}\"";
3103
 		}
3125
 		}
3104
 		foreach ($this->attributes as $key => $value) {
3126
 		foreach ($this->attributes as $key => $value) {
3105
 			if ($key === 'class' || $key === 'id' || $key === 'style') continue;
3127
 			if ($key === 'class' || $key === 'id' || $key === 'style') continue;
3106
 			$cleanKey = MDUtils::scrubAttributeName($key);
3128
 			$cleanKey = MDUtils::scrubAttributeName($key);
3107
 			if (mb_strlen($cleanKey) == 0) continue;
3129
 			if (mb_strlen($cleanKey) == 0) continue;
3108
-			$cleanValue = htmlentities($value);
3130
+			$cleanValue = MDUtils::escapeHTML($value);
3109
 			$html .= " {$cleanKey}=\"{$cleanValue}\"";
3131
 			$html .= " {$cleanKey}=\"{$cleanValue}\"";
3110
 		}
3132
 		}
3111
 		return $html;
3133
 		return $html;
3343
 	public function toHTML(MDState $state): string {
3365
 	public function toHTML(MDState $state): string {
3344
 		$languageModifier = ($this->language !== null) ? " class=\"language-{$this->language}\"" : '';
3366
 		$languageModifier = ($this->language !== null) ? " class=\"language-{$this->language}\"" : '';
3345
 		return "<pre" . $this->htmlAttributes() . "><code{$languageModifier}>" .
3367
 		return "<pre" . $this->htmlAttributes() . "><code{$languageModifier}>" .
3346
-			htmlentities($this->text) . "</code></pre>\n";
3368
+			MDUtils::escapeHTML($this->text) . "</code></pre>\n";
3347
 	}
3369
 	}
3348
 }
3370
 }
3349
 
3371
 
3555
 	}
3577
 	}
3556
 
3578
 
3557
 	public function toHTML(MDState $state): string {
3579
 	public function toHTML(MDState $state): string {
3558
-		return htmlentities($this->text);
3580
+		return MDUtils::escapeHTML($this->text);
3559
 	}
3581
 	}
3560
 
3582
 
3561
 	public function toPlaintext(MDState $state): string {
3583
 	public function toPlaintext(MDState $state): string {
3653
 	}
3675
 	}
3654
 
3676
 
3655
 	public function toHTML(MDState $state): string {
3677
 	public function toHTML(MDState $state): string {
3656
-		return "<code" . $this->htmlAttributes() . ">" . htmlentities($this->text) . "</code>";
3678
+		return "<code" . $this->htmlAttributes() . ">" . MDUtils::escapeHTML($this->text) . "</code>";
3657
 	}
3679
 	}
3658
 }
3680
 }
3659
 
3681
 
3695
 			return "<sup class=\"footnote\" id=\"{$state->root()->elementIdPrefix}footnoteref_{$this->occurrenceId}\"" .
3717
 			return "<sup class=\"footnote\" id=\"{$state->root()->elementIdPrefix}footnoteref_{$this->occurrenceId}\"" .
3696
 				$this->htmlAttributes() . ">" .
3718
 				$this->htmlAttributes() . ">" .
3697
 				"<a href=\"#{$state->root()->elementIdPrefix}footnote_{$this->footnoteId}\">" .
3719
 				"<a href=\"#{$state->root()->elementIdPrefix}footnote_{$this->footnoteId}\">" .
3698
-				htmlentities($this->displaySymbol ?? $this->symbol) . "</a></sup>";
3720
+				MDUtils::escapeHTML($this->displaySymbol ?? $this->symbol) . "</a></sup>";
3699
 		}
3721
 		}
3700
 		return "<!--FNREF:{{$this->symbol}}-->";
3722
 		return "<!--FNREF:{{$this->symbol}}-->";
3701
 	}
3723
 	}
3722
 		if (str_starts_with($this->href, 'mailto:')) {
3744
 		if (str_starts_with($this->href, 'mailto:')) {
3723
 			$escapedLink = MDUtils::escapeObfuscated($this->href);
3745
 			$escapedLink = MDUtils::escapeObfuscated($this->href);
3724
 		} else {
3746
 		} else {
3725
-			$escapedLink = htmlentities($this->href);
3747
+			$escapedLink = MDUtils::escapeHTML($this->href);
3726
 		}
3748
 		}
3727
 		return "<a href=\"{$escapedLink}\"" . $this->htmlAttributes() . ">" . $this->childHTML($state) . "</a>";
3749
 		return "<a href=\"{$escapedLink}\"" . $this->htmlAttributes() . ">" . $this->childHTML($state) . "</a>";
3728
 	}
3750
 	}
3769
 	}
3791
 	}
3770
 
3792
 
3771
 	public function toHTML(MDState $state): string {
3793
 	public function toHTML(MDState $state): string {
3772
-		$html = "<img src=\"" . htmlentities($this->src) . "\"";
3773
-		if ($this->alt) $html .= " alt=\"" . htmlentities($this->alt) . "\"";
3794
+		$html = "<img src=\"" . MDUtils::escapeHTML($this->src) . "\"";
3795
+		if ($this->alt) $html .= " alt=\"" . MDUtils::escapeHTML($this->alt) . "\"";
3774
 		$html .= $this->htmlAttributes() . ">";
3796
 		$html .= $this->htmlAttributes() . ">";
3775
 		return $html;
3797
 		return $html;
3776
 	}
3798
 	}
3815
 	}
3837
 	}
3816
 
3838
 
3817
 	public function toHTML(MDState $state): string {
3839
 	public function toHTML(MDState $state): string {
3818
-		return "<abbr" . $this->htmlAttributes() . ">" . htmlentities($this->abbreviation) . "</abbr>";
3840
+		return "<abbr" . $this->htmlAttributes() . ">" . MDUtils::escapeHTML($this->abbreviation) . "</abbr>";
3819
 	}
3841
 	}
3820
 }
3842
 }
3821
 
3843
 

+ 250
- 0
phptest/InlineTests.php Ver fichero

1
+<?php
2
+declare(strict_types=1);
3
+
4
+use PHPUnit\Framework\TestCase;
5
+
6
+require_once __DIR__ . '/../php/markdown.php';
7
+
8
+final class InlineTests extends TestCase {
9
+	/** @type {Markdown} */
10
+	private ?Markdown $parser = null;
11
+
12
+	protected function setUp(): void {
13
+		parent::setUp();
14
+		$this->parser = Markdown::completeParser();
15
+	}
16
+
17
+	private function md(string $markdown): string {
18
+		return $this->normalizeWhitespace($this->parser->toHTML($markdown));
19
+	}
20
+
21
+	private function normalizeWhitespace(string $str): string {
22
+		$str = mb_eregi_replace('\\s+', ' ', $str);
23
+		return trim($str);
24
+	}
25
+
26
+	public function test_simpleText() {
27
+		$markdown = 'Lorem ipsum';
28
+		$expected = 'Lorem ipsum';
29
+		$actual = $this->md($markdown);
30
+		$this->assertSame($expected, $actual);
31
+	}
32
+
33
+	public function test_strong() {
34
+		$markdown = 'Lorem **ipsum** dolor **sit**';
35
+		$expected = 'Lorem <strong>ipsum</strong> dolor <strong>sit</strong>';
36
+		$actual = $this->md($markdown);
37
+		$this->assertSame($expected, $actual);
38
+	}
39
+
40
+	public function test_emphasis() {
41
+		$markdown = 'Lorem _ipsum_ dolor _sit_';
42
+		$expected = 'Lorem <em>ipsum</em> dolor <em>sit</em>';
43
+		$actual = $this->md($markdown);
44
+		$this->assertSame($expected, $actual);
45
+	}
46
+
47
+	public function test_strongEmphasis_cleanNesting1() {
48
+		$markdown = 'Lorem **ipsum *dolor* sit** amet';
49
+		$expected = 'Lorem <strong>ipsum <em>dolor</em> sit</strong> amet';
50
+		$actual = $this->md($markdown);
51
+		$this->assertSame($expected, $actual);
52
+	}
53
+
54
+	public function test_strongEmphasis_cleanNesting2() {
55
+		$markdown = 'Lorem *ipsum **dolor** sit* amet';
56
+		$expected = 'Lorem <em>ipsum <strong>dolor</strong> sit</em> amet';
57
+		$actual = $this->md($markdown);
58
+		$this->assertSame($expected, $actual);
59
+	}
60
+
61
+	public function test_strongEmphasis_tightNesting() {
62
+		$markdown = 'Lorem ***ipsum*** dolor';
63
+		$expected = 'Lorem <strong><em>ipsum</em></strong> dolor';
64
+		$actual = $this->md($markdown);
65
+		$this->assertSame($expected, $actual);
66
+	}
67
+
68
+	public function test_strongEmphasis_lopsidedNesting1() {
69
+		$markdown = 'Lorem ***ipsum* dolor** sit';
70
+		$expected = 'Lorem <strong><em>ipsum</em> dolor</strong> sit';
71
+		$actual = $this->md($markdown);
72
+		$this->assertSame($expected, $actual);
73
+	}
74
+
75
+	public function test_strongEmphasis_lopsidedNesting2() {
76
+		$markdown = 'Lorem ***ipsum** dolor* sit';
77
+		$expected = 'Lorem <em><strong>ipsum</strong> dolor</em> sit';
78
+		$actual = $this->md($markdown);
79
+		$this->assertSame($expected, $actual);
80
+	}
81
+
82
+	public function test_strongEmphasis_lopsidedNesting3() {
83
+		$markdown = 'Lorem **ipsum *dolor*** sit';
84
+		$expected = 'Lorem <strong>ipsum <em>dolor</em></strong> sit';
85
+		$actual = $this->md($markdown);
86
+		$this->assertSame($expected, $actual);
87
+	}
88
+
89
+	public function test_strongEmphasis_lopsidedNesting4() {
90
+		$markdown = 'Lorem *ipsum **dolor*** sit';
91
+		$expected = 'Lorem <em>ipsum <strong>dolor</strong></em> sit';
92
+		$actual = $this->md($markdown);
93
+		$this->assertSame($expected, $actual);
94
+	}
95
+
96
+	public function test_inlineCode() {
97
+		$markdown = 'Lorem `ipsum` dolor';
98
+		$expected = 'Lorem <code>ipsum</code> dolor';
99
+		$actual = $this->md($markdown);
100
+		$this->assertSame($expected, $actual);
101
+	}
102
+
103
+	public function test_inlineCode_withInnerBacktick() {
104
+		$markdown = 'Lorem ``ip`su`m`` dolor';
105
+		$expected = 'Lorem <code>ip`su`m</code> dolor';
106
+		$actual = $this->md($markdown);
107
+		$this->assertSame($expected, $actual);
108
+	}
109
+
110
+	public function test_strikethrough_double() {
111
+		$markdown = 'Lorem ~~ipsum~~ dolor';
112
+		$expected = 'Lorem <s>ipsum</s> dolor';
113
+		$actual = $this->md($markdown);
114
+		$this->assertSame($expected, $actual);
115
+	}
116
+
117
+	public function test_subscript() {
118
+		$markdown = 'H~2~O';
119
+		$expected = 'H<sub>2</sub>O';
120
+		$actual = $this->md($markdown);
121
+		$this->assertSame($expected, $actual);
122
+	}
123
+
124
+	public function test_superscript() {
125
+		$markdown = 'E=mc^2^';
126
+		$expected = 'E=mc<sup>2</sup>';
127
+		$actual = $this->md($markdown);
128
+		$this->assertSame($expected, $actual);
129
+	}
130
+
131
+	public function test_highlight() {
132
+		$markdown = 'Lorem ==ipsum== dolor';
133
+		$expected = 'Lorem <mark>ipsum</mark> dolor';
134
+		$actual = $this->md($markdown);
135
+		$this->assertSame($expected, $actual);
136
+	}
137
+
138
+	public function test_underline() {
139
+		$markdown = 'Lorem __ipsum__ dolor';
140
+		$expected = 'Lorem <u>ipsum</u> dolor';
141
+		$actual = $this->md($markdown);
142
+		$this->assertSame($expected, $actual);
143
+	}
144
+
145
+	public function test_link_fullyQualified() {
146
+		$markdown = 'Lorem [ipsum](https://example.com/path/page.html) dolor';
147
+		$expected = 'Lorem <a href="https://example.com/path/page.html">ipsum</a> dolor';
148
+		$actual = $this->md($markdown);
149
+		$this->assertSame($expected, $actual);
150
+	}
151
+
152
+	public function test_link_relative() {
153
+		$markdown = 'Lorem [ipsum](page.html) dolor';
154
+		$expected = 'Lorem <a href="page.html">ipsum</a> dolor';
155
+		$actual = $this->md($markdown);
156
+		$this->assertSame($expected, $actual);
157
+	}
158
+
159
+	public function test_link_title() {
160
+		$markdown = 'Lorem [ipsum](page.html "link title") dolor';
161
+		$expected = 'Lorem <a href="page.html" title="link title">ipsum</a> dolor';
162
+		$actual = $this->md($markdown);
163
+		$this->assertSame($expected, $actual);
164
+	}
165
+
166
+	public function test_link_literal() {
167
+		$markdown = 'Lorem <https://example.com> dolor';
168
+		$expected = 'Lorem <a href="https://example.com">https://example.com</a> dolor';
169
+		$actual = $this->md($markdown);
170
+		$this->assertSame($expected, $actual);
171
+	}
172
+
173
+	public function test_link_ref() {
174
+		$markdown = "Lorem [ipsum][ref] dolor\n\n[ref]: https://example.com";
175
+		$expected = '<p>Lorem <a href="https://example.com">ipsum</a> dolor</p>';
176
+		$actual = $this->md($markdown);
177
+		$this->assertSame($expected, $actual);
178
+	}
179
+
180
+	public function test_link_email() {
181
+		$markdown = 'Lorem [ipsum](user@example.com) dolor';
182
+		$expected = 'Lorem <a href="mailto:&#117;&#115;&#101;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">ipsum</a> dolor';
183
+		$actual = $this->md($markdown);
184
+		$this->assertSame($expected, $actual);
185
+	}
186
+
187
+	public function test_link_email_withTitle() {
188
+		$markdown = 'Lorem [ipsum](user@example.com "title") dolor';
189
+		$expected = 'Lorem <a href="mailto:&#117;&#115;&#101;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;" title="title">ipsum</a> dolor';
190
+		$actual = $this->md($markdown);
191
+		$this->assertSame($expected, $actual);
192
+	}
193
+
194
+	public function test_link_literalEmail() {
195
+		$markdown = 'Lorem <user@example.com> dolor';
196
+		$expected = 'Lorem <a href="mailto:&#117;&#115;&#101;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">&#117;&#115;&#101;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;</a> dolor';
197
+		$actual = $this->md($markdown);
198
+		$this->assertSame($expected, $actual);
199
+	}
200
+
201
+	public function test_link_image() {
202
+		$markdown = 'Lorem [![alt](image.jpg)](page.html) ipsum';
203
+		$expected = 'Lorem <a href="page.html"><img src="image.jpg" alt="alt"></a> ipsum';
204
+		$actual = $this->md($markdown);
205
+		$this->assertSame($expected, $actual);
206
+	}
207
+
208
+	public function test_link_image_complex() {
209
+		$markdown = 'Lorem [![alt] (image.jpg "image title")] (page.html "link title") ipsum';
210
+		$expected = 'Lorem <a href="page.html" title="link title"><img src="image.jpg" alt="alt" title="image title"></a> ipsum';
211
+		$actual = $this->md($markdown);
212
+		$this->assertSame($expected, $actual);
213
+	}
214
+
215
+	public function test_image() {
216
+		$markdown = 'Lorem ![alt text](image.jpg) dolor';
217
+		$expected = 'Lorem <img src="image.jpg" alt="alt text"> dolor';
218
+		$actual = $this->md($markdown);
219
+		$this->assertSame($expected, $actual);
220
+	}
221
+
222
+	public function test_image_noAlt() {
223
+		$markdown = 'Lorem ![](image.jpg) dolor';
224
+		$expected = 'Lorem <img src="image.jpg"> dolor';
225
+		$actual = $this->md($markdown);
226
+		$this->assertSame($expected, $actual);
227
+	}
228
+
229
+	public function test_image_withTitle() {
230
+		$markdown = 'Lorem ![alt text](image.jpg "image title") dolor';
231
+		$expected = 'Lorem <img src="image.jpg" alt="alt text" title="image title"> dolor';
232
+		$actual = $this->md($markdown);
233
+		$this->assertSame($expected, $actual);
234
+	}
235
+
236
+	public function test_image_ref() {
237
+		$markdown = "Lorem ![alt text][ref] dolor\n\n" .
238
+			'[ref]: image.jpg "image title"';
239
+		$expected = '<p>Lorem <img src="image.jpg" alt="alt text" title="image title"> dolor</p>';
240
+		$actual = $this->md($markdown);
241
+		$this->assertSame($expected, $actual);
242
+	}
243
+
244
+	public function test_htmlTags() {
245
+		$markdown = 'Lorem <strong title=\'with " quote\' id="with \' apostrophe" lang=unquoted translate forbidden="true">ipsum</strong> dolor';
246
+		$expected = 'Lorem <strong title="with &quot; quote" id="with \' apostrophe" lang="unquoted" translate>ipsum</strong> dolor';
247
+		$actual = $this->md($markdown);
248
+		$this->assertSame($expected, $actual);
249
+	}
250
+}

+ 139
- 0
phptest/TokenTests.php Ver fichero

1
+<?php
2
+declare(strict_types=1);
3
+
4
+use PHPUnit\Framework\TestCase;
5
+
6
+require_once __DIR__ . '/../php/markdown.php';
7
+
8
+final class TokenTests extends TestCase {
9
+	public function test_tokenizeLabel() {
10
+		// Escapes are preserved
11
+		$this->assertSame([ '[foo]', 'foo' ], MDToken::tokenizeLabel('[foo] bar'));
12
+		$this->assertSame([ '[foo\\[]', 'foo\\[' ], MDToken::tokenizeLabel('[foo\\[] bar'));
13
+		$this->assertSame([ '[foo\\]]', 'foo\\]' ], MDToken::tokenizeLabel('[foo\\]] bar'));
14
+		$this->assertSame([ '[foo[]]', 'foo[]' ], MDToken::tokenizeLabel('[foo[]] bar'));
15
+		$this->assertSame([ '[foo\\(]', 'foo\\(' ], MDToken::tokenizeLabel('[foo\\(] bar'));
16
+		$this->assertSame([ '[foo\\)]', 'foo\\)' ], MDToken::tokenizeLabel('[foo\\)] bar'));
17
+		$this->assertSame([ '[foo()]', 'foo()' ], MDToken::tokenizeLabel('[foo()] bar'));
18
+
19
+		$this->assertSame(null, MDToken::tokenizeLabel('foo bar'));
20
+		$this->assertSame(null, MDToken::tokenizeLabel('[foo\\] bar'));
21
+		$this->assertSame(null, MDToken::tokenizeLabel('[foo bar'));
22
+		$this->assertSame(null, MDToken::tokenizeLabel('[foo[] bar'));
23
+	}
24
+
25
+	public function test_tokenizeURL() {
26
+		$this->assertSame([ '(page.html)', 'page.html', null ], MDToken::tokenizeURL('(page.html) foo'));
27
+		$this->assertSame([ '(page.html "link title")', 'page.html', 'link title' ], MDToken::tokenizeURL('(page.html "link title") foo'));
28
+		$this->assertSame([ '(https://example.com/path/page.html?query=foo&bar=baz#fragment)', 'https://example.com/path/page.html?query=foo&bar=baz#fragment', null ], MDToken::tokenizeURL('(https://example.com/path/page.html?query=foo&bar=baz#fragment) foo'));
29
+
30
+		$this->assertSame(null, MDToken::tokenizeURL('page.html foo'));
31
+		$this->assertSame(null, MDToken::tokenizeURL('(page.html foo'));
32
+		$this->assertSame(null, MDToken::tokenizeURL('page.html) foo'));
33
+		$this->assertSame(null, MDToken::tokenizeURL('(page.html "title) foo'));
34
+		$this->assertSame(null, MDToken::tokenizeURL('(page .html) foo'));
35
+		$this->assertSame(null, MDToken::tokenizeURL('(user@example.com) foo'));
36
+		$this->assertSame(null, MDToken::tokenizeURL('(user@example.com "title") foo'));
37
+	}
38
+
39
+	public function test_tokenizeEmail() {
40
+		$this->assertSame([ '(user@example.com)', 'user@example.com', null ], MDToken::tokenizeEmail('(user@example.com)'));
41
+		$this->assertSame([ '(user@example.com "link title")', 'user@example.com', 'link title' ], MDToken::tokenizeEmail('(user@example.com "link title")'));
42
+
43
+		$this->assertSame(null, MDToken::tokenizeEmail('(https://example.com) foo'));
44
+		$this->assertSame(null, MDToken::tokenizeEmail('(https://example.com "link title") foo'));
45
+		$this->assertSame(null, MDToken::tokenizeEmail('(user@example.com "link title) foo'));
46
+		$this->assertSame(null, MDToken::tokenizeEmail('(user@example.com foo'));
47
+		$this->assertSame(null, MDToken::tokenizeEmail('user@example.com) foo'));
48
+	}
49
+
50
+	public function test_findFirstTokens() {
51
+		$tokens = [
52
+			new MDToken('Lorem', MDTokenType::Text),
53
+			new MDToken(' ', MDTokenType::Whitespace),
54
+			new MDToken('_', MDTokenType::Underscore),
55
+			new MDToken('ipsum', MDTokenType::Text),
56
+			new MDToken('_', MDTokenType::Underscore),
57
+			new MDToken(' ', MDTokenType::Whitespace),
58
+			new MDToken('dolor', MDTokenType::Text),
59
+			new MDToken('_', MDTokenType::Underscore),
60
+			new MDToken('sit', MDTokenType::Text),
61
+			new MDToken('_', MDTokenType::Underscore),
62
+		];
63
+		$pattern = [
64
+			MDTokenType::Underscore,
65
+		];
66
+		$result = MDToken::findFirstTokens($tokens, $pattern);
67
+		$expected = new MDTokenMatch([ $tokens[2] ], 2);
68
+		$this->assertEquals($expected, $result);
69
+	}
70
+
71
+	public function test_findFirstTokens_optionalWhitespace1() {
72
+		$tokens = [
73
+			new MDToken('Lorem', MDTokenType::Text),
74
+			new MDToken(' ', MDTokenType::Whitespace),
75
+			new MDToken('[ipsum]', MDTokenType::Label, 'ipsum'),
76
+			new MDToken('(link.html)', MDTokenType::URL, 'link.html'),
77
+			new MDToken(' ', MDTokenType::Whitespace),
78
+			new MDToken('dolor', MDTokenType::Text),
79
+		];
80
+		$pattern = [
81
+			MDTokenType::Label,
82
+			MDTokenType::META_OptionalWhitespace,
83
+			MDTokenType::URL,
84
+		];
85
+		$result = MDToken::findFirstTokens($tokens, $pattern);
86
+		$expected = new MDTokenMatch([ $tokens[2], $tokens[3] ], 2);
87
+		$this->assertEquals($expected, $result);
88
+	}
89
+
90
+	public function test_findFirstTokens_optionalWhitespace2() {
91
+		$tokens = [
92
+			new MDToken('Lorem', MDTokenType::Text),
93
+			new MDToken(' ', MDTokenType::Whitespace),
94
+			new MDToken('[ipsum]', MDTokenType::Label, 'ipsum'),
95
+			new MDToken(' ', MDTokenType::Whitespace),
96
+			new MDToken('(link.html)', MDTokenType::URL, 'link.html'),
97
+			new MDToken(' ', MDTokenType::Whitespace),
98
+			new MDToken('dolor', MDTokenType::Text),
99
+		];
100
+		$pattern = [
101
+			MDTokenType::Label,
102
+			MDTokenType::META_OptionalWhitespace,
103
+			MDTokenType::URL,
104
+		];
105
+		$result = MDToken::findFirstTokens($tokens, $pattern);
106
+		$expected = new MDTokenmatch([ $tokens[2], $tokens[3], $tokens[4] ], 2);
107
+		$this->assertEquals($expected, $result);
108
+	}
109
+
110
+	public function test_findPairedTokens() {
111
+		$tokens = [
112
+			new MDToken('Lorem', MDTokenType::Text),
113
+			new MDToken(' ', MDTokenType::Whitespace),
114
+			new MDToken('_', MDTokenType::Underscore),
115
+			new MDToken('ipsum', MDTokenType::Text),
116
+			new MDToken('_', MDTokenType::Underscore),
117
+			new MDToken(' ', MDTokenType::Whitespace),
118
+			new MDToken('dolor', MDTokenType::Text),
119
+			new MDToken('_', MDTokenType::Underscore),
120
+			new MDToken('sit', MDTokenType::Text),
121
+			new MDToken('_', MDTokenType::Underscore),
122
+		];
123
+		$pattern = [
124
+			MDTokenType::Underscore,
125
+		];
126
+		$result = MDToken::findPairedTokens($tokens, $pattern, $pattern);
127
+		$expected = new MDPairedTokenMatch(
128
+			[ $tokens[2] ], // startTokens
129
+			[ $tokens[3] ], // contentTokens
130
+			[ $tokens[4] ], // endTokens
131
+			2, // startIndex
132
+			3, // contentIndex
133
+			4, // endIndex
134
+			3 // totalLength
135
+		);
136
+		$this->assertEquals($expected, $result);
137
+	}
138
+}
139
+?>

+ 0
- 41
phptest/UtilsTests.php Ver fichero

47
 		$this->assertSame(1, MDUtils::countIndents("\tfoo", true));
47
 		$this->assertSame(1, MDUtils::countIndents("\tfoo", true));
48
 		$this->assertSame(2, MDUtils::countIndents("\t\tfoo", true));
48
 		$this->assertSame(2, MDUtils::countIndents("\t\tfoo", true));
49
 	}
49
 	}
50
-
51
-	public function test_tokenizeLabel() {
52
-		// Escapes are preserved
53
-		$this->assertSame([ '[foo]', 'foo' ], MDToken::tokenizeLabel('[foo] bar'));
54
-		$this->assertSame([ '[foo\\[]', 'foo\\[' ], MDToken::tokenizeLabel('[foo\\[] bar'));
55
-		$this->assertSame([ '[foo\\]]', 'foo\\]' ], MDToken::tokenizeLabel('[foo\\]] bar'));
56
-		$this->assertSame([ '[foo[]]', 'foo[]' ], MDToken::tokenizeLabel('[foo[]] bar'));
57
-		$this->assertSame([ '[foo\\(]', 'foo\\(' ], MDToken::tokenizeLabel('[foo\\(] bar'));
58
-		$this->assertSame([ '[foo\\)]', 'foo\\)' ], MDToken::tokenizeLabel('[foo\\)] bar'));
59
-		$this->assertSame([ '[foo()]', 'foo()' ], MDToken::tokenizeLabel('[foo()] bar'));
60
-
61
-		$this->assertSame(null, MDToken::tokenizeLabel('foo bar'));
62
-		$this->assertSame(null, MDToken::tokenizeLabel('[foo\\] bar'));
63
-		$this->assertSame(null, MDToken::tokenizeLabel('[foo bar'));
64
-		$this->assertSame(null, MDToken::tokenizeLabel('[foo[] bar'));
65
-	}
66
-
67
-	public function test_tokenizeURL() {
68
-		$this->assertSame([ '(page.html)', 'page.html', null ], MDToken::tokenizeURL('(page.html) foo'));
69
-		$this->assertSame([ '(page.html "link title")', 'page.html', 'link title' ], MDToken::tokenizeURL('(page.html "link title") foo'));
70
-		$this->assertSame([ '(https://example.com/path/page.html?query=foo&bar=baz#fragment)', 'https://example.com/path/page.html?query=foo&bar=baz#fragment', null ], MDToken::tokenizeURL('(https://example.com/path/page.html?query=foo&bar=baz#fragment) foo'));
71
-
72
-		$this->assertSame(null, MDToken::tokenizeURL('page.html foo'));
73
-		$this->assertSame(null, MDToken::tokenizeURL('(page.html foo'));
74
-		$this->assertSame(null, MDToken::tokenizeURL('page.html) foo'));
75
-		$this->assertSame(null, MDToken::tokenizeURL('(page.html "title) foo'));
76
-		$this->assertSame(null, MDToken::tokenizeURL('(page .html) foo'));
77
-		$this->assertSame(null, MDToken::tokenizeURL('(user@example.com) foo'));
78
-		$this->assertSame(null, MDToken::tokenizeURL('(user@example.com "title") foo'));
79
-	}
80
-
81
-	public function test_tokenizeEmail() {
82
-		$this->assertSame([ '(user@example.com)', 'user@example.com', null ], MDToken::tokenizeEmail('(user@example.com)'));
83
-		$this->assertSame([ '(user@example.com "link title")', 'user@example.com', 'link title' ], MDToken::tokenizeEmail('(user@example.com "link title")'));
84
-
85
-		$this->assertSame(null, MDToken::tokenizeEmail('(https://example.com) foo'));
86
-		$this->assertSame(null, MDToken::tokenizeEmail('(https://example.com "link title") foo'));
87
-		$this->assertSame(null, MDToken::tokenizeEmail('(user@example.com "link title) foo'));
88
-		$this->assertSame(null, MDToken::tokenizeEmail('(user@example.com foo'));
89
-		$this->assertSame(null, MDToken::tokenizeEmail('user@example.com) foo'));
90
-	}
91
 }
50
 }
92
 ?>
51
 ?>

+ 5
- 1
runphptests.sh Ver fichero

1
 #!/bin/sh
1
 #!/bin/sh
2
-php lib/phpunit.phar phptest/UtilsTests.php
2
+php lib/phpunit.phar \
3
+	phptest/UtilsTests.php \
4
+	phptest/TokenTests.php \
5
+	phptest/InlineTests.php \
6
+

Loading…
Cancelar
Guardar