Aleksander Machniak
2016-05-22 87cf0a3fb158b5ffaa54a79997d7b01492d39b74
commit | author | age
a2a1f8 1 <?php
AM 2
3 /*
4  +-----------------------------------------------------------------------+
5  | tests/Selenium/bootstrap.php                                          |
6  |                                                                       |
7  | This file is part of the Roundcube Webmail client                     |
d220eb 8  | Copyright (C) 2009-2014, The Roundcube Dev Team                       |
a2a1f8 9  |                                                                       |
AM 10  | Licensed under the GNU General Public License version 3 or            |
11  | any later version with exceptions for skins & plugins.                |
12  | See the README file for a full license statement.                     |
13  |                                                                       |
14  | PURPOSE:                                                              |
15  |   Environment initialization script for unit tests                    |
16  +-----------------------------------------------------------------------+
17  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
18  | Author: Aleksander Machniak <alec@alec.pl>                            |
19  +-----------------------------------------------------------------------+
20 */
21
22 if (php_sapi_name() != 'cli')
23   die("Not in shell mode (php-cli)");
24
0ea079 25 if (!defined('INSTALL_PATH')) define('INSTALL_PATH', realpath(__DIR__ . '/../../') . '/' );
a2a1f8 26
f7c5f4 27 define('TESTS_DIR', realpath(__DIR__ . '/../') . '/');
a2a1f8 28
AM 29 if (@is_dir(TESTS_DIR . 'config')) {
deb2b8 30     define('RCUBE_CONFIG_DIR', TESTS_DIR . 'config');
a2a1f8 31 }
AM 32
33 require_once(INSTALL_PATH . 'program/include/iniset.php');
34
35 // Extend include path so some plugin test won't fail
36 $include_path = ini_get('include_path') . PATH_SEPARATOR . TESTS_DIR . '..';
37 if (set_include_path($include_path) === false) {
38     die("Fatal error: ini_set/set_include_path does not work.");
39 }
40
1b39d9 41 $rcmail = rcmail::get_instance(0, 'test');
a2a1f8 42
AM 43 define('TESTS_URL',     $rcmail->config->get('tests_url'));
44 define('TESTS_BROWSER', $rcmail->config->get('tests_browser', 'firefox'));
45 define('TESTS_USER',    $rcmail->config->get('tests_username'));
46 define('TESTS_PASS',    $rcmail->config->get('tests_password'));
47 define('TESTS_SLEEP',   $rcmail->config->get('tests_sleep', 5));
48
49 PHPUnit_Extensions_Selenium2TestCase::shareSession(true);
50
d220eb 51
TB 52 /**
53  * satisfy PHPUnit
54  */
55 class bootstrap
56 {
28b1cb 57     static $imap_ready = null;
TB 58
d220eb 59     /**
TB 60      * Wipe and re-initialize (mysql) database
61      */
62     public static function init_db()
63     {
64         $rcmail = rcmail::get_instance();
0d4d43 65         $dsn = rcube_db::parse_dsn($rcmail->config->get('db_dsnw'));
d220eb 66
0d4d43 67         if ($dsn['phptype'] == 'mysql' || $dsn['phptype'] == 'mysqli') {
TB 68             // drop all existing tables first
69             $db = $rcmail->get_dbh();
70             $db->query("SET FOREIGN_KEY_CHECKS=0");
71             $sql_res = $db->query("SHOW TABLES");
72             while ($sql_arr = $db->fetch_array($sql_res)) {
73                 $table = reset($sql_arr);
74                 $db->query("DROP TABLE $table");
75             }
d220eb 76
0d4d43 77             // init database with schema
d220eb 78             system(sprintf('cat %s %s | mysql -h %s -u %s --password=%s %s',
TB 79                 realpath(INSTALL_PATH . '/SQL/mysql.initial.sql'),
f7c5f4 80                 realpath(TESTS_DIR . 'Selenium/data/mysql.sql'),
91f217 81                 escapeshellarg($dsn['hostspec']),
TB 82                 escapeshellarg($dsn['username']),
83                 escapeshellarg($dsn['password']),
0d4d43 84                 escapeshellarg($dsn['database'])
d220eb 85             ));
TB 86         }
0d4d43 87         else if ($dsn['phptype'] == 'sqlite') {
TB 88             // delete database file -- will be re-initialized on first access
89             system(sprintf('rm -f %s', escapeshellarg($dsn['database'])));
90         }
d220eb 91     }
TB 92
93     /**
94      * Wipe the configured IMAP account and fill with test data
95      */
96     public static function init_imap()
97     {
28b1cb 98         if (!TESTS_USER) {
d220eb 99             return false;
28b1cb 100         }
TB 101         else if (self::$imap_ready !== null) {
102             return self::$imap_ready;
103         }
d220eb 104
2fece8 105         self::connect_imap(TESTS_USER, TESTS_PASS);
TB 106         self::purge_mailbox('INBOX');
107         self::ensure_mailbox('Archive', true);
108
109         return self::$imap_ready;
110     }
111
112     /**
113      * Authenticate to IMAP with the given credentials
114      */
115     public static function connect_imap($username, $password, $host = null)
116     {
f7c5f4 117         $rcmail = rcmail::get_instance();
TB 118         $imap = $rcmail->get_storage();
119
2fece8 120         if ($imap->is_connected()) {
TB 121             $imap->close();
122             self::$imap_ready = false;
123         }
124
125         $imap_host = $host ?: $rcmail->config->get('default_host');
126         $a_host = parse_url($imap_host);
f7c5f4 127         if ($a_host['host']) {
TB 128             $imap_host = $a_host['host'];
2fece8 129             $imap_ssl  = isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'));
f7c5f4 130             $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143);
TB 131         }
132         else {
133             $imap_port = 143;
134             $imap_ssl = false;
135         }
136
2fece8 137         if (!$imap->connect($imap_host, $username, $password, $imap_port, $imap_ssl)) {
f7c5f4 138             die("IMAP error: unable to authenticate with user " . TESTS_USER);
TB 139         }
140
28b1cb 141         self::$imap_ready = true;
TB 142     }
143
144     /**
145      * Import the given file into IMAP
146      */
147     public static function import_message($filename, $mailbox = 'INBOX')
148     {
149         if (!self::init_imap()) {
150             die(__METHOD__ . ': IMAP connection unavailable');
151         }
152
153         $imap = rcmail::get_instance()->get_storage();
154         $imap->save_message($mailbox, file_get_contents($filename));
155     }
156
157     /**
158      * Delete all messages from the given mailbox
159      */
160     public static function purge_mailbox($mailbox)
161     {
162         if (!self::init_imap()) {
163             die(__METHOD__ . ': IMAP connection unavailable');
164         }
165
166         $imap = rcmail::get_instance()->get_storage();
167         $imap->delete_message('*', $mailbox);
168     }
169
170     /**
171      * Make sure the given mailbox exists in IMAP
172      */
173     public static function ensure_mailbox($mailbox, $empty = false)
174     {
175         if (!self::init_imap()) {
176             die(__METHOD__ . ': IMAP connection unavailable');
177         }
178
179         $imap = rcmail::get_instance()->get_storage();
180
f7c5f4 181         $folders = $imap->list_folders();
28b1cb 182         if (!in_array($mailbox, $folders)) {
TB 183             $imap->create_folder($mailbox, true);
f7c5f4 184         }
28b1cb 185         else if ($empty) {
TB 186             $imap->delete_message('*', $mailbox);
f7c5f4 187         }
d220eb 188     }
TB 189 }
190
a2a1f8 191 // @TODO: make sure mailbox has some content (always the same) or is empty
846669 192 // @TODO: plugins: enable all?
a2a1f8 193
AM 194 /**
195  * Base class for all tests in this directory
196  */
197 class Selenium_Test extends PHPUnit_Extensions_Selenium2TestCase
198 {
2fece8 199     protected $login_data = null;
TB 200
a2a1f8 201     protected function setUp()
AM 202     {
203         $this->setBrowser(TESTS_BROWSER);
2fece8 204         $this->login_data = array(TESTS_USER, TESTS_PASS);
846669 205
AM 206         // Set root to our index.html, for better performance
207         // See https://github.com/sebastianbergmann/phpunit-selenium/issues/217
d220eb 208         $baseurl = preg_replace('!/index(-.+)?\.php^!', '', TESTS_URL);
TB 209         $this->setBrowserUrl($baseurl . '/tests/Selenium');
a2a1f8 210     }
AM 211
2fece8 212     protected function login($username = null, $password = null)
a2a1f8 213     {
2fece8 214         if (!empty($username)) {
TB 215             $this->login_data = array($username, $password);
216         }
217
f7c5f4 218         $this->go('mail', null, true);
TB 219     }
a2a1f8 220
f7c5f4 221     protected function do_login()
TB 222     {
a2a1f8 223         $user_input = $this->byCssSelector('form input[name="_user"]');
AM 224         $pass_input = $this->byCssSelector('form input[name="_pass"]');
225         $submit     = $this->byCssSelector('form input[type="submit"]');
226
2fece8 227         $user_input->value($this->login_data[0]);
TB 228         $pass_input->value($this->login_data[1]);
a2a1f8 229
AM 230         // submit login form
231         $submit->click();
232
233         // wait after successful login
234         sleep(TESTS_SLEEP);
235     }
236
f7c5f4 237     protected function go($task = 'mail', $action = null, $login = true)
a2a1f8 238     {
d220eb 239         $this->url(TESTS_URL . '?_task=' . $task);
a2a1f8 240
AM 241         // wait for interface load (initial ajax requests, etc.)
242         sleep(TESTS_SLEEP);
243
f7c5f4 244         // check if we have a valid session
TB 245         $env = $this->get_env();
246         if ($login && $env['task'] == 'login') {
247             $this->do_login();
248         }
249
a2a1f8 250         if ($action) {
AM 251             $this->click_button($action);
252             sleep(TESTS_SLEEP);
253         }
254     }
255
256     protected function get_env()
257     {
258         return $this->execute(array(
f7c5f4 259             'script' => 'return window.rcmail ? rcmail.env : {};',
a2a1f8 260             'args' => array(),
AM 261         ));
262     }
263
264     protected function get_buttons($action)
265     {
266         $buttons = $this->execute(array(
267             'script' => "return rcmail.buttons['$action'];",
268             'args' => array(),
269         ));
270
271         if (is_array($buttons)) {
272             foreach ($buttons as $idx => $button) {
273                 $buttons[$idx] = $button['id'];
274             }
275         }
276
277         return (array) $buttons;
278     }
279
280     protected function get_objects()
281     {
282         return $this->execute(array(
283             'script' => "var i,r = []; for (i in rcmail.gui_objects) r.push(i); return r;",
284             'args' => array(),
285         ));
286     }
287
288     protected function click_button($action)
289     {
290         $buttons = $this->get_buttons($action);
291         $id      = array_shift($buttons);
292
293         // this doesn't work for me
294         $this->byId($id)->click();
295     }
296
297     protected function ajaxResponse($action, $script = '', $button = false)
298     {
299         if (!$script && !$button) {
300             $script = "rcmail.command('$action')";
301         }
302
303         $script = 
304         "if (!window.test_ajax_response) {
305             window.test_ajax_response_object = {};
306             function test_ajax_response(response)
307             {
308                 if (response.response && response.response.action) {
309                     window.test_ajax_response_object[response.response.action] = response.response;
310                 }
311             }
312             rcmail.addEventListener('responsebefore', test_ajax_response);
313         }
314         window.test_ajax_response_object['$action'] = null;
315         $script;
316         ";
317
318         // run request
319         $this->execute(array(
320             'script' => $script,
321             'args' => array(),
322         ));
323
324         if ($button) {
325             $this->click_button($action);
326         }
327
328         // wait
329         sleep(TESTS_SLEEP);
330
331         // get response
332         $response = $this->execute(array(
f7c5f4 333             'script' => "return window.test_ajax_response_object ? test_ajax_response_object['$action'] : {};",
a2a1f8 334             'args' => array(),
AM 335         ));
336
337         return $response;
338     }
f7c5f4 339
9311fe 340     protected function getText($element)
TB 341     {
342         return $element->text() ?: $element->attribute('textContent');
343     }
344
f7c5f4 345     protected function assertHasClass($classname, $element)
TB 346     {
347         $this->assertContains($classname, $element->attribute('class'));
348     }
a2a1f8 349 }