| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Test the Drupal mailing system. 6 */ 7 class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { 8 /** 9 * The most recent message that was sent through the test case. 10 * 11 * We take advantage here of the fact that static variables are shared among 12 * all instance of the same class. 13 */ 14 private static $sent_message; 15 16 public static function getInfo() { 17 return array( 18 'name' => 'Mail system', 19 'description' => 'Performs tests on the pluggable mailing framework.', 20 'group' => 'System', 21 ); 22 } 23 24 function setUp() { 25 parent::setUp(array('simpletest')); 26 27 // Set MailTestCase (i.e. this class) as the SMTP library 28 variable_set('mail_system', array('default-system' => 'MailTestCase')); 29 } 30 31 /** 32 * Assert that the pluggable mail system is functional. 33 */ 34 function testPluggableFramework() { 35 global $language; 36 37 // Use MailTestCase for sending a message. 38 $message = drupal_mail('simpletest', 'mail_test', 'testing@example.com', $language); 39 40 // Assert whether the message was sent through the send function. 41 $this->assertEqual(self::$sent_message['to'], 'testing@example.com', t('Pluggable mail system is extendable.')); 42 } 43 44 /** 45 * Test that message sending may be canceled. 46 * 47 * @see simpletest_mail_alter() 48 */ 49 function testCancelMessage() { 50 global $language; 51 52 // Reset the class variable holding a copy of the last sent message. 53 self::$sent_message = NULL; 54 55 // Send a test message that simpletest_mail_alter should cancel. 56 $message = drupal_mail('simpletest', 'cancel_test', 'cancel@example.com', $language); 57 58 // Assert that the message was not actually sent. 59 $this->assertNull(self::$sent_message, 'Message was canceled.'); 60 } 61 62 /** 63 * Concatenate and wrap the e-mail body for plain-text mails. 64 * 65 * @see DefaultMailSystem 66 */ 67 public function format(array $message) { 68 // Join the body array into one string. 69 $message['body'] = implode("\n\n", $message['body']); 70 // Convert any HTML to plain-text. 71 $message['body'] = drupal_html_to_text($message['body']); 72 // Wrap the mail body for sending. 73 $message['body'] = drupal_wrap_mail($message['body']); 74 return $message; 75 } 76 77 /** 78 * Send function that is called through the mail system. 79 */ 80 public function mail(array $message) { 81 self::$sent_message = $message; 82 } 83 } 84 85 /** 86 * Unit tests for drupal_html_to_text(). 87 */ 88 class DrupalHtmlToTextTestCase extends DrupalWebTestCase { 89 public static function getInfo() { 90 return array( 91 'name' => 'HTML to text conversion', 92 'description' => 'Tests drupal_html_to_text().', 93 'group' => 'Mail', 94 ); 95 } 96 97 /** 98 * Converts a string to its PHP source equivalent for display in test messages. 99 * 100 * @param $text 101 * The text string to convert. 102 * 103 * @return 104 * An HTML representation of the text string that, when displayed in a 105 * browser, represents the PHP source code equivalent of $text. 106 */ 107 function stringToHtml($text) { 108 return '"' . 109 str_replace( 110 array("\n", ' '), 111 array('\n', ' '), 112 check_plain($text) 113 ) . '"'; 114 } 115 116 /** 117 * Helper function for testing drupal_html_to_text(). 118 * 119 * @param $html 120 * The source HTML string to be converted. 121 * @param $text 122 * The expected result of converting $html to text. 123 * @param $message 124 * A text message to display in the assertion message. 125 * @param $allowed_tags 126 * (optional) An array of allowed tags, or NULL to default to the full 127 * set of tags supported by drupal_html_to_text(). 128 */ 129 function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) { 130 preg_match_all('/<([a-z0-6]+)/', drupal_strtolower($html), $matches); 131 $tested_tags = implode(', ', array_unique($matches[1])); 132 $message .= ' (' . $tested_tags . ')'; 133 $result = drupal_html_to_text($html, $allowed_tags); 134 $pass = $this->assertEqual($result, $text, check_plain($message)); 135 $verbose = 'html = <pre>' . $this->stringToHtml($html) 136 . '</pre><br />' . 'result = <pre>' . $this->stringToHtml($result) 137 . '</pre><br />' . 'expected = <pre>' . $this->stringToHtml($text) 138 . '</pre>'; 139 $this->verbose($verbose); 140 if (!$pass) { 141 $this->pass("Previous test verbose info:<br />$verbose"); 142 } 143 } 144 145 /** 146 * Test all supported tags of drupal_html_to_text(). 147 */ 148 function testTags() { 149 global $base_path, $base_url; 150 $tests = array( 151 // @todo Trailing linefeeds should be trimmed. 152 '<a href = "http://drupal.org">Drupal.org</a>' => "Drupal.org [1]\n\n[1] http://drupal.org\n", 153 // @todo Footer URLs should be absolute. 154 "<a href = \"$base_path\">Homepage</a>" => "Homepage [1]\n\n[1] $base_url/\n", 155 '<address>Drupal</address>' => "Drupal\n", 156 // @todo The <address> tag is currently not supported. 157 '<address>Drupal</address><address>Drupal</address>' => "DrupalDrupal\n", 158 '<b>Drupal</b>' => "*Drupal*\n", 159 // @todo There should be a space between the '>' and the text. 160 '<blockquote>Drupal</blockquote>' => ">Drupal\n", 161 '<blockquote>Drupal</blockquote><blockquote>Drupal</blockquote>' => ">Drupal\n>Drupal\n", 162 '<br />Drupal<br />Drupal<br /><br />Drupal' => "Drupal\nDrupal\nDrupal\n", 163 '<br/>Drupal<br/>Drupal<br/><br/>Drupal' => "Drupal\nDrupal\nDrupal\n", 164 // @todo There should be two line breaks before the paragraph. 165 '<br/>Drupal<br/>Drupal<br/><br/>Drupal<p>Drupal</p>' => "Drupal\nDrupal\nDrupal\nDrupal\n\n", 166 '<div>Drupal</div>' => "Drupal\n", 167 // @todo The <div> tag is currently not supported. 168 '<div>Drupal</div><div>Drupal</div>' => "DrupalDrupal\n", 169 '<em>Drupal</em>' => "/Drupal/\n", 170 '<h1>Drupal</h1>' => "======== DRUPAL ==============================================================\n\n", 171 '<h1>Drupal</h1><p>Drupal</p>' => "======== DRUPAL ==============================================================\n\nDrupal\n\n", 172 '<h2>Drupal</h2>' => "-------- DRUPAL --------------------------------------------------------------\n\n", 173 '<h2>Drupal</h2><p>Drupal</p>' => "-------- DRUPAL --------------------------------------------------------------\n\nDrupal\n\n", 174 '<h3>Drupal</h3>' => ".... Drupal\n\n", 175 '<h3>Drupal</h3><p>Drupal</p>' => ".... Drupal\n\nDrupal\n\n", 176 '<h4>Drupal</h4>' => ".. Drupal\n\n", 177 '<h4>Drupal</h4><p>Drupal</p>' => ".. Drupal\n\nDrupal\n\n", 178 '<h5>Drupal</h5>' => "Drupal\n\n", 179 '<h5>Drupal</h5><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", 180 '<h6>Drupal</h6>' => "Drupal\n\n", 181 '<h6>Drupal</h6><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", 182 '<hr />Drupal<hr />' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", 183 '<hr/>Drupal<hr/>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", 184 '<hr/>Drupal<hr/><p>Drupal</p>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n\n", 185 '<i>Drupal</i>' => "/Drupal/\n", 186 '<p>Drupal</p>' => "Drupal\n\n", 187 '<p>Drupal</p><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", 188 '<strong>Drupal</strong>' => "*Drupal*\n", 189 // @todo Tables are currently not supported. 190 '<table><tr><td>Drupal</td><td>Drupal</td></tr><tr><td>Drupal</td><td>Drupal</td></tr></table>' => "DrupalDrupalDrupalDrupal\n", 191 '<table><tr><td>Drupal</td></tr></table><p>Drupal</p>' => "Drupal\nDrupal\n\n", 192 // @todo The <u> tag is currently not supported. 193 '<u>Drupal</u>' => "Drupal\n", 194 '<ul><li>Drupal</li></ul>' => " * Drupal\n\n", 195 '<ul><li>Drupal <em>Drupal</em> Drupal</li></ul>' => " * Drupal /Drupal/ Drupal\n\n", 196 // @todo Lines containing nothing but spaces should be trimmed. 197 '<ul><li>Drupal</li><li><ol><li>Drupal</li><li>Drupal</li></ol></li></ul>' => " * Drupal\n * 1) Drupal\n 2) Drupal\n \n\n", 198 '<ul><li>Drupal</li><li><ol><li>Drupal</li></ol></li><li>Drupal</li></ul>' => " * Drupal\n * 1) Drupal\n \n * Drupal\n\n", 199 '<ul><li>Drupal</li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n\n", 200 '<ul><li>Drupal</li></ul><p>Drupal</p>' => " * Drupal\n\nDrupal\n\n", 201 '<ol><li>Drupal</li></ol>' => " 1) Drupal\n\n", 202 '<ol><li>Drupal</li><li><ul><li>Drupal</li><li>Drupal</li></ul></li></ol>' => " 1) Drupal\n 2) * Drupal\n * Drupal\n \n\n", 203 '<ol><li>Drupal</li><li>Drupal</li></ol>' => " 1) Drupal\n 2) Drupal\n\n", 204 '<ol>Drupal</ol>' => "Drupal\n\n", 205 '<ol><li>Drupal</li></ol><p>Drupal</p>' => " 1) Drupal\n\nDrupal\n\n", 206 '<dl><dt>Drupal</dt></dl>' => "Drupal\n\n", 207 '<dl><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\n\n", 208 '<dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\nDrupal\n Drupal\n\n", 209 '<dl><dt>Drupal</dt><dd>Drupal</dd></dl><p>Drupal</p>' => "Drupal\n Drupal\n\nDrupal\n\n", 210 '<dl><dt>Drupal<dd>Drupal</dl>' => "Drupal\n Drupal\n\n", 211 '<dl><dt>Drupal</dt></dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", 212 // @todo Again, lines containing only spaces should be trimmed. 213 '<ul><li>Drupal</li><li><dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl></li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n Drupal\n Drupal\n Drupal\n \n * Drupal\n\n", 214 // Tests malformed HTML tags. 215 '<br>Drupal<br>Drupal' => "Drupal\nDrupal\n", 216 '<hr>Drupal<hr>Drupal' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n", 217 '<ol><li>Drupal<li>Drupal</ol>' => " 1) Drupal\n 2) Drupal\n\n", 218 '<ul><li>Drupal <em>Drupal</em> Drupal</ul></ul>' => " * Drupal /Drupal/ Drupal\n\n", 219 '<ul><li>Drupal<li>Drupal</ol>' => " * Drupal\n * Drupal\n\n", 220 '<ul><li>Drupal<li>Drupal</ul>' => " * Drupal\n * Drupal\n\n", 221 '<ul>Drupal</ul>' => "Drupal\n\n", 222 'Drupal</ul></ol></dl><li>Drupal' => "Drupal\n * Drupal\n", 223 '<dl>Drupal</dl>' => "Drupal\n\n", 224 '<dl>Drupal</dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", 225 '<dt>Drupal</dt>' => "Drupal\n", 226 // Tests some unsupported HTML tags. 227 '<html>Drupal</html>' => "Drupal\n", 228 // @todo Perhaps the contents of <script> tags should be dropped. 229 '<script type="text/javascript">Drupal</script>' => "Drupal\n", 230 ); 231 232 foreach ($tests as $html => $text) { 233 $this->assertHtmlToText($html, $text, 'Supported tags'); 234 } 235 } 236 237 /** 238 * Test $allowed_tags argument of drupal_html_to_text(). 239 */ 240 function testDrupalHtmlToTextArgs() { 241 // The second parameter of drupal_html_to_text() overrules the allowed tags. 242 $this->assertHtmlToText( 243 'Drupal <b>Drupal</b> Drupal', 244 "Drupal *Drupal* Drupal\n", 245 'Allowed <b> tag found', 246 array('b') 247 ); 248 $this->assertHtmlToText( 249 'Drupal <h1>Drupal</h1> Drupal', 250 "Drupal Drupal Drupal\n", 251 'Disallowed <h1> tag not found', 252 array('b') 253 ); 254 255 $this->assertHtmlToText( 256 'Drupal <p><em><b>Drupal</b></em><p> Drupal', 257 "Drupal Drupal Drupal\n", 258 'Disallowed <p>, <em>, and <b> tags not found', 259 array('a', 'br', 'h1') 260 ); 261 262 $this->assertHtmlToText( 263 '<html><body>Drupal</body></html>', 264 "Drupal\n", 265 'Unsupported <html> and <body> tags not found', 266 array('html', 'body') 267 ); 268 } 269 270 /** 271 * Test that whitespace is collapsed. 272 */ 273 function testDrupalHtmltoTextCollapsesWhitespace() { 274 $input = "<p>Drupal Drupal\n\nDrupal<pre>Drupal Drupal\n\nDrupal</pre>Drupal Drupal\n\nDrupal</p>"; 275 // @todo The whitespace should be collapsed. 276 $collapsed = "Drupal Drupal\n\nDrupalDrupal Drupal\n\nDrupalDrupal Drupal\n\nDrupal\n\n"; 277 $this->assertHtmlToText( 278 $input, 279 $collapsed, 280 'Whitespace is collapsed', 281 array('p') 282 ); 283 } 284 285 /** 286 * Test that text separated by block-level tags in HTML get separated by 287 * (at least) a newline in the plaintext version. 288 */ 289 function testDrupalHtmlToTextBlockTagToNewline() { 290 $input = '[text]' 291 . '<blockquote>[blockquote]</blockquote>' 292 . '<br />[br]' 293 . '<dl><dt>[dl-dt]</dt>' 294 . '<dt>[dt]</dt>' 295 . '<dd>[dd]</dd>' 296 . '<dd>[dd-dl]</dd></dl>' 297 . '<h1>[h1]</h1>' 298 . '<h2>[h2]</h2>' 299 . '<h3>[h3]</h3>' 300 . '<h4>[h4]</h4>' 301 . '<h5>[h5]</h5>' 302 . '<h6>[h6]</h6>' 303 . '<hr />[hr]' 304 . '<ol><li>[ol-li]</li>' 305 . '<li>[li]</li>' 306 . '<li>[li-ol]</li></ol>' 307 . '<p>[p]</p>' 308 . '<ul><li>[ul-li]</li>' 309 . '<li>[li-ul]</li></ul>' 310 . '[text]'; 311 $output = drupal_html_to_text($input); 312 $pass = $this->assertFalse( 313 preg_match('/\][^\n]*\[/s', $output), 314 'Block-level HTML tags should force newlines' 315 ); 316 if (!$pass) { 317 $this->verbose($this->stringToHtml($output)); 318 } 319 $output_upper = drupal_strtoupper($output); 320 $upper_input = drupal_strtoupper($input); 321 $upper_output = drupal_html_to_text($upper_input); 322 $pass = $this->assertEqual( 323 $upper_output, 324 $output_upper, 325 'Tag recognition should be case-insensitive' 326 ); 327 if (!$pass) { 328 $this->verbose( 329 $upper_output 330 . '<br />should be equal to <br />' 331 . $output_upper 332 ); 333 } 334 } 335 336 /** 337 * Test that headers are properly separated from surrounding text. 338 */ 339 function testHeaderSeparation() { 340 $html = 'Drupal<h1>Drupal</h1>Drupal'; 341 // @todo There should be more space above the header than below it. 342 $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n"; 343 $this->assertHtmlToText($html, $text, 344 'Text before and after <h1> tag'); 345 $html = '<p>Drupal</p><h1>Drupal</h1>Drupal'; 346 // @todo There should be more space above the header than below it. 347 $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n"; 348 $this->assertHtmlToText($html, $text, 349 'Paragraph before and text after <h1> tag'); 350 $html = 'Drupal<h1>Drupal</h1><p>Drupal</p>'; 351 // @todo There should be more space above the header than below it. 352 $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; 353 $this->assertHtmlToText($html, $text, 354 'Text before and paragraph after <h1> tag'); 355 $html = '<p>Drupal</p><h1>Drupal</h1><p>Drupal</p>'; 356 $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; 357 $this->assertHtmlToText($html, $text, 358 'Paragraph before and after <h1> tag'); 359 } 360 361 /** 362 * Test that footnote references are properly generated. 363 */ 364 function testFootnoteReferences() { 365 global $base_path, $base_url; 366 $source = '<a href="http://www.example.com/node/1">Host and path</a>' 367 . '<br /><a href="http://www.example.com">Host, no path</a>' 368 . '<br /><a href="' . $base_path . 'node/1">Path, no host</a>' 369 . '<br /><a href="node/1">Relative path</a>'; 370 // @todo Footnote URLs should be absolute. 371 $tt = "Host and path [1]" 372 . "\nHost, no path [2]" 373 // @todo The following two references should be combined. 374 . "\nPath, no host [3]" 375 . "\nRelative path [4]" 376 . "\n" 377 . "\n[1] http://www.example.com/node/1" 378 . "\n[2] http://www.example.com" 379 // @todo The following two references should be combined. 380 . "\n[3] $base_url/node/1" 381 . "\n[4] node/1\n"; 382 $this->assertHtmlToText($source, $tt, 'Footnotes'); 383 } 384 385 /** 386 * Test that combinations of paragraph breaks, line breaks, linefeeds, 387 * and spaces are properly handled. 388 */ 389 function testDrupalHtmlToTextParagraphs() { 390 $tests = array(); 391 $tests[] = array( 392 'html' => "<p>line 1<br />\nline 2<br />line 3\n<br />line 4</p><p>paragraph</p>", 393 // @todo Trailing line breaks should be trimmed. 394 'text' => "line 1\nline 2\nline 3\nline 4\n\nparagraph\n\n", 395 ); 396 $tests[] = array( 397 'html' => "<p>line 1<br /> line 2</p> <p>line 4<br /> line 5</p> <p>0</p>", 398 // @todo Trailing line breaks should be trimmed. 399 'text' => "line 1\nline 2\n\nline 4\nline 5\n\n0\n\n", 400 ); 401 foreach ($tests as $test) { 402 $this->assertHtmlToText($test['html'], $test['text'], 'Paragraph breaks'); 403 } 404 } 405 406 /** 407 * Tests that drupal_html_to_text() wraps before 1000 characters. 408 * 409 * RFC 3676 says, "The Text/Plain media type is the lowest common 410 * denominator of Internet email, with lines of no more than 998 characters." 411 * 412 * RFC 2046 says, "SMTP [RFC-821] allows a maximum of 998 octets before the 413 * next CRLF sequence." 414 * 415 * RFC 821 says, "The maximum total length of a text line including the 416 * <CRLF> is 1000 characters." 417 */ 418 function testVeryLongLineWrap() { 419 $input = 'Drupal<br /><p>' . str_repeat('x', 2100) . '</><br />Drupal'; 420 $output = drupal_html_to_text($input); 421 // This awkward construct comes from includes/mail.inc lines 8-13. 422 $eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS); 423 // We must use strlen() rather than drupal_strlen() in order to count 424 // octets rather than characters. 425 $line_length_limit = 1000 - drupal_strlen($eol); 426 $maximum_line_length = 0; 427 foreach (explode($eol, $output) as $line) { 428 // We must use strlen() rather than drupal_strlen() in order to count 429 // octets rather than characters. 430 $maximum_line_length = max($maximum_line_length, strlen($line . $eol)); 431 } 432 $verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.'; 433 // @todo This should assert that $maximum_line_length <= 1000. 434 $this->pass($verbose); 435 } 436 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title