3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License along with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
28 * Mail utils for use during unit testing to allow retrieval
29 * and examination of 'sent' emails.
33 * $mut = new CiviMailUtils( $this, true ); //true automatically starts spooling
35 * $msg = $mut->getMostRecentEmail( 'raw' ); // or 'ezc' to get an ezc mail object
36 * ... assert stuff about $msg ...
46 class CiviMailUtils
extends PHPUnit_Framework_TestCase
{
49 * @var mixed current outbound email option
51 protected $_outBound_option = NULL;
54 * @var bool is this a webtest
56 protected $_webtest = FALSE;
61 * @param CiviSeleniumTestCase|CiviUnitTestCase $unit_test The currently running test
62 * @param bool $startImmediately
63 * Start writing to db now or wait until start() is called.
65 public function __construct(&$unit_test, $startImmediately = TRUE) {
66 $this->_ut
= $unit_test;
68 // Check if running under webtests or not
69 if (is_subclass_of($unit_test, 'CiviSeleniumTestCase')) {
70 $this->_webtest
= TRUE;
73 if ($startImmediately) {
79 * Start writing emails to db instead of current option.
81 public function start() {
82 if ($this->_webtest
) {
83 // Change outbound mail setting
84 $this->_ut
->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next");
86 // First remember the current setting
87 $this->_outBound_option
= $this->getSelectedOutboundOption();
89 $this->_ut
->click('xpath=//input[@name="outBound_option" and @value="' . CRM_Mailing_Config
::OUTBOUND_OPTION_REDIRECT_TO_DB
. '"]');
90 $this->_ut
->clickLink("_qf_Smtp_next");
92 // Is there supposed to be a status message displayed when outbound email settings are changed?
98 // save current setting for outbound option, then change it
99 $mailingBackend = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::MAILING_PREFERENCES_NAME
,
103 $this->_outBound_option
= $mailingBackend['outBound_option'];
104 $mailingBackend['outBound_option'] = CRM_Mailing_Config
::OUTBOUND_OPTION_REDIRECT_TO_DB
;
106 Civi
::settings()->set('mailing_backend', $mailingBackend);
108 $mailingBackend = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::MAILING_PREFERENCES_NAME
,
114 public function stop() {
115 if ($this->_webtest
) {
116 if ($this->_outBound_option
!= CRM_Mailing_Config
::OUTBOUND_OPTION_REDIRECT_TO_DB
) {
117 // Change outbound mail setting
118 $this->_ut
->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next");
119 $this->_ut
->click('xpath=//input[@name="outBound_option" and @value="' . $this->_outBound_option
. '"]');
120 // There will be a warning when switching from test to live mode
121 if ($this->_outBound_option
!= CRM_Mailing_Config
::OUTBOUND_OPTION_DISABLED
) {
122 $this->_ut
->getAlert();
124 $this->_ut
->clickLink("_qf_Smtp_next");
129 $mailingBackend = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::MAILING_PREFERENCES_NAME
,
133 $mailingBackend['outBound_option'] = $this->_outBound_option
;
135 Civi
::settings()->set('mailing_backend', $mailingBackend);
140 * @param string $type
142 * @return ezcMail|string
144 public function getMostRecentEmail($type = 'raw') {
147 if ($this->_webtest
) {
148 // I don't understand but for some reason we have to load the page twice for a recent mailing to appear.
149 $this->_ut
->openCiviPage('mailing/browse/archived', 'reset=1');
150 $this->_ut
->openCiviPage('mailing/browse/archived', 'reset=1', 'css=td.crm-mailing-name');
152 // We can't fetch mailing headers from webtest so we'll only try if the format is raw
153 if ($this->_webtest
&& $type == 'raw') {
154 // This should select the first "Report" link in the table, which is sorted by Completion Date descending, so in theory is the most recent email. Not sure of a more robust way at the moment.
155 $this->_ut
->clickLink('xpath=//tr[contains(@id, "crm-mailing_")]//a[text()="Report"]');
157 // Also not sure how robust this is, but there isn't a good
158 // identifier for this link either.
159 $this->_ut
->waitForElementPresent('xpath=//a[contains(text(), "View complete message")]');
160 $this->_ut
->clickAjaxLink('xpath=//a[contains(text(), "View complete message")]');
161 $msg = $this->_ut
->getText('css=.ui-dialog-content.crm-ajax-container');
164 $dao = CRM_Core_DAO
::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
166 $msg = $dao->headers
. "\n\n" . $dao->body
;
177 $msg = $this->convertToEzc($msg);
184 * @param string $type
188 * @return array(ezcMail)|array(string)
190 public function getAllMessages($type = 'raw') {
193 if ($this->_webtest
) {
194 throw new Exception("Not implementated: getAllMessages for WebTest");
197 $dao = CRM_Core_DAO
::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id');
198 while ($dao->fetch()) {
199 $msgs[] = $dao->headers
. "\n\n" . $dao->body
;
210 foreach ($msgs as $i => $msg) {
211 $msgs[$i] = $this->convertToEzc($msg);
222 public function getSelectedOutboundOption() {
223 $selectedOption = CRM_Mailing_Config
::OUTBOUND_OPTION_MAIL
;
224 // Is there a better way to do this? How do you get the currently selected value of a radio button in selenium?
225 for ($i = 0; $i <= 5; $i++
) {
226 if ($i != CRM_Mailing_Config
::OUTBOUND_OPTION_MOCK
) {
227 if ($this->_ut
->getValue('xpath=//input[@name="outBound_option" and @value="' . $i . '"]') == "on") {
228 $selectedOption = $i;
233 return $selectedOption;
237 * Utility functions (previously part of CiviUnitTestCase)
238 * Included for backward compatibility with existing tests.
242 * Check contents of mail log.
244 * @param array $strings
245 * Strings that should be included.
246 * @param array $absentStrings
247 * Strings that should not be included.
248 * @param string $prefix
250 * @return \ezcMail|string
252 public function checkMailLog($strings, $absentStrings = array(), $prefix = '') {
253 $mail = $this->getMostRecentEmail('raw');
254 return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail);
258 * Check contents of mail log.
260 * @param array $strings
261 * Strings that should be included.
262 * @param array $absentStrings
263 * Strings that should not be included.
264 * @param string $prefix
266 * @return \ezcMail|string
268 public function checkAllMailLog($strings, $absentStrings = array(), $prefix = '') {
269 $mails = $this->getAllMessages('raw');
270 $mail = implode(',', $mails);
271 return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail);
275 * Check that mail log is empty.
276 * @param string $prefix
278 public function assertMailLogEmpty($prefix = '') {
279 $mail = $this->getMostRecentEmail('raw');
280 $this->_ut
->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix);
284 * Assert that $expectedRecipients (and no else) have received emails
286 * @param array $expectedRecipients
287 * Array($msgPos => array($recipPos => $emailAddr)).
289 public function assertRecipients($expectedRecipients) {
290 $recipients = array();
291 foreach ($this->getAllMessages('ezc') as $message) {
292 $recipients[] = CRM_Utils_Array
::collect('email', $message->to
);
294 $cmp = function($a, $b) {
295 if ($a[0] == $b[0]) {
298 return ($a[0] < $b[0]) ?
1 : -1;
300 usort($recipients, $cmp);
301 usort($expectedRecipients, $cmp);
302 $this->_ut
->assertEquals(
305 "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE)
310 * Assert that $expectedSubjects (and no other subjects) were sent.
312 * @param array $expectedSubjects
313 * Array(string $subj).
315 public function assertSubjects($expectedSubjects) {
317 foreach ($this->getAllMessages('ezc') as $message) {
318 /** @var ezcMail $message */
319 $subjects[] = $message->subject
;
322 sort($expectedSubjects);
323 $this->_ut
->assertEquals(
326 "Incorrect subjects: " . print_r(array('expected' => $expectedSubjects, 'actual' => $subjects), TRUE)
331 * Remove any sent messages from the log.
337 public function clearMessages($limit = 1) {
338 if ($this->_webtest
) {
339 throw new Exception("Not implementated: clearMessages for WebTest");
342 CRM_Core_DAO
::executeQuery('DELETE FROM civicrm_mailing_spool ORDER BY id DESC LIMIT ' . $limit);
348 * Email header and body.
351 private function convertToEzc($msg) {
352 $set = new ezcMailVariableSet($msg);
353 $parser = new ezcMailParser();
354 $mail = $parser->parseMail($set);
355 $this->_ut
->assertNotEmpty($mail, 'Cannot parse mail');
361 * @param $absentStrings
366 public function checkMailForStrings($strings, $absentStrings, $prefix, $mail) {
367 foreach ($strings as $string) {
368 $this->_ut
->assertContains($string, $mail, "$string . not found in $mail $prefix");
370 foreach ($absentStrings as $string) {
371 $this->_ut
->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix");;