From 2608c670ababfd04a4ce85393db7ebe4a0c14188 Mon Sep 17 00:00:00 2001 From: webchick Date: Wed, 29 Jun 2011 22:51:13 -0700 Subject: Issue #1130198 by pillarsdotnet, Damien Tournoud: Fixed Regression: line-breaks are mangled by drupal_html_to_text(). --- modules/simpletest/tests/mail.test | 353 +++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) (limited to 'modules/simpletest/tests') diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test index 8a7b152d9..a6c7b40e5 100644 --- a/modules/simpletest/tests/mail.test +++ b/modules/simpletest/tests/mail.test @@ -1,6 +1,7 @@ 'HTML to text conversion', + 'description' => 'Tests drupal_html_to_text().', + 'group' => 'Mail', + ); + } + + /** + * Converts a string to its PHP source equivalent for display in test messages. + * + * @param $text + * The text string to convert. + * + * @return + * An HTML representation of the text string that, when displayed in a + * browser, represents the PHP source code equivalent of $text. + */ + function stringToHtml($text) { + return '"' . + str_replace( + array("\n", ' '), + array('\n', ' '), + check_plain($text) + ) . '"'; + } + + /** + * Helper function for testing drupal_html_to_text(). + * + * @param $html + * The source HTML string to be converted. + * @param $text + * The expected result of converting $html to text. + * @param $message + * A text message to display in the assertion message. + * @param $allowed_tags + * (optional) An array of allowed tags, or NULL to default to the full + * set of tags supported by drupal_html_to_text(). + */ + function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) { + preg_match_all('/<([a-z0-6]+)/', drupal_strtolower($html), $matches); + $tested_tags = implode(', ', array_unique($matches[1])); + $message .= ' (' . $tested_tags . ')'; + $result = drupal_html_to_text($html, $allowed_tags); + $pass = $this->assertEqual($result, $text, check_plain($message)); + $verbose = 'html =
' . $this->stringToHtml($html)
+      . '

' . 'result =
' . $this->stringToHtml($result)
+      . '

' . 'expected =
' . $this->stringToHtml($text)
+      . '
'; + $this->verbose($verbose); + if (!$pass) { + $this->pass("Previous test verbose info:
$verbose"); + } + } + + /** + * Test all supported tags of drupal_html_to_text(). + */ + function testTags() { + global $base_path, $base_url; + $tests = array( + // @todo Trailing linefeeds should be trimmed. + 'Drupal.org' => "Drupal.org [1]\n\n[1] http://drupal.org\n", + // @todo Footer urls should be absolute. + "Homepage" => "Homepage [1]\n\n[1] $base_url/\n", + '
Drupal
' => "Drupal\n", + // @todo The
tag is currently not supported. + '
Drupal
Drupal
' => "DrupalDrupal\n", + 'Drupal' => "*Drupal*\n", + // @todo There should be a space between the '>' and the text. + '
Drupal
' => ">Drupal\n", + '
Drupal
Drupal
' => ">Drupal\n>Drupal\n", + '
Drupal
Drupal

Drupal' => "Drupal\nDrupal\nDrupal\n", + '
Drupal
Drupal

Drupal' => "Drupal\nDrupal\nDrupal\n", + // @todo There should be two line breaks before the paragraph. + '
Drupal
Drupal

Drupal

Drupal

' => "Drupal\nDrupal\nDrupal\nDrupal\n\n", + '
Drupal
' => "Drupal\n", + // @todo The
tag is currently not supported. + '
Drupal
Drupal
' => "DrupalDrupal\n", + 'Drupal' => "/Drupal/\n", + '

Drupal

' => "======== DRUPAL ==============================================================\n\n", + '

Drupal

Drupal

' => "======== DRUPAL ==============================================================\n\nDrupal\n\n", + '

Drupal

' => "-------- DRUPAL --------------------------------------------------------------\n\n", + '

Drupal

Drupal

' => "-------- DRUPAL --------------------------------------------------------------\n\nDrupal\n\n", + '

Drupal

' => ".... Drupal\n\n", + '

Drupal

Drupal

' => ".... Drupal\n\nDrupal\n\n", + '

Drupal

' => ".. Drupal\n\n", + '

Drupal

Drupal

' => ".. Drupal\n\nDrupal\n\n", + '
Drupal
' => "Drupal\n\n", + '
Drupal

Drupal

' => "Drupal\n\nDrupal\n\n", + '
Drupal
' => "Drupal\n\n", + '
Drupal

Drupal

' => "Drupal\n\nDrupal\n\n", + '
Drupal
' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", + '
Drupal
' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", + '
Drupal

Drupal

' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n\n", + 'Drupal' => "/Drupal/\n", + '

Drupal

' => "Drupal\n\n", + '

Drupal

Drupal

' => "Drupal\n\nDrupal\n\n", + 'Drupal' => "*Drupal*\n", + // @todo Tables are currently not supported. + '
DrupalDrupal
DrupalDrupal
' => "DrupalDrupalDrupalDrupal\n", + '
Drupal

Drupal

' => "Drupal\nDrupal\n\n", + // @todo The tag is currently not supported. + 'Drupal' => "Drupal\n", + '
  • Drupal
' => " * Drupal\n\n", + '
  • Drupal Drupal Drupal
' => " * Drupal /Drupal/ Drupal\n\n", + // @todo Lines containing nothing but spaces should be trimmed. + '
  • Drupal
    1. Drupal
    2. Drupal
' => " * Drupal\n * 1) Drupal\n 2) Drupal\n \n\n", + '
  • Drupal
    1. Drupal
  • Drupal
' => " * Drupal\n * 1) Drupal\n \n * Drupal\n\n", + '
  • Drupal
  • Drupal
' => " * Drupal\n * Drupal\n\n", + '
  • Drupal

Drupal

' => " * Drupal\n\nDrupal\n\n", + '
  1. Drupal
' => " 1) Drupal\n\n", + '
  1. Drupal
    • Drupal
    • Drupal
' => " 1) Drupal\n 2) * Drupal\n * Drupal\n \n\n", + '
  1. Drupal
  2. Drupal
' => " 1) Drupal\n 2) Drupal\n\n", + '
    Drupal
' => "Drupal\n\n", + '
  1. Drupal

Drupal

' => " 1) Drupal\n\nDrupal\n\n", + '
Drupal
' => "Drupal\n\n", + '
Drupal
Drupal
' => "Drupal\n Drupal\n\n", + '
Drupal
Drupal
Drupal
Drupal
' => "Drupal\n Drupal\nDrupal\n Drupal\n\n", + '
Drupal
Drupal

Drupal

' => "Drupal\n Drupal\n\nDrupal\n\n", + '
Drupal
Drupal
' => "Drupal\n Drupal\n\n", + '
Drupal

Drupal

' => "Drupal\n\nDrupal\n\n", + // @todo Again, lines containing only spaces should be trimmed. + '
  • Drupal
  • Drupal
    Drupal
    Drupal
    Drupal
  • Drupal
' => " * Drupal\n * Drupal\n Drupal\n Drupal\n Drupal\n \n * Drupal\n\n", + // Tests malformed HTML tags. + '
Drupal
Drupal' => "Drupal\nDrupal\n", + '
Drupal
Drupal' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n", + '
  1. Drupal
  2. Drupal
' => " 1) Drupal\n 2) Drupal\n\n", + '
  • Drupal Drupal Drupal
' => " * Drupal /Drupal/ Drupal\n\n", + '
  • Drupal
  • Drupal' => " * Drupal\n * Drupal\n\n", + '
    • Drupal
    • Drupal
    ' => " * Drupal\n * Drupal\n\n", + '
      Drupal
    ' => "Drupal\n\n", + 'Drupal
  • Drupal' => "Drupal\n * Drupal\n", + '
    Drupal
    ' => "Drupal\n\n", + '
    Drupal

    Drupal

    ' => "Drupal\n\nDrupal\n\n", + '
    Drupal
    ' => "Drupal\n", + // Tests some unsupported HTML tags. + 'Drupal' => "Drupal\n", + // @todo Perhaps the contents of ' => "Drupal\n", + ); + + foreach ($tests as $html => $text) { + $this->assertHtmlToText($html, $text, 'Supported tags'); + } + } + + /** + * Test $allowed_tags argument of drupal_html_to_text(). + */ + function testDrupalHtmlToTextArgs() { + // The second parameter of drupal_html_to_text() overrules the allowed tags. + $this->assertHtmlToText( + 'Drupal Drupal Drupal', + "Drupal *Drupal* Drupal\n", + 'Allowed tag found', + array('b') + ); + $this->assertHtmlToText( + 'Drupal

    Drupal

    Drupal', + "Drupal Drupal Drupal\n", + 'Disallowed

    tag not found', + array('b') + ); + + $this->assertHtmlToText( + 'Drupal

    Drupal

    Drupal', + "Drupal Drupal Drupal\n", + 'Disallowed

    , , and tags not found', + array('a', 'br', 'h1') + ); + + $this->assertHtmlToText( + 'Drupal', + "Drupal\n", + 'Unsupported and tags not found', + array('html', 'body') + ); + } + + /** + * Test that whitespace is collapsed. + */ + function testDrupalHtmltoTextCollapsesWhitespace() { + $input = "

    Drupal Drupal\n\nDrupal

    Drupal  Drupal\n\nDrupal
    Drupal Drupal\n\nDrupal

    "; + // @todo The whitespace should be collapsed. + $collapsed = "Drupal Drupal\n\nDrupalDrupal Drupal\n\nDrupalDrupal Drupal\n\nDrupal\n\n"; + $this->assertHtmlToText( + $input, + $collapsed, + 'Whitespace is collapsed', + array('p') + ); + } + + /** + * Test that text separated by block-level tags in HTML get separated by + * (at least) a newline in the plaintext version. + */ + function testDrupalHtmlToTextBlockTagToNewline() { + $input = '[text]' + . '
    [blockquote]
    ' + . '
    [br]' + . '
    [dl-dt]
    ' + . '
    [dt]
    ' + . '
    [dd]
    ' + . '
    [dd-dl]
    ' + . '

    [h1]

    ' + . '

    [h2]

    ' + . '

    [h3]

    ' + . '

    [h4]

    ' + . '
    [h5]
    ' + . '
    [h6]
    ' + . '
    [hr]' + . '
    1. [ol-li]
    2. ' + . '
    3. [li]
    4. ' + . '
    5. [li-ol]
    ' + . '

    [p]

    ' + . '
    • [ul-li]
    • ' + . '
    • [li-ul]
    ' + . '[text]'; + $output = drupal_html_to_text($input); + $pass = $this->assertFalse( + preg_match('/\][^\n]*\[/s', $output), + 'Block-level HTML tags should force newlines' + ); + if (!$pass) { + $this->verbose($this->stringToHtml($output)); + } + $output_upper = drupal_strtoupper($output); + $upper_input = drupal_strtoupper($input); + $upper_output = drupal_html_to_text($upper_input); + $pass = $this->assertEqual( + $upper_output, + $output_upper, + 'Tag recognition should be case-insensitive' + ); + if (!$pass) { + $this->verbose( + $upper_output + . '
    should be equal to
    ' + . $output_upper + ); + } + } + + /** + * Test that headers are properly separated from surrounding text. + */ + function testHeaderSeparation() { + $html = 'Drupal

    Drupal

    Drupal'; + // @todo There should be more space above the header than below it. + $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n"; + $this->assertHtmlToText($html, $text, + 'Text before and after

    tag'); + $html = '

    Drupal

    Drupal

    Drupal'; + // @todo There should be more space above the header than below it. + $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n"; + $this->assertHtmlToText($html, $text, + 'Paragraph before and text after

    tag'); + $html = 'Drupal

    Drupal

    Drupal

    '; + // @todo There should be more space above the header than below it. + $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; + $this->assertHtmlToText($html, $text, + 'Text before and paragraph after

    tag'); + $html = '

    Drupal

    Drupal

    Drupal

    '; + $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; + $this->assertHtmlToText($html, $text, + 'Paragraph before and after

    tag'); + } + + /** + * Test that footnote references are properly generated. + */ + function testFootnoteReferences() { + global $base_path, $base_url; + $source = 'Host and path' + . '
    Host, no path' + . '
    Path, no host' + . '
    Relative path'; + // @todo Footnote urls should be absolute. + $tt = "Host and path [1]" + . "\nHost, no path [2]" + // @todo The following two references should be combined. + . "\nPath, no host [3]" + . "\nRelative path [4]" + . "\n" + . "\n[1] http://www.example.com/node/1" + . "\n[2] http://www.example.com" + // @todo The following two references should be combined. + . "\n[3] $base_url/node/1" + . "\n[4] node/1\n"; + $this->assertHtmlToText($source, $tt, 'Footnotes'); + } + + /** + * Test that combinations of paragraph breaks, line breaks, linefeeds, + * and spaces are properly handled. + */ + function testDrupalHtmlToTextParagraphs() { + $tests = array(); + $tests[] = array( + 'html' => "

    line 1
    \nline 2
    line 3\n
    line 4

    paragraph

    ", + // @todo Trailing line breaks should be trimmed. + 'text' => "line 1\nline 2\nline 3\nline 4\n\nparagraph\n\n", + ); + $tests[] = array( + 'html' => "

    line 1
    line 2

    line 4
    line 5

    0

    ", + // @todo Trailing line breaks should be trimmed. + 'text' => "line 1\nline 2\n\nline 4\nline 5\n\n0\n\n", + ); + foreach ($tests as $test) { + $this->assertHtmlToText($test['html'], $test['text'], 'Paragraph breaks'); + } + } + + /** + * Tests that drupal_html_to_text() wraps before 1000 characters. + * + * RFC 3676 says, "The Text/Plain media type is the lowest common + * denominator of Internet email, with lines of no more than 998 characters." + * + * RFC 2046 says, "SMTP [RFC-821] allows a maximum of 998 octets before the + * next CRLF sequence." + * + * RFC 821 says, "The maximum total length of a text line including the + * is 1000 characters." + */ + function testVeryLongLineWrap() { + $input = 'Drupal

    ' . str_repeat('x', 2100) . '
    Drupal'; + $output = drupal_html_to_text($input); + // This awkward construct comes from includes/mail.inc lines 8-13. + $eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS); + // We must use strlen() rather than drupal_strlen() in order to count + // octets rather than characters. + $line_length_limit = 1000 - drupal_strlen($eol); + $maximum_line_length = 0; + foreach (explode($eol, $output) as $line) { + // We must use strlen() rather than drupal_strlen() in order to count + // octets rather than characters. + $maximum_line_length = max($maximum_line_length, strlen($line . $eol)); + } + $verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.'; + // @todo This should assert that $maximum_line_length <= 1000. + $this->pass($verbose); + } +} -- cgit v1.2.3