Aleksander Machniak
2013-01-30 a2a1f8d7ed2911c2a4bab05ae599a80ed690c9c4
Added automated in-browser tests (PHPUnit + Selenium Web Driver)
15 files added
463 ■■■■■ changed files
tests/Selenium/Addressbook/Addressbook.php 21 ●●●●● patch | view | raw | blame | history
tests/Selenium/Addressbook/Import.php 29 ●●●●● patch | view | raw | blame | history
tests/Selenium/Login.php 21 ●●●●● patch | view | raw | blame | history
tests/Selenium/Logout.php 20 ●●●●● patch | view | raw | blame | history
tests/Selenium/Mail/CheckRecent.php 14 ●●●●● patch | view | raw | blame | history
tests/Selenium/Mail/Compose.php 25 ●●●●● patch | view | raw | blame | history
tests/Selenium/Mail/Getunread.php 13 ●●●●● patch | view | raw | blame | history
tests/Selenium/Mail/List.php 25 ●●●●● patch | view | raw | blame | history
tests/Selenium/Mail/Mail.php 23 ●●●●● patch | view | raw | blame | history
tests/Selenium/Settings/About.php 14 ●●●●● patch | view | raw | blame | history
tests/Selenium/Settings/Folders.php 20 ●●●●● patch | view | raw | blame | history
tests/Selenium/Settings/Identities.php 19 ●●●●● patch | view | raw | blame | history
tests/Selenium/Settings/Settings.php 17 ●●●●● patch | view | raw | blame | history
tests/Selenium/bootstrap.php 181 ●●●●● patch | view | raw | blame | history
tests/Selenium/phpunit.xml 21 ●●●●● patch | view | raw | blame | history
tests/Selenium/Addressbook/Addressbook.php
New file
@@ -0,0 +1,21 @@
<?php
class Selenium_Addressbook_Addressbook extends Selenium_Test
{
    public function testAddressbook()
    {
        $this->go('addressbook');
        // check task
        $env = $this->get_env();
        $this->assertEquals('addressbook', $env['task']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('qsearchbox', $objects);
        $this->assertContains('folderlist', $objects);
        $this->assertContains('contactslist', $objects);
        $this->assertContains('countdisplay', $objects);
    }
}
tests/Selenium/Addressbook/Import.php
New file
@@ -0,0 +1,29 @@
<?php
class Selenium_Addressbook_Import extends Selenium_Test
{
    public function testImport()
    {
        $this->go('addressbook', 'import');
        // check task and action
        $env = $this->get_env();
        $this->assertEquals('addressbook', $env['task']);
        $this->assertEquals('import', $env['action']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('importform', $objects);
    }
    public function testImport2()
    {
        $this->go('addressbook', 'import');
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('importform', $objects);
    }
}
tests/Selenium/Login.php
New file
@@ -0,0 +1,21 @@
<?php
class Selenium_Login extends Selenium_Test
{
    public function testLogin()
    {
        // first test, we're already on the login page
        $this->url(TESTS_URL);
        // task should be set to 'login'
        $env = $this->get_env();
        $this->assertEquals('login', $env['task']);
        // test valid login
        $this->login();
        // task should be set to 'mail' now
        $env = $this->get_env();
        $this->assertEquals('mail', $env['task']);
    }
}
tests/Selenium/Logout.php
New file
@@ -0,0 +1,20 @@
<?php
class Selenium_Logout extends Selenium_Test
{
    public function testLogout()
    {
        $this->go('mail');
        $this->click_button('logout');
        sleep(TESTS_SLEEP);
        // task should be set to 'login'
        $env = $this->get_env();
        $this->assertEquals('login', $env['task']);
        // form should exist
        $user_input = $this->byCssSelector('form input[name="_user"]');
    }
}
tests/Selenium/Mail/CheckRecent.php
New file
@@ -0,0 +1,14 @@
<?php
class Selenium_Mail_CheckRecent extends Selenium_Test
{
    public function testCheckRecent()
    {
        $this->go('mail');
        $res = $this->ajaxResponse('check-recent', "rcmail.command('checkmail')");
        $this->assertEquals('check-recent', $res['action']);
        $this->assertRegExp('/this\.set_unread_count/', $res['exec']);
    }
}
tests/Selenium/Mail/Compose.php
New file
@@ -0,0 +1,25 @@
<?php
class Selenium_Mail_Compose extends Selenium_Test
{
    public function testCompose()
    {
        $this->go('mail', 'compose');
        // check task and action
        $env = $this->get_env();
        $this->assertEquals('mail', $env['task']);
        $this->assertEquals('compose', $env['action']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('qsearchbox', $objects);
        $this->assertContains('addressbookslist', $objects);
        $this->assertContains('contactslist', $objects);
        $this->assertContains('messageform', $objects);
        $this->assertContains('attachmentlist', $objects);
        $this->assertContains('filedrop', $objects);
        $this->assertContains('uploadform', $objects);
    }
}
tests/Selenium/Mail/Getunread.php
New file
@@ -0,0 +1,13 @@
<?php
class Selenium_Mail_Getunread extends Selenium_Test
{
    public function testGetunread()
    {
        $this->go('mail');
        $res = $this->ajaxResponse('getunread', "rcmail.http_request('getunread')");
        $this->assertEquals('getunread', $res['action']);
    }
}
tests/Selenium/Mail/List.php
New file
@@ -0,0 +1,25 @@
<?php
class Selenium_Mail_List extends Selenium_Test
{
    public function testCheckRecent()
    {
        $this->go('mail');
        $res = $this->ajaxResponse('list', "rcmail.command('list')");
        $this->assertEquals('list', $res['action']);
        $this->assertRegExp('/this\.set_pagetitle/', $res['exec']);
        $this->assertRegExp('/this\.set_unread_count/', $res['exec']);
        $this->assertRegExp('/this\.set_rowcount/', $res['exec']);
        $this->assertRegExp('/this\.set_message_coltypes/', $res['exec']);
//        $this->assertRegExp('/this\.add_message_row/', $res['exec']);
        $this->assertContains('current_page', $res['env']);
        $this->assertContains('exists', $res['env']);
        $this->assertContains('pagecount', $res['env']);
        $this->assertContains('pagesize', $res['env']);
        $this->assertContains('messagecount', $res['env']);
        $this->assertContains('mailbox', $res['env']);
    }
}
tests/Selenium/Mail/Mail.php
New file
@@ -0,0 +1,23 @@
<?php
class Selenium_Mail_Mail extends Selenium_Test
{
    public function testMail()
    {
        $this->go('mail');
        // check task
        $env = $this->get_env();
        $this->assertEquals('mail', $env['task']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('qsearchbox', $objects);
        $this->assertContains('mailboxlist', $objects);
        $this->assertContains('messagelist', $objects);
        $this->assertContains('quotadisplay', $objects);
        $this->assertContains('search_filter', $objects);
        $this->assertContains('countdisplay', $objects);
    }
}
tests/Selenium/Settings/About.php
New file
@@ -0,0 +1,14 @@
<?php
class Selenium_Settings_About extends Selenium_Test
{
    public function testAbout()
    {
        $this->url(TESTS_URL . '/?_task=settings&_action=about');
        // check task and action
        $env = $this->get_env();
        $this->assertEquals('settings', $env['task']);
        $this->assertEquals('about', $env['action']);
    }
}
tests/Selenium/Settings/Folders.php
New file
@@ -0,0 +1,20 @@
<?php
class Selenium_Settings_Folders extends Selenium_Test
{
    public function testFolders()
    {
        $this->go('settings', 'folders');
        // task should be set to 'settings' and action to 'folders'
        $env = $this->get_env();
        $this->assertEquals('settings', $env['task']);
        $this->assertEquals('folders', $env['action']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('quotadisplay', $objects);
        $this->assertContains('subscriptionlist', $objects);
    }
}
tests/Selenium/Settings/Identities.php
New file
@@ -0,0 +1,19 @@
<?php
class Selenium_Settings_Identities extends Selenium_Test
{
    public function testIdentities()
    {
        $this->go('settings', 'identities');
        // check task and action
        $env = $this->get_env();
        $this->assertEquals('settings', $env['task']);
        $this->assertEquals('identities', $env['action']);
        $objects = $this->get_objects();
        // these objects should be there always
        $this->assertContains('identitieslist', $objects);
    }
}
tests/Selenium/Settings/Settings.php
New file
@@ -0,0 +1,17 @@
<?php
class Selenium_Settings_Settings extends Selenium_Test
{
    public function testSettings()
    {
        $this->go('settings');
        // task should be set to 'settings'
        $env = $this->get_env();
        $this->assertEquals('settings', $env['task']);
        $objects = $this->get_objects();
        $this->assertContains('sectionslist', $objects);
    }
}
tests/Selenium/bootstrap.php
New file
@@ -0,0 +1,181 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | tests/Selenium/bootstrap.php                                          |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2009-2013, The Roundcube Dev Team                       |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
 | See the README file for a full license statement.                     |
 |                                                                       |
 | PURPOSE:                                                              |
 |   Environment initialization script for unit tests                    |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
*/
if (php_sapi_name() != 'cli')
  die("Not in shell mode (php-cli)");
if (!defined('INSTALL_PATH')) define('INSTALL_PATH', realpath(dirname(__FILE__) . '/../../') . '/' );
define('TESTS_DIR', dirname(__FILE__) . '/');
if (@is_dir(TESTS_DIR . 'config')) {
    define('RCMAIL_CONFIG_DIR', TESTS_DIR . 'config');
}
require_once(INSTALL_PATH . 'program/include/iniset.php');
// Extend include path so some plugin test won't fail
$include_path = ini_get('include_path') . PATH_SEPARATOR . TESTS_DIR . '..';
if (set_include_path($include_path) === false) {
    die("Fatal error: ini_set/set_include_path does not work.");
}
$rcmail = rcube::get_instance();
define('TESTS_URL',     $rcmail->config->get('tests_url'));
define('TESTS_BROWSER', $rcmail->config->get('tests_browser', 'firefox'));
define('TESTS_USER',    $rcmail->config->get('tests_username'));
define('TESTS_PASS',    $rcmail->config->get('tests_password'));
define('TESTS_SLEEP',   $rcmail->config->get('tests_sleep', 5));
PHPUnit_Extensions_Selenium2TestCase::shareSession(true);
// @TODO: remove user record from DB before running tests
// @TODO: make sure mailbox has some content (always the same) or is empty
/**
 * Base class for all tests in this directory
 */
class Selenium_Test extends PHPUnit_Extensions_Selenium2TestCase
{
    protected function setUp()
    {
//        $this->rc = rcube::get_instance();
        $this->setBrowser(TESTS_BROWSER);
        $this->setBrowserUrl(TESTS_URL);
    }
    protected function login()
    {
        $this->go('mail');
        $user_input = $this->byCssSelector('form input[name="_user"]');
        $pass_input = $this->byCssSelector('form input[name="_pass"]');
        $submit     = $this->byCssSelector('form input[type="submit"]');
        $user_input->value(TESTS_USER);
        $pass_input->value(TESTS_PASS);
        // submit login form
        $submit->click();
        // wait after successful login
        sleep(TESTS_SLEEP);
    }
    protected function go($task = 'mail', $action = null)
    {
        $this->url(TESTS_URL . '/?_task=' . $task);
        // wait for interface load (initial ajax requests, etc.)
        sleep(TESTS_SLEEP);
        if ($action) {
            $this->click_button($action);
            sleep(TESTS_SLEEP);
        }
    }
    protected function get_env()
    {
        return $this->execute(array(
            'script' => 'return rcmail.env;',
            'args' => array(),
        ));
    }
    protected function get_buttons($action)
    {
        $buttons = $this->execute(array(
            'script' => "return rcmail.buttons['$action'];",
            'args' => array(),
        ));
        if (is_array($buttons)) {
            foreach ($buttons as $idx => $button) {
                $buttons[$idx] = $button['id'];
            }
        }
        return (array) $buttons;
    }
    protected function get_objects()
    {
        return $this->execute(array(
            'script' => "var i,r = []; for (i in rcmail.gui_objects) r.push(i); return r;",
            'args' => array(),
        ));
    }
    protected function click_button($action)
    {
        $buttons = $this->get_buttons($action);
        $id      = array_shift($buttons);
        // this doesn't work for me
        $this->byId($id)->click();
    }
    protected function ajaxResponse($action, $script = '', $button = false)
    {
        if (!$script && !$button) {
            $script = "rcmail.command('$action')";
        }
        $script =
        "if (!window.test_ajax_response) {
            window.test_ajax_response_object = {};
            function test_ajax_response(response)
            {
                if (response.response && response.response.action) {
                    window.test_ajax_response_object[response.response.action] = response.response;
                }
            }
            rcmail.addEventListener('responsebefore', test_ajax_response);
        }
        window.test_ajax_response_object['$action'] = null;
        $script;
        ";
        // run request
        $this->execute(array(
            'script' => $script,
            'args' => array(),
        ));
        if ($button) {
            $this->click_button($action);
        }
        // wait
        sleep(TESTS_SLEEP);
        // get response
        $response = $this->execute(array(
            'script' => "return window.test_ajax_response_object['$action'];",
            'args' => array(),
        ));
        return $response;
    }
}
tests/Selenium/phpunit.xml
New file
@@ -0,0 +1,21 @@
<phpunit backupGlobals="false"
    bootstrap="bootstrap.php"
    colors="true">
    <testsuites>
        <testsuite name="All Tests">
            <file>Login.php</file><!-- Login.php test must be first -->
            <file>Addressbook/Addressbook.php</file>
            <file>Addressbook/Import.php</file>
            <file>Mail/Mail.php</file>
            <file>Mail/CheckRecent.php</file>
            <file>Mail/Compose.php</file>
            <file>Mail/Getunread.php</file>
            <file>Mail/List.php</file>
            <file>Settings/About.php</file>
            <file>Settings/Folders.php</file>
            <file>Settings/Identities.php</file>
            <file>Settings/Settings.php</file>
            <file>Logout.php</file><!-- Logout.php test must be last -->
        </testsuite>
    </testsuites>
</phpunit>