From b4a332a985f52c6bd6114583ede17c36a6facf04 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 16 Dec 2016 15:58:09 -0800 Subject: [PATCH] CRM-19690 - CRM_Mailing_MailingSystemTest - Improve CiviMail test coverage --- .../CRM/Mailing/BaseMailingSystemTest.php | 407 ++++++++++++++++++ .../phpunit/CRM/Mailing/MailingSystemTest.php | 114 +++++ .../phpunit/api/v3/JobProcessMailingTest.php | 1 + 3 files changed, 522 insertions(+) create mode 100644 tests/phpunit/CRM/Mailing/BaseMailingSystemTest.php create mode 100644 tests/phpunit/CRM/Mailing/MailingSystemTest.php diff --git a/tests/phpunit/CRM/Mailing/BaseMailingSystemTest.php b/tests/phpunit/CRM/Mailing/BaseMailingSystemTest.php new file mode 100644 index 0000000000..efc4343325 --- /dev/null +++ b/tests/phpunit/CRM/Mailing/BaseMailingSystemTest.php @@ -0,0 +1,407 @@ +useTransaction(); + parent::setUp(); + CRM_Mailing_BAO_MailingJob::$mailsProcessed = 0; // DGW + + $this->_groupID = $this->groupCreate(); + $this->createContactsInGroup(2, $this->_groupID); + + $this->defaultParams = array( + 'name' => 'mailing name', + 'created_id' => 1, + 'groups' => array('include' => array($this->_groupID)), + 'scheduled_date' => 'now', + ); + $this->_mut = new CiviMailUtils($this, TRUE); + $this->callAPISuccess('mail_settings', 'get', + array('api.mail_settings.create' => array('domain' => 'chaos.org'))); + } + + /** + */ + public function tearDown() { + $this->_mut->stop(); + CRM_Utils_Hook::singleton()->reset(); + CRM_Mailing_BAO_MailingJob::$mailsProcessed = 0; // DGW + parent::tearDown(); + } + + /** + * Generate a fully-formatted mailing with standard email headers. + */ + public function testBasicHeaders() { + $allMessages = $this->runMailingSuccess(array( + 'subject' => 'Accidents in cars cause children for {contact.display_name}!', + 'body_text' => 'BEWARE children need regular infusions of toys. Santa knows your {domain.address}. There is no {action.optOutUrl}.', + )); + foreach ($allMessages as $k => $message) { + /** @var ezcMail $message */ + + $offset = $k + 1; + + $this->assertEquals("FIXME", $message->from->name); + $this->assertEquals("info@EXAMPLE.ORG", $message->from->email); + $this->assertEquals("Mr. Foo{$offset} Anderson II", $message->to[0]->name); + $this->assertEquals("mail{$offset}@nul.example.com", $message->to[0]->email); + + $this->assertRegExp('#^text/plain; charset=utf-8#', $message->headers['Content-Type']); + $this->assertRegExp(';^b\.[\d\.a-f]+@chaos.org$;', $message->headers['Return-Path']); + $this->assertRegExp(';^b\.[\d\.a-f]+@chaos.org$;', $message->headers['X-CiviMail-Bounce'][0]); + $this->assertRegExp(';^\$;', $message->headers['List-Unsubscribe'][0]); + $this->assertEquals('bulk', $message->headers['Precedence'][0]); + } + } + + /** + * Generate a fully-formatted mailing (with body_text content). + */ + public function testText() { + $allMessages = $this->runMailingSuccess(array( + 'subject' => 'Accidents in cars cause children for {contact.display_name}!', + 'body_text' => 'BEWARE children need regular infusions of toys. Santa knows your {domain.address}. There is no {action.optOutUrl}.', + 'open_tracking' => 1, + // Note: open_tracking does nothing with text, but we'll just verify that it does nothing + )); + foreach ($allMessages as $k => $message) { + /** @var ezcMail $message */ + /** @var ezcMailText $textPart */ + + $this->assertTrue($message->body instanceof ezcMailText); + + $this->assertEquals('plain', $message->body->subType); + $this->assertRegExp( + ";" . + "Sample Header for TEXT formatted content.\n" . // Default header + "BEWARE children need regular infusions of toys. Santa knows your .*\\. There is no http.*civicrm/mailing/optout.*\\.\n" . + "to unsubscribe: http.*civicrm/mailing/optout" . // Default footer + ";", + $message->body->text + ); + } + } + + /** + * Generate a fully-formatted mailing (with body_html content). + */ + public function testHtmlWithOpenTracking() { + $allMessages = $this->runMailingSuccess(array( + 'subject' => 'Example Subject', + 'body_html' => '

You can go to Google or opt out.

', + 'open_tracking' => 1, + 'url_tracking' => 0, + )); + foreach ($allMessages as $k => $message) { + /** @var ezcMail $message */ + /** @var ezcMailText $htmlPart */ + /** @var ezcMailText $textPart */ + + $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); + + list($textPart, $htmlPart) = $message->body->getParts(); + + $this->assertEquals('html', $htmlPart->subType); + $this->assertRegExp( + ";" . + "Sample Header for HTML formatted content.\n" . // Default header + // FIXME: CiviMail puts double " after hyperlink! + "

You can go to Google or opt out.

\n" . // body_html + "Sample Footer for HTML formatted content" . // Default footer + ".*\n" . + "text + ); + + $this->assertEquals('plain', $textPart->subType); + $this->assertRegExp( + ";" . + "Sample Header for TEXT formatted content.\n" . // Default header + "You can go to Google \\[1\\] or opt out \\[2\\]\\.\n" . // body_html, filtered + "\n" . + "Links:\n" . + "------\n" . + "\\[1\\] http://example.net/first\\?cs=[0-9a-f_]+\n" . + "\\[2\\] http.*civicrm/mailing/optout.*\n" . + "\n" . + "to unsubscribe: http.*civicrm/mailing/optout" . // Default footer + ";", + $textPart->text + ); + } + } + + /** + * Generate a fully-formatted mailing (with body_html content). + */ + public function testHtmlWithOpenAndUrlTracking() { + $allMessages = $this->runMailingSuccess(array( + 'subject' => 'Example Subject', + 'body_html' => '

You can go to Google or opt out.

', + 'open_tracking' => 1, + 'url_tracking' => 1, + )); + foreach ($allMessages as $k => $message) { + /** @var ezcMail $message */ + /** @var ezcMailText $htmlPart */ + /** @var ezcMailText $textPart */ + + $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); + + list($textPart, $htmlPart) = $message->body->getParts(); + + $this->assertEquals('html', $htmlPart->subType); + $this->assertRegExp( + ";" . + // body_html + "

You can go to Google" . + " or opt out.

\n" . + // Default footer + "Sample Footer for HTML formatted content" . + ".*\n" . + // Open-tracking code + "text + ); + + $this->assertEquals('plain', $textPart->subType); + $this->assertRegExp( + ";" . + // body_html, filtered + "You can go to Google \\[1\\] or opt out \\[2\\]\\.\n" . + "\n" . + "Links:\n" . + "------\n" . + "\\[1\\] .*extern/url\.php\?u=\d+&qid=\d+\n" . + "\\[2\\] http.*civicrm/mailing/optout.*\n" . + "\n" . + // Default footer + "to unsubscribe: http.*civicrm/mailing/optout" . + ";", + $textPart->text + ); + } + } + + public function urlTrackingExamples() { + $cases = array(); + + // Each case comes in four parts: + // 1. Mailing HTML (body_html) + // 2. Regex to run against final HTML + // 3. Regex to run against final text + // 4. Additional mailing options + + // Tracking disabled + + $cases[] = array( + '

Foo

', + ';

Foo

;', + ';\\[1\\] http://example\.net/;', + array('url_tracking' => 0), + ); + $cases[] = array( + '

Foo

', + // FIXME: Legacy tracker adds extra quote after URL + ';

Foo

;', + ';\\[1\\] http://example\.net/\?id=\d+;', + array('url_tracking' => 0), + ); + $cases[] = array( + '

Foo

', + ';

Foo

;', + ';\\[1\\] http.*civicrm/mailing/optout.*;', + array('url_tracking' => 0), + ); + $cases[] = array( + '

Look at .

', + ';

Look at \.

;', + ';Look at \.;', + array('url_tracking' => 0), + ); + $cases[] = array( + // Plain-text URL's are tracked in plain-text emails... + // but not in HTML emails. + "

Please go to: http://example.net/

", + ";

Please go to: http://example\.net/

;", + ';Please go to: http://example\.net/;', + array('url_tracking' => 0), + ); + + // Tracking enabled + + $cases[] = array( + '

Foo

', + ';

Foo

;', + ';\\[1\\] .*extern/url\.php\?u=\d+.*;', + array('url_tracking' => 1), + ); + $cases[] = array( + // FIXME: CiviMail URL tracking doesn't track tokenized links. + '

Foo

', + // FIXME: Legacy tracker adds extra quote after URL + ';

Foo

;', + ';\\[1\\] http://example\.net/\?id=\d+;', + array('url_tracking' => 1), + ); + $cases[] = array( + // It would be redundant/slow to track the action URLs? + '

Foo

', + ';

Foo

;', + ';\\[1\\] http.*civicrm/mailing/optout.*;', + array('url_tracking' => 1), + ); + $cases[] = array( + // It would be excessive/slow to track every embedded image. + '

Look at .

', + ';

Look at \.

;', + ';Look at \.;', + array('url_tracking' => 1), + ); + $cases[] = array( + // Plain-text URL's are tracked in plain-text emails... + // but not in HTML emails. + "

Please go to: http://example.net/

", + ";

Please go to: http://example\.net/

;", + ';Please go to: .*extern/url.php\?u=\d+&qid=\d+;', + array('url_tracking' => 1), + ); + + return $cases; + } + + /** + * Generate a fully-formatted mailing (with body_html content). + * + * @dataProvider urlTrackingExamples + */ + public function testUrlTracking($inputHtml, $htmlUrlRegex, $textUrlRegex, $params) { + $caseName = print_r(array('inputHtml' => $inputHtml, 'params' => $params), 1); + + $allMessages = $this->runMailingSuccess($params + array( + 'subject' => 'Example Subject', + 'body_html' => $inputHtml, + )); + foreach ($allMessages as $k => $message) { + /** @var ezcMail $message */ + /** @var ezcMailText $htmlPart */ + /** @var ezcMailText $textPart */ + + $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); + + list($textPart, $htmlPart) = $message->body->getParts(); + + if ($htmlUrlRegex) { + $this->assertEquals('html', $htmlPart->subType, "Should have HTML part in case: $caseName"); + $this->assertRegExp($htmlUrlRegex, $htmlPart->text, "Should have correct HTML in case: $caseName"); + } + + if ($textUrlRegex) { + $this->assertEquals('plain', $textPart->subType, "Should have text part in case: $caseName"); + $this->assertRegExp($textUrlRegex, $textPart->text, "Should have correct text in case: $caseName"); + } + } + } + + /** + * Create contacts in group. + * + * @param int $count + * @param int $groupID + * @param string $domain + */ + protected function createContactsInGroup( + $count, + $groupID, + $domain = 'nul.example.com' + ) { + for ($i = 1; $i <= $count; $i++) { + $contactID = $this->individualCreate(array( + 'first_name' => "Foo{$i}", + 'email' => 'mail' . $i . '@' . $domain, + )); + $this->callAPISuccess('group_contact', 'create', array( + 'contact_id' => $contactID, + 'group_id' => $groupID, + 'status' => 'Added', + )); + } + } + + /** + * Create and execute a mailing. Return the matching messages. + * + * @param array $params + * List of parameters to send to Mailing.create API. + * @return array + */ + protected function runMailingSuccess($params) { + $mailingParams = array_merge($this->defaultParams, $params); + $this->callAPISuccess('mailing', 'create', $mailingParams); + $this->_mut->assertRecipients(array()); + $this->callAPISuccess('job', 'process_mailing', array()); + + $allMessages = $this->_mut->getAllMessages('ezc'); + // There are exactly two contacts produced by setUp(). + $this->assertEquals(2, count($allMessages)); + + return $allMessages; + } + +} diff --git a/tests/phpunit/CRM/Mailing/MailingSystemTest.php b/tests/phpunit/CRM/Mailing/MailingSystemTest.php new file mode 100644 index 0000000000..e3dcb9e61f --- /dev/null +++ b/tests/phpunit/CRM/Mailing/MailingSystemTest.php @@ -0,0 +1,114 @@ +add(array('experimentalFlexMailerEngine' => FALSE)); + + $hooks = \CRM_Utils_Hook::singleton(); + $hooks->setHook('civicrm_alterMailParams', + array($this, 'hook_alterMailParams')); + } + + /** + * @see CRM_Utils_Hook::alterMailParams + */ + public function hook_alterMailParams(&$params, $context = NULL) { + $this->counts['hook_alterMailParams'] = 1; + $this->assertEquals('civimail', $context); + } + + public function tearDown() { + parent::tearDown(); + $this->assertNotEmpty($this->counts['hook_alterMailParams']); + } + + // ---- Boilerplate ---- + + // The remainder of this class contains dummy stubs which make it easier to + // work with the tests in an IDE. + + /** + * Generate a fully-formatted mailing (with body_html content). + * + * @dataProvider urlTrackingExamples + */ + public function testUrlTracking( + $inputHtml, + $htmlUrlRegex, + $textUrlRegex, + $params + ) { + parent::testUrlTracking($inputHtml, $htmlUrlRegex, $textUrlRegex, $params); + } + + public function testBasicHeaders() { + parent::testBasicHeaders(); + } + + public function testText() { + parent::testText(); + } + + public function testHtmlWithOpenTracking() { + parent::testHtmlWithOpenTracking(); + } + + public function testHtmlWithOpenAndUrlTracking() { + parent::testHtmlWithOpenAndUrlTracking(); + } + +} diff --git a/tests/phpunit/api/v3/JobProcessMailingTest.php b/tests/phpunit/api/v3/JobProcessMailingTest.php index 769b4635a3..649941db70 100644 --- a/tests/phpunit/api/v3/JobProcessMailingTest.php +++ b/tests/phpunit/api/v3/JobProcessMailingTest.php @@ -39,6 +39,7 @@ /** * Class api_v3_JobTest * @group headless + * @group civimail */ class api_v3_JobProcessMailingTest extends CiviUnitTestCase { protected $_apiversion = 3; -- 2.25.1