Merge pull request #3749 from civicrm/4.4
[civicrm-core.git] / tests / phpunit / CiviTest / CiviMailUtils.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06a1bc01 4 | CiviCRM version 4.5 |
6a488035 5 +--------------------------------------------------------------------+
06a1bc01 6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 +--------------------------------------------------------------------+
25*/
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
43require_once 'ezc/Base/src/ezc_bootstrap.php';
44require_once 'ezc/autoload/mail_autoload.php';
45
4cbe18b8
EM
46/**
47 * Class CiviMailUtils
48 */
6a488035
TO
49class CiviMailUtils extends PHPUnit_Framework_TestCase {
50
51 /**
52 * @var mixed current outbound email option
53 */
54 protected $_outBound_option = NULL;
55
56 /**
57 * @var bool is this a webtest
58 */
59 protected $_webtest = FALSE;
60
61 /**
62 * Constructor
63 *
64 * @param $unit_test object The currently running test
65 * @param $startImmediately bool Start writing to db now or wait until start() is called
66 */
67 function __construct(&$unit_test, $startImmediately = TRUE) {
68 $this->_ut = $unit_test;
69
70 // Check if running under webtests or not
71 if (is_subclass_of($unit_test, 'CiviSeleniumTestCase')) {
72 $this->_webtest = TRUE;
73 }
74
75 if ($startImmediately) {
76 $this->start();
77 }
78 }
79
80 /**
81 * Start writing emails to db instead of current option
82 */
83 function start() {
84 if ($this->_webtest) {
85 // Change outbound mail setting
86 $this->_ut->open($this->_ut->sboxPath . "civicrm/admin/setting/smtp?reset=1");
87 $this->_ut->waitForElementPresent("_qf_Smtp_next");
88
89 // First remember the current setting
90 $this->_outBound_option = $this->getSelectedOutboundOption();
91
92 $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB . '"]');
93 $this->_ut->click("_qf_Smtp_next");
94 $this->_ut->waitForPageToLoad("30000");
95
96 // Is there supposed to be a status message displayed when outbound email settings are changed?
97 // assert something?
98
99 }
100 else {
101
102 // save current setting for outbound option, then change it
103 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
104 'mailing_backend'
105 );
106
107 $this->_outBound_option = $mailingBackend['outBound_option'];
108 $mailingBackend['outBound_option'] = CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB;
109
110 CRM_Core_BAO_Setting::setItem($mailingBackend,
111 CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
112 'mailing_backend'
113 );
114
115 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
116 'mailing_backend'
117 );
118 }
119 }
120
121 function stop() {
122 if ($this->_webtest) {
123
124 $this->_ut->open($this->_ut->sboxPath . "civicrm/admin/setting/smtp?reset=1");
125 $this->_ut->waitForElementPresent("_qf_Smtp_next");
126 $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . $this->_outBound_option . '"]');
127 $this->_ut->click("_qf_Smtp_next");
128 $this->_ut->waitForPageToLoad("30000");
129
130 // Is there supposed to be a status message displayed when outbound email settings are changed?
131 // assert something?
132
133 }
134 else {
135
136 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
137 'mailing_backend'
138 );
139
140 $mailingBackend['outBound_option'] = $this->_outBound_option;
141
142 CRM_Core_BAO_Setting::setItem($mailingBackend,
143 CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
144 'mailing_backend'
145 );
146 }
147 }
148
4cbe18b8
EM
149 /**
150 * @param string $type
151 *
152 * @return ezcMail|string
153 */
6a488035
TO
154 function getMostRecentEmail($type = 'raw') {
155 $msg = '';
156
157 // Check if running under webtests or not
158 if ($this->_webtest) {
159
160 $this->_ut->open($this->_ut->sboxPath . 'civicrm/mailing/browse/archived?reset=1');
161 // I don't understand but for some reason we have to load the page twice for a recent mailing to appear.
162 $this->_ut->waitForPageToLoad("30000");
163 $this->_ut->open($this->_ut->sboxPath . 'civicrm/mailing/browse/archived?reset=1');
164 $this->_ut->waitForElementPresent('css=td.crm-mailing-name');
165
166 // 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.
167 $this->_ut->click('xpath=//tr[contains(@id, "crm-mailing_")]//a[text()="Report"]');
168
169 // Also not sure how robust this is, but there isn't a good
170 // identifier for this link either.
171 $this->_ut->waitForElementPresent('xpath=//a[contains(text(), "View complete message")]');
172 $this->_ut->click('xpath=//a[contains(text(), "View complete message")]');
173
174 $this->_ut->waitForPopUp(NULL, 30000);
175 $this->_ut->selectPopUp(NULL);
176 /*
177 * FIXME:
178 *
179 * Argh.
180 * getBodyText() doesn't work because you can't get the actual html, just the rendered version.
181 * getHtmlSource() doesn't work because it sees email addresses as html tags and inserts its own closing tags.
182 *
183 * At the moment the combination of escaping just the headers in CRM_Mailing_BAO_Spool plus using getBodyText() works well enough to do basic unit testing and also not screw up the display in the actual UI.
184 */
185 //$msg = $this->_ut->getHtmlSource();
186 $msg = $this->_ut->getBodyText();
187 $this->_ut->close();
188 $this->_ut->selectWindow(NULL);
189
190 }
191 else {
192 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
193 if ($dao->fetch()) {
194 $msg = $dao->headers . "\n\n" . $dao->body;
195 }
196 $dao->free();
197 }
198
199 switch ($type) {
200 case 'raw':
201 // nothing to do
202 break;
203 case 'ezc':
204 $msg = $this->convertToEzc($msg);
205 break;
206 }
207 return $msg;
208 }
209
210 /**
211 * @param string $type 'raw'|'ezc'
cbdcc634
EM
212 *
213 * @throws Exception
6a488035
TO
214 * @return array(ezcMail)|array(string)
215 */
216 function getAllMessages($type = 'raw') {
217 $msgs = array();
218
219 if ($this->_webtest) {
220 throw new Exception("Not implementated: getAllMessages for WebTest");
221 }
222 else {
223 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id');
224 while ($dao->fetch()) {
225 $msgs[] = $dao->headers . "\n\n" . $dao->body;
226 }
227 $dao->free();
228 }
229
230 switch ($type) {
231 case 'raw':
232 // nothing to do
233 break;
234 case 'ezc':
235 foreach ($msgs as $i => $msg) {
236 $msgs[$i] = $this->convertToEzc($msg);
237 }
238 break;
239 }
240
241 return $msgs;
242 }
243
4cbe18b8
EM
244 /**
245 * @return int
246 */
6a488035
TO
247 function getSelectedOutboundOption() {
248 $selectedOption = CRM_Mailing_Config::OUTBOUND_OPTION_MAIL;
249 // Is there a better way to do this? How do you get the currently selected value of a radio button in selenium?
250 for ($i = 0; $i <= 5; $i++) {
251 if ($i != CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) {
252 if ($this->_ut->getValue('xpath=//input[@name="outBound_option" and @value="' . $i . '"]') == "on") {
253 $selectedOption = $i;
254 break;
255 }
256 }
257 }
258 return $selectedOption;
259 }
260
261 /*
262 * Utility functions (previously part of CiviUnitTestCase)
263 * Included for backward compatibility with existing tests.
264 */
265
266 /**
267 * Check contents of mail log
77b97be7 268 *
6a488035
TO
269 * @param array $strings strings that should be included
270 * @param array $absentStrings strings that should not be included
77b97be7 271 * @param string $prefix
6a488035 272 *
77b97be7 273 * @return \ezcMail|string
6a488035
TO
274 */
275 function checkMailLog($strings, $absentStrings = array(), $prefix = '') {
276 $mail = $this->getMostRecentEmail('raw');
277 foreach ($strings as $string) {
278 $this->_ut->assertContains($string, $mail, "$string . not found in $mail $prefix");
279 }
280 foreach ($absentStrings as $string) {
281 $this->_ut->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix");
282 ;
283 }
284 return $mail;
285 }
286
287 /**
288 * Check that mail log is empty
289 */
290 function assertMailLogEmpty($prefix = '') {
291 $mail = $this->getMostRecentEmail('raw');
292 $this->_ut->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix);
293 }
294
295 /**
296 * Assert that $expectedRecipients (and no else) have received emails
297 *
298 * @param array $expectedRecipients array($msgPos => array($recipPos => $emailAddr))
299 */
300 function assertRecipients($expectedRecipients) {
301 $recipients = array();
302 foreach ($this->getAllMessages('ezc') as $message) {
303 $recipients[] = CRM_Utils_Array::collect('email', $message->to);
304 }
305 sort($recipients);
306 sort($expectedRecipients);
307 $this->_ut->assertEquals(
308 $expectedRecipients,
309 $recipients,
310 "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE)
311 );
312 }
313
314 /**
315 * Remove any sent messages from the log
316 */
317 function clearMessages() {
318 if ($this->_webtest) {
319 throw new Exception("Not implementated: clearMessages for WebTest");
320 }
321 else {
322 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
323 }
324 }
325
326 /**
327 * @param string $msg email header and body
328 * @return ezcMail
329 */
330 private function convertToEzc($msg) {
331 $set = new ezcMailVariableSet($msg);
332 $parser = new ezcMailParser();
333 $mail = $parser->parseMail($set);
334 $this->_ut->assertNotEmpty($mail, 'Cannot parse mail');
335 return $mail[0];
336 }
337}