Merge pull request #13880 from eileenmcnaughton/temp_repeat
[civicrm-core.git] / tests / phpunit / CiviTest / CiviMailUtils.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
2fe49090 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
d25dd0ee 25 */
6a488035
TO
26
27/**
28 * Mail utils for use during unit testing to allow retrieval
29 * and examination of 'sent' emails.
30 *
31 * Basic usage:
32 *
33 * $mut = new CiviMailUtils( $this, true ); //true automatically starts spooling
34 * ... do stuff ...
35 * $msg = $mut->getMostRecentEmail( 'raw' ); // or 'ezc' to get an ezc mail object
36 * ... assert stuff about $msg ...
37 * $mut->stop();
38 *
39 *
40 * @package CiviCRM
41 */
42
4cbe18b8
EM
43/**
44 * Class CiviMailUtils
45 */
6a488035
TO
46class CiviMailUtils extends PHPUnit_Framework_TestCase {
47
48 /**
49 * @var mixed current outbound email option
50 */
51 protected $_outBound_option = NULL;
52
53 /**
54 * @var bool is this a webtest
55 */
56 protected $_webtest = FALSE;
57
58 /**
eceb18cc 59 * Constructor.
6a488035 60 *
858d0bf8 61 * @param CiviSeleniumTestCase|CiviUnitTestCase $unit_test The currently running test
e16033b4
TO
62 * @param bool $startImmediately
63 * Start writing to db now or wait until start() is called.
6a488035 64 */
00be9182 65 public function __construct(&$unit_test, $startImmediately = TRUE) {
70520a0b
CW
66 $this->_ut = $unit_test;
67
6a488035
TO
68 // Check if running under webtests or not
69 if (is_subclass_of($unit_test, 'CiviSeleniumTestCase')) {
70 $this->_webtest = TRUE;
71 }
72
73 if ($startImmediately) {
74 $this->start();
75 }
76 }
77
78 /**
eceb18cc 79 * Start writing emails to db instead of current option.
6a488035 80 */
00be9182 81 public function start() {
6a488035
TO
82 if ($this->_webtest) {
83 // Change outbound mail setting
14d3f751 84 $this->_ut->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next");
6a488035
TO
85
86 // First remember the current setting
87 $this->_outBound_option = $this->getSelectedOutboundOption();
88
89 $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB . '"]');
14d3f751 90 $this->_ut->clickLink("_qf_Smtp_next");
6a488035
TO
91
92 // Is there supposed to be a status message displayed when outbound email settings are changed?
93 // assert something?
94
95 }
96 else {
97
98 // save current setting for outbound option, then change it
99 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
100 'mailing_backend'
101 );
102
103 $this->_outBound_option = $mailingBackend['outBound_option'];
104 $mailingBackend['outBound_option'] = CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB;
105
08ef4ddd 106 Civi::settings()->set('mailing_backend', $mailingBackend);
6a488035
TO
107
108 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
109 'mailing_backend'
110 );
111 }
112 }
113
00be9182 114 public function stop() {
6a488035 115 if ($this->_webtest) {
054c3522
CW
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 . '"]');
3a35908d 120 // There will be a warning when switching from test to live mode
054c3522 121 if ($this->_outBound_option != CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED) {
499508f0 122 $this->_ut->getAlert();
054c3522
CW
123 }
124 $this->_ut->clickLink("_qf_Smtp_next");
125 }
6a488035
TO
126 }
127 else {
128
129 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
130 'mailing_backend'
131 );
132
133 $mailingBackend['outBound_option'] = $this->_outBound_option;
134
08ef4ddd 135 Civi::settings()->set('mailing_backend', $mailingBackend);
6a488035
TO
136 }
137 }
138
4cbe18b8
EM
139 /**
140 * @param string $type
141 *
142 * @return ezcMail|string
143 */
00be9182 144 public function getMostRecentEmail($type = 'raw') {
6a488035
TO
145 $msg = '';
146
6a488035 147 if ($this->_webtest) {
6a488035 148 // I don't understand but for some reason we have to load the page twice for a recent mailing to appear.
14d3f751
CW
149 $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1');
150 $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1', 'css=td.crm-mailing-name');
151 }
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') {
6a488035 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.
b3d40287 155 $this->_ut->clickLink('xpath=//tr[contains(@id, "crm-mailing_")]//a[text()="Report"]');
6a488035
TO
156
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")]');
a5d61f09 160 $this->_ut->clickAjaxLink('xpath=//a[contains(text(), "View complete message")]');
14d3f751 161 $msg = $this->_ut->getText('css=.ui-dialog-content.crm-ajax-container');
6a488035
TO
162 }
163 else {
164 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
165 if ($dao->fetch()) {
166 $msg = $dao->headers . "\n\n" . $dao->body;
167 }
168 $dao->free();
169 }
170
171 switch ($type) {
172 case 'raw':
173 // nothing to do
174 break;
6c6e6187 175
6a488035
TO
176 case 'ezc':
177 $msg = $this->convertToEzc($msg);
178 break;
179 }
180 return $msg;
181 }
182
183 /**
e16033b4
TO
184 * @param string $type
185 * 'raw'|'ezc'.
cbdcc634
EM
186 *
187 * @throws Exception
6a488035
TO
188 * @return array(ezcMail)|array(string)
189 */
00be9182 190 public function getAllMessages($type = 'raw') {
6a488035
TO
191 $msgs = array();
192
193 if ($this->_webtest) {
c0f9acff 194 throw new Exception("Not implemented: getAllMessages for WebTest");
6a488035
TO
195 }
196 else {
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;
200 }
201 $dao->free();
202 }
203
204 switch ($type) {
205 case 'raw':
206 // nothing to do
207 break;
6c6e6187 208
6a488035
TO
209 case 'ezc':
210 foreach ($msgs as $i => $msg) {
211 $msgs[$i] = $this->convertToEzc($msg);
212 }
213 break;
214 }
215
216 return $msgs;
217 }
218
4cbe18b8
EM
219 /**
220 * @return int
221 */
00be9182 222 public function getSelectedOutboundOption() {
6a488035
TO
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;
229 break;
230 }
231 }
232 }
233 return $selectedOption;
234 }
235
236 /*
237 * Utility functions (previously part of CiviUnitTestCase)
238 * Included for backward compatibility with existing tests.
239 */
240
241 /**
eceb18cc 242 * Check contents of mail log.
77b97be7 243 *
e16033b4
TO
244 * @param array $strings
245 * Strings that should be included.
246 * @param array $absentStrings
247 * Strings that should not be included.
77b97be7 248 * @param string $prefix
6a488035 249 *
77b97be7 250 * @return \ezcMail|string
6a488035 251 */
00be9182 252 public function checkMailLog($strings, $absentStrings = array(), $prefix = '') {
6a488035 253 $mail = $this->getMostRecentEmail('raw');
481312d9 254 return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail);
255 }
256
257 /**
258 * Check contents of mail log.
259 *
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
265 *
266 * @return \ezcMail|string
267 */
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);
6a488035
TO
272 }
273
274 /**
eceb18cc 275 * Check that mail log is empty.
1e1fdcf6 276 * @param string $prefix
6a488035 277 */
00be9182 278 public function assertMailLogEmpty($prefix = '') {
6a488035
TO
279 $mail = $this->getMostRecentEmail('raw');
280 $this->_ut->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix);
281 }
282
283 /**
284 * Assert that $expectedRecipients (and no else) have received emails
285 *
e16033b4
TO
286 * @param array $expectedRecipients
287 * Array($msgPos => array($recipPos => $emailAddr)).
6a488035 288 */
00be9182 289 public function assertRecipients($expectedRecipients) {
6a488035
TO
290 $recipients = array();
291 foreach ($this->getAllMessages('ezc') as $message) {
292 $recipients[] = CRM_Utils_Array::collect('email', $message->to);
293 }
11f535e4
TO
294 $cmp = function($a, $b) {
295 if ($a[0] == $b[0]) {
296 return 0;
297 }
298 return ($a[0] < $b[0]) ? 1 : -1;
299 };
300 usort($recipients, $cmp);
301 usort($expectedRecipients, $cmp);
6a488035
TO
302 $this->_ut->assertEquals(
303 $expectedRecipients,
304 $recipients,
305 "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE)
306 );
307 }
308
f8c3594b
TO
309 /**
310 * Assert that $expectedSubjects (and no other subjects) were sent.
311 *
312 * @param array $expectedSubjects
313 * Array(string $subj).
314 */
315 public function assertSubjects($expectedSubjects) {
316 $subjects = array();
317 foreach ($this->getAllMessages('ezc') as $message) {
318 /** @var ezcMail $message */
319 $subjects[] = $message->subject;
320 }
321 sort($subjects);
322 sort($expectedSubjects);
323 $this->_ut->assertEquals(
324 $expectedSubjects,
325 $subjects,
326 "Incorrect subjects: " . print_r(array('expected' => $expectedSubjects, 'actual' => $subjects), TRUE)
327 );
328 }
329
6a488035 330 /**
eceb18cc 331 * Remove any sent messages from the log.
aa1febb5 332 *
333 * @param int $limit
b13d4a61 334 * How many recent messages to remove, defaults to 0 (all).
aa1febb5 335 *
336 * @throws \Exception
6a488035 337 */
b13d4a61 338 public function clearMessages($limit = 0) {
6a488035 339 if ($this->_webtest) {
c0f9acff 340 throw new Exception("Not implemented: clearMessages for WebTest");
6a488035
TO
341 }
342 else {
b13d4a61
SL
343 $sql = 'DELETE FROM civicrm_mailing_spool ORDER BY id DESC';
344 if ($limit) {
345 $sql .= ' LIMIT ' . $limit;
346 }
347 CRM_Core_DAO::executeQuery($sql);
6a488035
TO
348 }
349 }
350
351 /**
e16033b4
TO
352 * @param string $msg
353 * Email header and body.
6a488035
TO
354 * @return ezcMail
355 */
356 private function convertToEzc($msg) {
14d3f751 357 $set = new ezcMailVariableSet($msg);
6a488035
TO
358 $parser = new ezcMailParser();
359 $mail = $parser->parseMail($set);
360 $this->_ut->assertNotEmpty($mail, 'Cannot parse mail');
361 return $mail[0];
362 }
96025800 363
481312d9 364 /**
365 * @param $strings
366 * @param $absentStrings
367 * @param $prefix
368 * @param $mail
369 * @return mixed
370 */
371 public function checkMailForStrings($strings, $absentStrings, $prefix, $mail) {
372 foreach ($strings as $string) {
373 $this->_ut->assertContains($string, $mail, "$string . not found in $mail $prefix");
374 }
375 foreach ($absentStrings as $string) {
376 $this->_ut->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix");;
377 }
378 return $mail;
379 }
380
6a488035 381}