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