Merge pull request #15630 from eileenmcnaughton/pay_fail
[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 */
a6439b6a 46class CiviMailUtils extends PHPUnit\Framework\TestCase {
6a488035
TO
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 }
6a488035
TO
168 }
169
170 switch ($type) {
171 case 'raw':
172 // nothing to do
173 break;
6c6e6187 174
6a488035
TO
175 case 'ezc':
176 $msg = $this->convertToEzc($msg);
177 break;
178 }
179 return $msg;
180 }
181
182 /**
e16033b4
TO
183 * @param string $type
184 * 'raw'|'ezc'.
cbdcc634
EM
185 *
186 * @throws Exception
6a488035
TO
187 * @return array(ezcMail)|array(string)
188 */
00be9182 189 public function getAllMessages($type = 'raw') {
6a488035
TO
190 $msgs = array();
191
192 if ($this->_webtest) {
c0f9acff 193 throw new Exception("Not implemented: getAllMessages for WebTest");
6a488035
TO
194 }
195 else {
196 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id');
197 while ($dao->fetch()) {
198 $msgs[] = $dao->headers . "\n\n" . $dao->body;
199 }
6a488035
TO
200 }
201
202 switch ($type) {
203 case 'raw':
204 // nothing to do
205 break;
6c6e6187 206
6a488035
TO
207 case 'ezc':
208 foreach ($msgs as $i => $msg) {
209 $msgs[$i] = $this->convertToEzc($msg);
210 }
211 break;
212 }
213
214 return $msgs;
215 }
216
4cbe18b8
EM
217 /**
218 * @return int
219 */
00be9182 220 public function getSelectedOutboundOption() {
6a488035
TO
221 $selectedOption = CRM_Mailing_Config::OUTBOUND_OPTION_MAIL;
222 // Is there a better way to do this? How do you get the currently selected value of a radio button in selenium?
223 for ($i = 0; $i <= 5; $i++) {
224 if ($i != CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) {
225 if ($this->_ut->getValue('xpath=//input[@name="outBound_option" and @value="' . $i . '"]') == "on") {
226 $selectedOption = $i;
227 break;
228 }
229 }
230 }
231 return $selectedOption;
232 }
233
234 /*
235 * Utility functions (previously part of CiviUnitTestCase)
236 * Included for backward compatibility with existing tests.
237 */
238
239 /**
eceb18cc 240 * Check contents of mail log.
77b97be7 241 *
e16033b4
TO
242 * @param array $strings
243 * Strings that should be included.
244 * @param array $absentStrings
245 * Strings that should not be included.
77b97be7 246 * @param string $prefix
6a488035 247 *
77b97be7 248 * @return \ezcMail|string
6a488035 249 */
00be9182 250 public function checkMailLog($strings, $absentStrings = array(), $prefix = '') {
6a488035 251 $mail = $this->getMostRecentEmail('raw');
481312d9 252 return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail);
253 }
254
255 /**
256 * Check contents of mail log.
257 *
258 * @param array $strings
259 * Strings that should be included.
260 * @param array $absentStrings
261 * Strings that should not be included.
262 * @param string $prefix
263 *
264 * @return \ezcMail|string
265 */
266 public function checkAllMailLog($strings, $absentStrings = array(), $prefix = '') {
267 $mails = $this->getAllMessages('raw');
268 $mail = implode(',', $mails);
269 return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail);
6a488035
TO
270 }
271
272 /**
eceb18cc 273 * Check that mail log is empty.
1e1fdcf6 274 * @param string $prefix
6a488035 275 */
00be9182 276 public function assertMailLogEmpty($prefix = '') {
6a488035
TO
277 $mail = $this->getMostRecentEmail('raw');
278 $this->_ut->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix);
279 }
280
281 /**
282 * Assert that $expectedRecipients (and no else) have received emails
283 *
e16033b4
TO
284 * @param array $expectedRecipients
285 * Array($msgPos => array($recipPos => $emailAddr)).
6a488035 286 */
00be9182 287 public function assertRecipients($expectedRecipients) {
6a488035
TO
288 $recipients = array();
289 foreach ($this->getAllMessages('ezc') as $message) {
290 $recipients[] = CRM_Utils_Array::collect('email', $message->to);
291 }
11f535e4
TO
292 $cmp = function($a, $b) {
293 if ($a[0] == $b[0]) {
294 return 0;
295 }
296 return ($a[0] < $b[0]) ? 1 : -1;
297 };
298 usort($recipients, $cmp);
299 usort($expectedRecipients, $cmp);
6a488035
TO
300 $this->_ut->assertEquals(
301 $expectedRecipients,
302 $recipients,
303 "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE)
304 );
305 }
306
f8c3594b
TO
307 /**
308 * Assert that $expectedSubjects (and no other subjects) were sent.
309 *
310 * @param array $expectedSubjects
311 * Array(string $subj).
312 */
313 public function assertSubjects($expectedSubjects) {
314 $subjects = array();
315 foreach ($this->getAllMessages('ezc') as $message) {
316 /** @var ezcMail $message */
317 $subjects[] = $message->subject;
318 }
319 sort($subjects);
320 sort($expectedSubjects);
321 $this->_ut->assertEquals(
322 $expectedSubjects,
323 $subjects,
324 "Incorrect subjects: " . print_r(array('expected' => $expectedSubjects, 'actual' => $subjects), TRUE)
325 );
326 }
327
6a488035 328 /**
eceb18cc 329 * Remove any sent messages from the log.
aa1febb5 330 *
331 * @param int $limit
b13d4a61 332 * How many recent messages to remove, defaults to 0 (all).
aa1febb5 333 *
1e0f58c7 334 * @throws \CRM_Core_Exception
6a488035 335 */
b13d4a61 336 public function clearMessages($limit = 0) {
6a488035 337 if ($this->_webtest) {
1e0f58c7 338 throw new \CRM_Core_Exception("Not implemented: clearMessages for WebTest");
6a488035
TO
339 }
340 else {
b13d4a61
SL
341 $sql = 'DELETE FROM civicrm_mailing_spool ORDER BY id DESC';
342 if ($limit) {
343 $sql .= ' LIMIT ' . $limit;
344 }
345 CRM_Core_DAO::executeQuery($sql);
6a488035
TO
346 }
347 }
348
349 /**
e16033b4
TO
350 * @param string $msg
351 * Email header and body.
6a488035
TO
352 * @return ezcMail
353 */
354 private function convertToEzc($msg) {
14d3f751 355 $set = new ezcMailVariableSet($msg);
6a488035
TO
356 $parser = new ezcMailParser();
357 $mail = $parser->parseMail($set);
358 $this->_ut->assertNotEmpty($mail, 'Cannot parse mail');
359 return $mail[0];
360 }
96025800 361
481312d9 362 /**
363 * @param $strings
364 * @param $absentStrings
365 * @param $prefix
366 * @param $mail
367 * @return mixed
368 */
369 public function checkMailForStrings($strings, $absentStrings, $prefix, $mail) {
370 foreach ($strings as $string) {
371 $this->_ut->assertContains($string, $mail, "$string . not found in $mail $prefix");
372 }
373 foreach ($absentStrings as $string) {
374 $this->_ut->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix");;
375 }
376 return $mail;
377 }
378
6a488035 379}