Page MenuHomePhorge

No OneTemporary

Size
89 KB
Referenced Files
None
Subscribers
None
diff --git a/tests/Browser/Browser.php b/tests/Browser/Browser.php
new file mode 100644
index 000000000..91ff17b15
--- /dev/null
+++ b/tests/Browser/Browser.php
@@ -0,0 +1,248 @@
+<?php
+
+namespace Tests\Browser;
+
+use Facebook\WebDriver\WebDriverKeys;
+use PHPUnit\Framework\Assert;
+use Tests\Browser\Components;
+
+/**
+ * Laravel Dusk Browser extensions
+ */
+class Browser extends \Laravel\Dusk\Browser
+{
+ /**
+ * Assert specified rcmail.env value
+ */
+ public function assertEnvEquals($key, $expected)
+ {
+ $this->assertEquals($expected, $this->getEnv($key));
+
+ return $this;
+ }
+
+ /**
+ * Assert specified checkbox state
+ */
+ public function assertCheckboxState($selector, $state)
+ {
+ if ($state) {
+ $this->assertChecked($selector);
+ }
+ else {
+ $this->assertNotChecked($selector);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Assert Task menu state
+ */
+ public function assertTaskMenu($selected)
+ {
+ $this->with(new Components\Taskmenu(), function ($browser) use ($selected) {
+ $browser->assertMenuState($selected);
+ });
+
+ return $this;
+ }
+
+ /**
+ * Assert toolbar menu state
+ */
+ public function assertToolbarMenu($active, $disabled)
+ {
+ $this->with(new Components\Toolbarmenu(), function ($browser) use ($active, $disabled) {
+ $browser->assertMenuState($active, $disabled);
+ });
+
+ return $this;
+ }
+
+ /**
+ * Close toolbar menu (on phones)
+ */
+ public function closeToolbarMenu()
+ {
+ $this->with(new Components\Toolbarmenu(), function ($browser) {
+ $browser->closeMenu();
+ });
+
+ return $this;
+ }
+
+ /**
+ * Select taskmenu item
+ */
+ public function clickTaskMenuItem($name)
+ {
+ $this->with(new Components\Taskmenu(), function ($browser) use ($name) {
+ $browser->clickMenuItem($name);
+ });
+
+ return $this;
+ }
+
+ /**
+ * Select toolbar menu item
+ */
+ public function clickToolbarMenuItem($name, $dropdown_action = null)
+ {
+ $this->with(new Components\Toolbarmenu(), function ($browser) use ($name, $dropdown_action) {
+ $browser->clickMenuItem($name, $dropdown_action);
+ });
+
+ return $this;
+ }
+
+ /**
+ * Shortcut to click an element while holding CTRL key
+ */
+ public function ctrlClick($selector)
+ {
+ $this->driver->getKeyboard()->pressKey(WebDriverKeys::LEFT_CONTROL);
+ $this->element($selector)->click();
+ $this->driver->getKeyboard()->releaseKey(WebDriverKeys::LEFT_CONTROL);
+ }
+
+ /**
+ * Log in the test user
+ */
+ public function doLogin()
+ {
+ $this->type('_user', TESTS_USER);
+ $this->type('_pass', TESTS_PASS);
+ $this->click('button[type="submit"]');
+
+ // wait after successful login
+ $this->waitUntil('!rcmail.busy');
+
+ return $this;
+ }
+
+ /**
+ * Visit specified task/action with logon if needed
+ */
+ public function go($task = 'mail', $action = null, $login = true)
+ {
+ $this->visit("/?_task=$task&_action=$action");
+
+ // check if we have a valid session
+ if ($login) {
+ $app = new Components\App();
+ if ($app->getEnv($this, 'task') == 'login') {
+ $this->doLogin();
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Check if in Phone mode
+ */
+ public static function isPhone()
+ {
+ return getenv('TESTS_MODE') == 'phone';
+ }
+
+ /**
+ * Check if in Tablet mode
+ */
+ public static function isTablet()
+ {
+ return getenv('TESTS_MODE') == 'tablet';
+ }
+
+ /**
+ * Check if in Desktop mode
+ */
+ public static function isDesktop()
+ {
+ return !self::isPhone() && !self::isTablet();
+ }
+
+ /**
+ * Change state of the Elastic's pretty checkbox
+ */
+ public function setCheckboxState($selector, $state)
+ {
+ // Because you can't operate on the original checkbox directly
+ $this->ensurejQueryIsAvailable();
+
+ if ($state) {
+ $run = "if (!element.prev().is(':checked')) element.click()";
+ }
+ else {
+ $run = "if (element.prev().is(':checked')) element.click()";
+ }
+
+ $this->script(
+ "var element = jQuery('$selector')[0] || jQuery('input[name=$selector]')[0];"
+ ."element = jQuery(element).next('.custom-control-label'); $run;"
+ );
+
+ return $this;
+ }
+
+ /**
+ * Returns content of a downloaded file
+ */
+ public function readDownloadedFile($filename)
+ {
+ $filename = TESTS_DIR . "downloads/$filename";
+
+ // Give the browser a chance to finish download
+ if (!file_exists($filename)) {
+ sleep(2);
+ }
+
+ Assert::assertFileExists($filename);
+
+ return file_get_contents($filename);
+ }
+
+ /**
+ * Removes downloaded file
+ */
+ public function removeDownloadedFile($filename)
+ {
+ @unlink(TESTS_DIR . "downloads/$filename");
+
+ return $this;
+ }
+
+ /**
+ * Wait for UI (notice/confirmation/loading/error/warning) message
+ * and assert it's text
+ */
+ public function waitForMessage($type, $text)
+ {
+ $selector = '#messagestack > div.' . $type;
+
+ $this->waitFor($selector)->assertSeeIn($selector, $text);
+
+ return $this;
+ }
+
+ /**
+ * Execute code within body context.
+ * Useful to execute code that selects elements outside of a component context
+ */
+ public function withinBody($callback)
+ {
+ if ($this->resolver->prefix != 'body') {
+ $orig_prefix = $this->resolver->prefix;
+ $this->resolver->prefix = 'body';
+ }
+
+ call_user_func($callback, $this);
+
+ if (isset($orig_prefix)) {
+ $this->resolver->prefix = $orig_prefix;
+ }
+
+ return $this;
+ }
+}
diff --git a/tests/Browser/Components/App.php b/tests/Browser/Components/App.php
new file mode 100644
index 000000000..fbc2744de
--- /dev/null
+++ b/tests/Browser/Components/App.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Tests\Browser\Components;
+
+use Laravel\Dusk\Component as BaseComponent;
+use PHPUnit\Framework\Assert;
+use Tests\Browser\Browser;
+
+class App extends BaseComponent
+{
+ /**
+ * Get the root selector for the component.
+ *
+ * @return string
+ */
+ public function selector()
+ {
+ return '';
+ }
+
+ /**
+ * Assert that the browser page contains the component.
+ *
+ * @param Browser $browser
+ *
+ * @return void
+ */
+ public function assert($browser)
+ {
+ $result = $browser->script("return typeof(window.rcmail)");
+
+ Assert::assertEquals('object', $result[0]);
+ }
+
+ /**
+ * Get the element shortcuts for the component.
+ *
+ * @return array
+ */
+ public function elements()
+ {
+ return [
+ ];
+ }
+
+ /**
+ * Assert value of rcmail.env entry
+ */
+ public function assertEnv($browser, string $key, $expected)
+ {
+ Assert::assertEquals($expected, $this->getEnv($browser, $key));
+ }
+
+ /**
+ * Assert existence of defined gui_objects
+ */
+ public function assertObjects($browser, array $names)
+ {
+ $objects = $this->getObjects($browser);
+
+ foreach ($names as $object_name) {
+ Assert::assertContains($object_name, $objects);
+ }
+ }
+
+ /**
+ * Return rcmail.env entry
+ */
+ public function getEnv($browser, $key)
+ {
+ $result = $browser->script("return rcmail.env['$key']");
+ $result = $result[0];
+
+ return $result;
+ }
+
+ /**
+ * Return names of defined gui_objects
+ */
+ public function getObjects($browser)
+ {
+ $objects = $browser->script("var i, r = []; for (i in rcmail.gui_objects) r.push(i); return r");
+ $objects = $objects[0];
+
+ return (array) $objects;
+ }
+}
diff --git a/tests/Browser/Components/Taskmenu.php b/tests/Browser/Components/Taskmenu.php
new file mode 100644
index 000000000..499f8ca59
--- /dev/null
+++ b/tests/Browser/Components/Taskmenu.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Tests\Browser\Components;
+
+use Tests\Browser\Browser;
+use Laravel\Dusk\Component as BaseComponent;
+
+class Taskmenu extends BaseComponent
+{
+ protected $options = ['compose', 'mail', 'contacts', 'settings', 'about', 'logout'];
+
+ /**
+ * Get the root selector for the component.
+ *
+ * @return string
+ */
+ public function selector()
+ {
+ return '#taskmenu';
+ }
+
+ /**
+ * Assert that the browser page contains the component.
+ *
+ * @param Browser $browser
+ *
+ * @return void
+ */
+ public function assert($browser)
+ {
+ if ($browser->isPhone()) {
+ $browser->assertPresent($this->selector());
+ }
+ else {
+ $browser->assertVisible($this->selector());
+ }
+ }
+
+ /**
+ * Get the element shortcuts for the component.
+ *
+ * @return array
+ */
+ public function elements()
+ {
+ return [
+ ];
+ }
+
+ /**
+ * Assert Taskmenu state
+ */
+ public function assertMenuState(Browser $browser, $selected)
+ {
+ // On phone the menu is invisible, open it
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->click('.task-menu-button');
+ $browser->waitFor($this->selector());
+ });
+ }
+
+ foreach ($this->options as $option) {
+ $browser->assertVisible("a.{$option}:not(.disabled)" . ($selected == $option ? ".selected" : ":not(.selected)"));
+ }
+
+ // hide the menu back
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->click('.popover a.button.cancel');
+ $browser->waitUntilMissing($this->selector());
+ });
+ }
+ }
+
+ /**
+ * Select Taskmenu item
+ */
+ public function clickMenuItem(Browser $browser, $name)
+ {
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->click('.task-menu-button');
+ });
+ }
+
+ $browser->click("a.{$name}");
+
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->waitUntilMissing($this->selector());
+ });
+ }
+ }
+}
diff --git a/tests/Browser/Components/Toolbarmenu.php b/tests/Browser/Components/Toolbarmenu.php
new file mode 100644
index 000000000..39b0e3c80
--- /dev/null
+++ b/tests/Browser/Components/Toolbarmenu.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Tests\Browser\Components;
+
+use Tests\Browser\Browser;
+use Laravel\Dusk\Component as BaseComponent;
+
+class Toolbarmenu extends BaseComponent
+{
+ /**
+ * Get the root selector for the component.
+ *
+ * @return string
+ */
+ public function selector()
+ {
+ return '#toolbar-menu';
+ }
+
+ /**
+ * Assert that the browser page contains the component.
+ *
+ * @param Browser $browser
+ *
+ * @return void
+ */
+ public function assert($browser)
+ {
+ if ($browser->isPhone()) {
+ $browser->assertPresent($this->selector());
+ }
+ else {
+ $browser->assertVisible($this->selector());
+ }
+ }
+
+ /**
+ * Get the element shortcuts for the component.
+ *
+ * @return array
+ */
+ public function elements()
+ {
+ return [
+ ];
+ }
+
+ /**
+ * Assert toolbar menu state
+ */
+ public function assertMenuState($browser, $active, $disabled)
+ {
+ // On phone the menu is invisible, open it
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->click('.toolbar-menu-button');
+ $browser->waitFor($this->selector());
+ });
+ }
+
+ foreach ($active as $option) {
+ // Print action is disabled on phones
+ if ($option == 'print' && $browser->isPhone()) {
+ $browser->assertMissing("a.print");
+ }
+ else {
+ $browser->assertVisible("a.{$option}:not(.disabled)");
+ }
+ }
+
+ foreach ($disabled as $option) {
+ if ($option == 'print' && $browser->isPhone()) {
+ $browser->assertMissing("a.print");
+ }
+ else {
+ $browser->assertVisible("a.{$option}.disabled");
+ }
+ }
+
+ $this->closeMenu($browser);
+ }
+
+ /**
+ * Close toolbar menu (on phones)
+ */
+ public function closeMenu($browser)
+ {
+ // hide the menu back
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->script("window.UI.menu_hide('toolbar-menu')");
+ $browser->waitUntilMissing($this->selector());
+ // FIXME: For some reason sometimes .popover-overlay does not close,
+ // we have to remove it manually
+ $browser->script(
+ "Array.from(document.getElementsByClassName('popover-overlay')).forEach(function(elem) { elem.parentNode.removeChild(elem); })"
+ );
+ });
+ }
+ }
+
+ /**
+ * Select toolbar menu item
+ */
+ public function clickMenuItem($browser, $name, $dropdown_action = null)
+ {
+ if ($browser->isPhone()) {
+ $browser->withinBody(function ($browser) {
+ $browser->click('.toolbar-menu-button');
+ });
+ }
+
+ $selector = "a.{$name}" . ($dropdown_action ? " + a.dropdown" : '');
+
+ $browser->click($selector);
+
+ if ($dropdown_action) {
+ $popup_id = $browser->attribute($selector, 'data-popup');
+ $browser->withinBody(function ($browser) use ($popup_id, $dropdown_action) {
+ $browser->click("#{$popup_id} li a.{$dropdown_action}");
+ });
+ }
+
+ $this->closeMenu($browser);
+ }
+}
diff --git a/tests/Browser/Contacts/Contacts.php b/tests/Browser/Contacts/Contacts.php
index b706074da..74890d996 100644
--- a/tests/Browser/Contacts/Contacts.php
+++ b/tests/Browser/Contacts/Contacts.php
@@ -1,83 +1,82 @@
<?php
namespace Tests\Browser\Contacts;
-class Contacts extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Contacts extends \Tests\Browser\TestCase
{
protected function setUp()
{
parent::setUp();
\bootstrap::init_db();
}
/**
* Contacts UI Basics
*/
public function testContactsUI()
{
$this->browse(function ($browser) {
- $this->go('addressbook');
-
- // check task
- $this->assertEnvEquals('task', 'addressbook');
+ $browser->go('addressbook');
- $objects = $this->getObjects();
+ $browser->with(new App(), function ($browser) {
+ // check task
+ $browser->assertEnv('task', 'addressbook');
- // these objects should be there always
- $this->assertContains('qsearchbox', $objects);
- $this->assertContains('folderlist', $objects);
- $this->assertContains('contactslist', $objects);
- $this->assertContains('countdisplay', $objects);
+ // these objects should be there always
+ $browser->assertObjects(['qsearchbox', 'folderlist', 'contactslist', 'countdisplay']);
+ });
- if (!$this->isDesktop()) {
+ if (!$browser->isDesktop()) {
$browser->assertMissing('#directorylist');
$browser->click('a.back-sidebar-button');
}
$browser->assertSeeIn('#layout-sidebar .header', 'Groups');
// Groups/Addressbooks list
$browser->assertVisible('#directorylist');
$browser->assertSeeIn('#directorylist li:first-child', 'Personal Addresses');
$browser->assertMissing('#directorylist .treetoggle.expanded');
// Contacts list
- if (!$this->isDesktop()) {
+ if (!$browser->isDesktop()) {
$browser->assertMissing('#contacts-table');
$browser->click('#directorylist li:first-child');
$browser->waitFor('#contacts-table');
}
else {
$browser->assertVisible('#contacts-table');
}
// Contacts list menu
- if ($this->isPhone()) {
- $this->assertToolbarMenu(['select'], []);
+ if ($browser->isPhone()) {
+ $browser->assertToolbarMenu(['select'], []);
}
- else if ($this->isTablet()) {
+ else if ($browser->isTablet()) {
$browser->click('.toolbar-list-button');
$browser->assertVisible('#toolbar-list-menu a.select:not(.disabled)');
$browser->click();
}
else {
$browser->assertVisible('#toolbar-list-menu a.select:not(.disabled)');
}
// Toolbar menu
- $this->assertToolbarMenu(
+ $browser->assertToolbarMenu(
['create', 'search', 'import', 'export'], // active items
['print', 'delete', 'more'], // inactive items
);
// Contact frame
- if (!$this->isPhone()) {
+ if (!$browser->isPhone()) {
$browser->assertVisible('#contact-frame');
}
// Task menu
- $this->assertTaskMenu('contacts');
+ $browser->assertTaskMenu('contacts');
});
}
}
diff --git a/tests/Browser/Contacts/Export.php b/tests/Browser/Contacts/Export.php
index 5d757df2d..cfe3977de 100644
--- a/tests/Browser/Contacts/Export.php
+++ b/tests/Browser/Contacts/Export.php
@@ -1,50 +1,55 @@
<?php
namespace Tests\Browser\Contacts;
-class Export extends \Tests\Browser\DuskTestCase
+class Export extends \Tests\Browser\TestCase
{
/**
* Test exporting all contacts
*/
public function testExportAll()
{
\bootstrap::init_db();
$this->browse(function ($browser) {
- $this->go('addressbook');
+ $browser->go('addressbook');
- $this->clickToolbarMenuItem('export');
- });
+ $browser->clickToolbarMenuItem('export');
+
+ // Parse the downloaded vCard file
+ $vcard_content = $browser->readDownloadedFile('contacts.vcf');
+ $vcard = new \rcube_vcard();
+ $contacts = $vcard->import($vcard_content);
- // Parse the downloaded vCard file
- $vcard_content = $this->readDownloadedFile('contacts.vcf');
- $vcard = new \rcube_vcard();
- $contacts = $vcard->import($vcard_content);
+ $this->assertCount(2, $contacts);
+ $this->assertSame('John Doe', $contacts[0]->displayname);
+ $this->assertSame('Jane Stalone', $contacts[1]->displayname);
- $this->assertCount(2, $contacts);
- $this->assertSame('John Doe', $contacts[0]->displayname);
- $this->assertSame('Jane Stalone', $contacts[1]->displayname);
- $this->removeDownloadedFile('contacts.vcf');
+ $browser->removeDownloadedFile('contacts.vcf');
+ });
}
/**
* Test exporting selected contacts
*
* @depends testExportAll
*/
public function testExportSelected()
{
- $this->ctrlClick('#contacts-table tbody tr:first-child');
- $this->clickToolbarMenuItem('export', 'export.select');
+ $this->browse(function ($browser) {
+ $browser->ctrlClick('#contacts-table tbody tr:first-child');
- $vcard_content = $this->readDownloadedFile('contacts.vcf');
- $vcard = new \rcube_vcard();
- $contacts = $vcard->import($vcard_content);
+ $browser->clickToolbarMenuItem('export', 'export.select');
- // Parse the downloaded vCard file
- $this->assertCount(1, $contacts);
- $this->assertSame('John Doe', $contacts[0]->displayname);
- $this->removeDownloadedFile('contacts.vcf');
+ $vcard_content = $browser->readDownloadedFile('contacts.vcf');
+ $vcard = new \rcube_vcard();
+ $contacts = $vcard->import($vcard_content);
+
+ // Parse the downloaded vCard file
+ $this->assertCount(1, $contacts);
+ $this->assertSame('John Doe', $contacts[0]->displayname);
+
+ $browser->removeDownloadedFile('contacts.vcf');
+ });
}
}
diff --git a/tests/Browser/Contacts/Import.php b/tests/Browser/Contacts/Import.php
index fd04df0cd..2070114a1 100644
--- a/tests/Browser/Contacts/Import.php
+++ b/tests/Browser/Contacts/Import.php
@@ -1,105 +1,106 @@
<?php
namespace Tests\Browser\Contacts;
-class Import extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Import extends \Tests\Browser\TestCase
{
/**
* Test basic elements of contacts import UI
*/
public function testImportUI()
{
\bootstrap::init_db();
$this->browse(function ($browser) {
- $this->go('addressbook');
+ $browser->go('addressbook');
- $this->clickToolbarMenuItem('import');
+ $browser->clickToolbarMenuItem('import');
$browser->assertSeeIn('.ui-dialog-title', 'Import contacts');
$browser->assertVisible('.ui-dialog button.mainaction.import');
$browser->assertVisible('.ui-dialog button.cancel');
$browser->withinFrame('.ui-dialog iframe', function ($browser) {
// check task and action
- $this->assertEnvEquals('task', 'addressbook');
- $this->assertEnvEquals('action', 'import');
-
- $objects = $this->getObjects();
-
- // these objects should be there always
- $this->assertContains('importform', $objects);
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'addressbook');
+ $browser->assertEnv('action', 'import');
+ // these objects should be there always
+ $browser->assertObjects(['importform']);
+ });
$browser->assertSee('You can upload');
$browser->assertVisible('#rcmImportForm');
$browser->assertVisible('#rcmImportForm select');
$browser->assertVisible('#rcmImportForm .custom-switch');
// FIXME: selecting the file input directly does not work
$browser->assertVisible('#rcmImportForm .custom-file');
$browser->assertSelected('#rcmImportForm select', 0);
});
// Close the dialog
$browser->click('.ui-dialog button.cancel');
$browser->assertMissing('.ui-dialog');
});
}
/**
* Import contacts from a vCard file
*
* @depends testImportUI
*/
public function testImportProcess()
{
$this->browse(function ($browser) {
// Open the dialog again
- $this->clickToolbarMenuItem('import');
+ $browser->clickToolbarMenuItem('import');
$browser->assertSeeIn('.ui-dialog-title', 'Import contacts');
// Submit the form with no file attached
$browser->click('.ui-dialog button.mainaction');
$browser->waitForText('Attention');
$browser->assertSee('Please select a file');
$browser->driver->getKeyboard()->sendKeys(\Facebook\WebDriver\WebDriverKeys::ESCAPE);
$this->assertCount(1, $browser->elements('.ui-dialog'));
$browser->withinFrame('.ui-dialog iframe', function ($browser) {
$browser->attach('.custom-file input', TESTS_DIR . 'data/contacts.vcf');
});
$browser->click('.ui-dialog button.mainaction');
$browser->withinFrame('.ui-dialog iframe', function ($browser) {
$browser->waitForText('Successfully imported 2 contacts:');
});
// Close the dialog
$browser->click('.ui-dialog button.cancel');
// Expected existing contacts + imported
$browser->waitFor('#contacts-table tr');
$this->assertCount(4, $browser->elements('#contacts-table tbody tr'));
$browser->assertSeeIn('#rcmcountdisplay', '1 – 4 of 4');
});
}
/**
* Test imported contact
*
* @depends testImportProcess
*/
public function testImportResult()
{
$this->browse(function ($browser) {
// Open the dialog again
$browser->click('#contacts-table tr:last-child');
$browser->withinFrame('#contact-frame', function ($browser) {
$browser->waitFor('a.email'); // wait for iframe to load
$browser->assertSeeIn('.names', 'Sylvester Stalone');
$browser->assertSeeIn('a.email', 's.stalone@rambo.tv');
});
});
}
}
diff --git a/tests/Browser/DuskTestCase.php b/tests/Browser/DuskTestCase.php
deleted file mode 100644
index 15fe07b95..000000000
--- a/tests/Browser/DuskTestCase.php
+++ /dev/null
@@ -1,460 +0,0 @@
-<?php
-
-namespace Tests\Browser;
-
-use PHPUnit\Framework\TestCase;
-use Facebook\WebDriver\Chrome\ChromeOptions;
-use Facebook\WebDriver\Remote\RemoteWebDriver;
-use Facebook\WebDriver\Remote\DesiredCapabilities;
-use Laravel\Dusk\Browser;
-use Laravel\Dusk\Chrome\SupportsChrome;
-use Laravel\Dusk\Concerns\ProvidesBrowser;
-use Symfony\Component\Finder\Finder;
-use Symfony\Component\Process\Process;
-
-abstract class DuskTestCase extends TestCase
-{
- use ProvidesBrowser,
- SupportsChrome;
-
- protected $app;
- protected static $phpProcess;
-
-
- /**
- * Prepare for Dusk test execution.
- *
- * @beforeClass
- * @return void
- */
- public static function prepare()
- {
- static::startWebServer();
- static::startChromeDriver();
- }
-
- /**
- * Create the RemoteWebDriver instance.
- *
- * @return \Facebook\WebDriver\Remote\RemoteWebDriver
- */
- protected function driver()
- {
- $options = (new ChromeOptions())->addArguments([
- '--lang=en_US',
- '--disable-gpu',
- '--headless',
- ]);
-
- // For file download handling
- $prefs = [
- 'profile.default_content_settings.popups' => 0,
- 'download.default_directory' => TESTS_DIR . 'downloads',
- ];
-
- $options->setExperimentalOption('prefs', $prefs);
-
- if (getenv('TESTS_MODE') == 'phone') {
- // Fake User-Agent string for mobile mode
- $ua = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36';
- $options->setExperimentalOption('mobileEmulation', ['userAgent' => $ua]);
- $options->addArguments(['--window-size=375,667']);
- }
- else if (getenv('TESTS_MODE') == 'tablet') {
- // Fake User-Agent string for mobile mode
- $ua = 'Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36';
- $options->setExperimentalOption('mobileEmulation', ['userAgent' => $ua]);
- $options->addArguments(['--window-size=1024,768']);
- }
- else {
- $options->addArguments(['--window-size=1280,720']);
- }
-
- // Make sure downloads dir exists and is empty
- if (!file_exists(TESTS_DIR . 'downloads')) {
- mkdir(TESTS_DIR . 'downloads', 0777, true);
- }
- else {
- foreach (glob(TESTS_DIR . 'downloads/*') as $file) {
- @unlink($file);
- }
- }
-
- return RemoteWebDriver::create(
- 'http://localhost:9515',
- DesiredCapabilities::chrome()->setCapability(
- ChromeOptions::CAPABILITY,
- $options
- )
- );
- }
-
- /**
- * Set up the test run
- *
- * @return void
- */
- protected function setUp()
- {
- parent::setUp();
-
- $this->app = \rcmail::get_instance();
-
- Browser::$baseUrl = 'http://localhost:8000';
- Browser::$storeScreenshotsAt = TESTS_DIR . 'screenshots';
- Browser::$storeConsoleLogAt = TESTS_DIR . 'console';
-
- // This folder must exist in case Browser will try to write logs to it
- if (!is_dir(Browser::$storeConsoleLogAt)) {
- mkdir(Browser::$storeConsoleLogAt, 0777, true);
- }
-
- // Purge screenshots from the last test run
- $pattern = sprintf('failure-%s_%s-*',
- str_replace("\\", '_', get_class($this)),
- $this->getName(false)
- );
-
- try {
- $files = Finder::create()->files()->in(Browser::$storeScreenshotsAt)->name($pattern);
- foreach ($files as $file) {
- @unlink($file->getRealPath());
- }
- }
- catch (\Symfony\Component\Finder\Exception\DirectoryNotFoundException $e) {
- // ignore missing screenshots directory
- }
-
- // Purge console logs from the last test run
- $pattern = sprintf('%s_%s-*',
- str_replace("\\", '_', get_class($this)),
- $this->getName(false)
- );
-
- try {
- $files = Finder::create()->files()->in(Browser::$storeConsoleLogAt)->name($pattern);
- foreach ($files as $file) {
- @unlink($file->getRealPath());
- }
- }
- catch (\Symfony\Component\Finder\Exception\DirectoryNotFoundException $e) {
- // ignore missing screenshots directory
- }
- }
-
- /**
- * Check if in Phone mode
- */
- public static function isPhone()
- {
- return getenv('TESTS_MODE') == 'phone';
- }
-
- /**
- * Check if in Tablet mode
- */
- public static function isTablet()
- {
- return getenv('TESTS_MODE') == 'tablet';
- }
-
- /**
- * Check if in Desktop mode
- */
- public static function isDesktop()
- {
- return !self::isPhone() && !self::isTablet();
- }
-
- /**
- * Assert specified rcmail.env value
- */
- protected function assertEnvEquals($key, $expected)
- {
- $this->assertEquals($expected, $this->getEnv($key));
- }
-
- /**
- * Assert specified checkbox state
- */
- protected function assertCheckboxState($selector, $state)
- {
- $this->browse(function (Browser $browser) use ($selector, $state) {
- if ($state) {
- $browser->assertChecked($selector);
- }
- else {
- $browser->assertNotChecked($selector);
- }
- });
- }
-
- /**
- * Assert Task menu state
- */
- protected function assertTaskMenu($selected)
- {
- $this->browse(function (Browser $browser) use ($selected) {
- // On phone the menu is invisible, open it
- if ($this->isPhone()) {
- $browser->click('.task-menu-button');
- $browser->waitFor('#taskmenu');
- }
-
- $browser->with('#taskmenu', function(Browser $browser) use ($selected) {
- $options = ['compose', 'mail', 'contacts', 'settings', 'about', 'logout'];
- foreach ($options as $option) {
- $browser->assertVisible("a.{$option}:not(.disabled)" . ($selected == $option ? ".selected" : ":not(.selected)"));
- }
- });
-
- // hide the menu back
- if ($this->isPhone()) {
- $browser->click('.popover a.button.cancel');
- $browser->waitUntilMissing('#taskmenu');
- }
- });
- }
-
- /**
- * Assert toolbar menu state
- */
- protected function assertToolbarMenu($active, $disabled)
- {
- $this->browse(function (Browser $browser) use ($active, $disabled) {
- // On phone the menu is invisible, open it
- if ($this->isPhone()) {
- $browser->click('.toolbar-menu-button');
- $browser->waitFor('#toolbar-menu');
- }
-
- $browser->with('#toolbar-menu', function(Browser $browser) use ($active, $disabled) {
- foreach ($active as $option) {
- // Print action is disabled on phones
- if ($option == 'print' && $this->isPhone()) {
- $browser->assertMissing("a.print");
- }
- else {
- $browser->assertVisible("a.{$option}:not(.disabled)");
- }
- }
- foreach ($disabled as $option) {
- if ($option == 'print' && $this->isPhone()) {
- $browser->assertMissing("a.print");
- }
- else {
- $browser->assertVisible("a.{$option}.disabled");
- }
- }
- });
-
- $this->closeToolbarMenu();
- });
- }
-
- /**
- * Close toolbar menu (on phones)
- */
- protected function closeToolbarMenu()
- {
- // hide the menu back
- if ($this->isPhone()) {
- $this->browse(function (Browser $browser) {
- $browser->script("window.UI.menu_hide('toolbar-menu')");
- $browser->waitUntilMissing('#toolbar-menu');
- // FIXME: For some reason sometimes .popover-overlay does not close,
- // we have to remove it manually
- $browser->script(
- "Array.from(document.getElementsByClassName('popover-overlay')).forEach(function(elem) { elem.parentNode.removeChild(elem); })"
- );
- });
- }
- }
-
- /**
- * Select taskmenu item
- */
- protected function clickTaskMenuItem($name)
- {
- $this->browse(function (Browser $browser) use ($name) {
- if ($this->isPhone()) {
- $browser->click('.task-menu-button');
- }
-
- $browser->click("#taskmenu a.{$name}");
-
- if ($this->isPhone()) {
- $browser->waitUntilMissing('#taskmenu');
- }
- });
- }
-
- /**
- * Select toolbar menu item
- */
- protected function clickToolbarMenuItem($name, $dropdown_action = null)
- {
- $this->browse(function (Browser $browser) use ($name, $dropdown_action) {
- if ($this->isPhone()) {
- $browser->click('.toolbar-menu-button');
- }
-
- $selector = "#toolbar-menu a.{$name}" . ($dropdown_action ? " + a.dropdown" : '');
-
- $browser->click($selector);
-
- if ($dropdown_action) {
- $popup_id = $browser->attribute($selector, 'data-popup');
- $browser->click("#{$popup_id} li a.{$dropdown_action}");
- }
-
- if ($this->isPhone()) {
- $this->closeToolbarMenu();
- }
- });
- }
-
- protected function ctrlClick($selector)
- {
- $this->browse(function (Browser $browser) use ($selector) {
- $browser->driver->getKeyboard()->pressKey(\Facebook\WebDriver\WebDriverKeys::LEFT_CONTROL);
- $browser->element('#contacts-table tbody tr:first-child')->click();
- $browser->driver->getKeyboard()->releaseKey(\Facebook\WebDriver\WebDriverKeys::LEFT_CONTROL);
- });
- }
-
- /**
- * Get content of rcmail.env entry
- */
- protected function getEnv($key)
- {
- $this->browse(function (Browser $browser) use ($key, &$result) {
- $result = $browser->script("return rcmail.env['$key']");
- $result = $result[0];
- });
-
- return $result;
- }
-
- /**
- * Return names of defined gui_objects
- */
- protected function getObjects()
- {
- $this->browse(function (Browser $browser) use (&$objects) {
- $objects = $browser->script("var i, r = []; for (i in rcmail.gui_objects) r.push(i); return r");
- $objects = $objects[0];
- });
-
- return (array) $objects;
- }
-
- /**
- * Log in the test user
- */
- protected function doLogin()
- {
- $this->browse(function (Browser $browser) {
- $browser->type('_user', TESTS_USER);
- $browser->type('_pass', TESTS_PASS);
- $browser->click('button[type="submit"]');
-
- // wait after successful login
- //$browser->waitForReload();
- $browser->waitUntil('!rcmail.busy');
- });
- }
-
- /**
- * Visit specified task/action with logon if needed
- */
- protected function go($task = 'mail', $action = null, $login = true)
- {
- $this->browse(function (Browser $browser) use ($task, $action, $login) {
- $browser->visit("/?_task=$task&_action=$action");
-
- // check if we have a valid session
- if ($login && $this->getEnv('task') == 'login') {
- $this->doLogin();
- }
- });
- }
-
- /**
- * Change state of the Elastic's pretty checkbox
- */
- protected function setCheckboxState($selector, $state)
- {
- // Because you can't operate on the original checkbox directly
- $this->browse(function (Browser $browser) use ($selector, $state) {
- $browser->ensurejQueryIsAvailable();
-
- if ($state) {
- $run = "if (!element.prev().is(':checked')) element.click()";
- }
- else {
- $run = "if (element.prev().is(':checked')) element.click()";
- }
-
- $browser->script(
- "var element = jQuery('$selector')[0] || jQuery('input[name=$selector]')[0];"
- ."element = jQuery(element).next('.custom-control-label'); $run;"
- );
- });
- }
-
- /**
- * Wait for UI (notice/confirmation/loading/error/warning) message
- * and assert it's text
- */
- protected function waitForMessage($type, $text)
- {
- $selector = '#messagestack > div.' . $type;
-
- $this->browse(function ($browser) use ($selector, $text) {
- $browser->waitFor($selector)->assertSeeIn($selector, $text);
- });
- }
-
- /**
- * Returns content of a downloaded file
- */
- protected function readDownloadedFile($filename)
- {
- $filename = TESTS_DIR . "downloads/$filename";
- // Give the browser a chance to finish download
- if (!file_exists($filename)) {
- sleep(2);
- }
-
- $this->assertFileExists($filename);
-
- return file_get_contents($filename);
- }
-
- /**
- * Removes downloaded file
- */
- protected function removeDownloadedFile($filename)
- {
- @unlink(TESTS_DIR . "downloads/$filename");
- }
-
- /**
- * Starts PHP server.
- */
- protected static function startWebServer()
- {
- $path = realpath(__DIR__ . '/../../public_html');
- $cmd = ['php', '-S', 'localhost:8000'];
- $env = [];
-
- static::$phpProcess = new Process($cmd, null, $env);
- static::$phpProcess->setWorkingDirectory($path);
- static::$phpProcess->start();
-
- static::afterClass(function () {
- static::$phpProcess->stop();
- });
- }
-}
diff --git a/tests/Browser/Login.php b/tests/Browser/Logon/Login.php
similarity index 77%
rename from tests/Browser/Login.php
rename to tests/Browser/Logon/Login.php
index ea2321f9d..306939569 100644
--- a/tests/Browser/Login.php
+++ b/tests/Browser/Logon/Login.php
@@ -1,47 +1,53 @@
<?php
namespace Tests\Browser;
-class Login extends DuskTestCase
+use Tests\Browser\Components\App;
+
+class Login extends TestCase
{
protected function setUp()
{
parent::setUp();
\bootstrap::init_db();
\bootstrap::init_imap();
}
public function testLogin()
{
// First test, we're already on the logon page
$this->browse(function ($browser) {
$browser->visit('/');
$browser->assertTitleContains($this->app->config->get('product_name'));
// task should be set to 'login'
- $this->assertEnvEquals('task', 'login');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'login');
+ });
// Logon form
$browser->assertVisible('#logo');
$browser->assertVisible('#login-form');
$browser->assertVisible('#rcmloginuser');
$browser->assertVisible('#rcmloginpwd');
$browser->assertVisible('#rcmloginsubmit');
$browser->assertSee($this->app->config->get('product_name'));
// Support link
if ($url = $this->app->config->get('support_url')) {
$browser->assertSeeLink('Get support');
$this->assertStringStartsWith($url, $browser->attribute('.support-link', 'href'));
}
// test valid login
- $this->go('mail');
+ $browser->go('mail');
// task should be set to 'mail' now
- $this->assertEnvEquals('task', 'mail');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'mail');
+ });
});
}
}
diff --git a/tests/Browser/Logout.php b/tests/Browser/Logon/Logout.php
similarity index 60%
rename from tests/Browser/Logout.php
rename to tests/Browser/Logon/Logout.php
index c601ca887..96105d61c 100644
--- a/tests/Browser/Logout.php
+++ b/tests/Browser/Logon/Logout.php
@@ -1,24 +1,28 @@
<?php
namespace Tests\Browser;
-class Logout extends DuskTestCase
+use Tests\Browser\Components\App;
+
+class Logout extends TestCase
{
public function testLogout()
{
$this->browse(function ($browser) {
- $this->go('settings');
+ $browser->go('settings');
// click the Logout button in taskmenu
- $this->clickTaskMenuItem('logout');
+ $browser->clickTaskMenuItem('logout');
// task should be set to 'login'
- $this->assertEnvEquals('task', 'login');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'login');
+ });
// form should exist
$browser->assertVisible('input[name="_user"]');
$browser->assertVisible('input[name="_pass"]');
$browser->assertMissing('#taskmenu');
});
}
}
diff --git a/tests/Browser/Mail/Compose.php b/tests/Browser/Mail/Compose.php
index 8b536aa9e..2d8054cce 100644
--- a/tests/Browser/Mail/Compose.php
+++ b/tests/Browser/Mail/Compose.php
@@ -1,64 +1,68 @@
<?php
namespace Tests\Browser\Mail;
-class Compose extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Compose extends \Tests\Browser\TestCase
{
public function testCompose()
{
$this->browse(function ($browser) {
- $this->go('mail');
+ $browser->go('mail');
- $this->clickTaskMenuItem('compose');
+ $browser->clickTaskMenuItem('compose');
// check task and action
- $this->assertEnvEquals('task', 'mail');
- $this->assertEnvEquals('action', 'compose');
-
- $objects = $this->getObjects();
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'mail');
+ $browser->assertEnv('action', 'compose');
- // 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);
+ // these objects should be there always
+ $browser->assertObjects([
+ 'qsearchbox',
+ 'addressbookslist',
+ 'contactslist',
+ 'messageform',
+ 'attachmentlist',
+ 'filedrop',
+ 'uploadform'
+ ]);
+ });
// Toolbar menu
- $this->assertToolbarMenu(
+ $browser->assertToolbarMenu(
['save.draft', 'responses', 'spellcheck'], // active items
['signature'], // inactive items
);
- if ($this->isPhone()) {
- $this->assertToolbarMenu(['options'], []);
+ if ($browser->isPhone()) {
+ $browser->assertToolbarMenu(['options'], []);
}
else {
- $this->assertToolbarMenu(['attach'], []);
+ $browser->assertToolbarMenu(['attach'], []);
$browser->assertMissing('#toolbar-menu a.options');
}
// Task menu
- $this->assertTaskMenu('compose');
+ $browser->assertTaskMenu('compose');
// Header inputs
$browser->assertVisible('#_from');
$browser->assertVisible('#compose-subject');
$browser->assertInputValue('#compose-subject', '');
// Mail body input
$browser->assertVisible('#composebodycontainer.html-editor');
$browser->assertVisible('#composebodycontainer > textarea');
- if ($this->isPhone()) {
- $this->clickToolbarMenuItem('options');
+ if ($browser->isPhone()) {
+ $browser->clickToolbarMenuItem('options');
}
// Compose options
$browser->assertSeeIn('#layout-sidebar .header', 'Options and attachments');
$browser->assertVisible('#compose-attachments');
});
}
}
diff --git a/tests/Browser/Mail/Getunread.php b/tests/Browser/Mail/Getunread.php
index 2fa714bc8..28f6cc4ed 100644
--- a/tests/Browser/Mail/Getunread.php
+++ b/tests/Browser/Mail/Getunread.php
@@ -1,43 +1,43 @@
<?php
namespace Tests\Browser\Mail;
-class Getunread extends \Tests\Browser\DuskTestCase
+class Getunread extends \Tests\Browser\TestCase
{
protected $msgcount = 0;
protected function setUp()
{
parent::setUp();
\bootstrap::init_imap();
\bootstrap::purge_mailbox('INBOX');
// import email messages
foreach (glob(TESTS_DIR . 'data/mail/list_*.eml') as $f) {
\bootstrap::import_message($f, 'INBOX');
$this->msgcount++;
}
}
public function testGetunread()
{
$this->browse(function ($browser) {
- $this->go('mail');
+ $browser->go('mail');
$browser->waitFor('#messagelist tbody tr');
// Messages list state
$this->assertCount($this->msgcount, $browser->elements('#messagelist tbody tr.unread'));
- if (!$this->isDesktop()) {
+ if (!$browser->isDesktop()) {
$browser->click('.back-sidebar-button');
}
// Folders list state
$browser->assertVisible('.folderlist li.inbox.unread');
$this->assertEquals(strval($this->msgcount), $browser->text('.folderlist li.inbox span.unreadcount'));
});
}
}
diff --git a/tests/Browser/Mail/List.php b/tests/Browser/Mail/List.php
index 183eb342f..f56985d97 100644
--- a/tests/Browser/Mail/List.php
+++ b/tests/Browser/Mail/List.php
@@ -1,129 +1,129 @@
<?php
namespace Tests\Browser\Mail;
-class MailList extends \Tests\Browser\DuskTestCase
+class MailList extends \Tests\Browser\TestCase
{
protected function setUp()
{
parent::setUp();
\bootstrap::init_imap();
\bootstrap::purge_mailbox('INBOX');
// import email messages
foreach (glob(TESTS_DIR . 'data/mail/list_00.eml') as $f) {
\bootstrap::import_message($f, 'INBOX');
}
}
public function testList()
{
$this->browse(function ($browser) {
- $this->go('mail');
+ $browser->go('mail');
$this->assertCount(1, $browser->elements('#messagelist tbody tr'));
// check message list
$browser->assertVisible('#messagelist tbody tr:first-child.unread');
$this->assertEquals('Lines', $browser->text('#messagelist tbody tr:first-child span.subject'));
// Note: This element icon has width=0, use assertPresent() not assertVisible()
$browser->assertPresent('#messagelist tbody tr:first-child span.msgicon.unread');
// List toolbar menu
$browser->assertVisible('#layout-list .header a.toolbar-button.refresh:not(.disabled)');
- if ($this->isDesktop()) {
+ if ($browser->isDesktop()) {
$browser->with('#toolbar-list-menu', function ($browser) {
$browser->assertVisible('a.select:not(.disabled)');
$browser->assertVisible('a.options:not(.disabled)');
$imap = \bootstrap::get_storage();
if ($imap->get_threading()) {
$browser->assertVisible('a.threads:not(.disabled)');
}
else {
$browser->assertMissing('a.threads');
}
});
}
- else if ($this->isTablet()) {
+ else if ($browser->isTablet()) {
$browser->click('.toolbar-list-button');
$browser->with('#toolbar-list-menu', function ($browser) {
$browser->assertVisible('a.select:not(.disabled)');
$browser->assertVisible('a.options:not(.disabled)');
$imap = \bootstrap::get_storage();
if ($imap->get_threading()) {
$browser->assertVisible('a.threads:not(.disabled)');
}
else {
$browser->assertMissing('a.threads');
}
});
$browser->click(); // hide the popup menu
}
else { // phone
// On phones list options are in the toolbar menu
$browser->click('.toolbar-menu-button');
$browser->with('#toolbar-menu', function ($browser) {
$browser->assertVisible('a.select:not(.disabled)');
$browser->assertVisible('a.options:not(.disabled)');
$imap = \bootstrap::get_storage();
if ($imap->get_threading()) {
$browser->assertVisible('a.threads:not(.disabled)');
}
else {
$browser->assertMissing('a.threads');
}
});
- $this->closeToolbarMenu();
+ $browser->closeToolbarMenu();
}
});
}
/**
* @depends testList
*/
public function testListSelection()
{
$this->browse(function ($browser) {
- if ($this->isPhone()) {
+ if ($browser->isPhone()) {
$browser->click('.toolbar-menu-button');
$browser->click('#toolbar-menu a.select');
}
- else if ($this->isTablet()) {
+ else if ($browser->isTablet()) {
$browser->click('.toolbar-list-button');
$browser->click('#toolbar-list-menu a.select');
}
else {
$browser->click('#toolbar-list-menu a.select');
$browser->assertFocused('#toolbar-list-menu a.select');
}
// Popup menu content
$browser->with('#listselect-menu', function($browser) {
$browser->assertVisible('a.selection:not(.disabled)');
$browser->assertVisible('a.select.all:not(.disabled)');
$browser->assertVisible('a.select.page:not(.disabled)');
$browser->assertVisible('a.select.unread:not(.disabled)');
$browser->assertVisible('a.select.flagged:not(.disabled)');
$browser->assertVisible('a.select.invert:not(.disabled)');
$browser->assertVisible('a.select.none:not(.disabled)');
});
// Close the menu(s) by clicking the page body
$browser->click();
$browser->waitUntilMissing('#listselect-menu');
// TODO: Test selection actions
});
}
}
diff --git a/tests/Browser/Mail/Mail.php b/tests/Browser/Mail/Mail.php
index 64cff0f96..8c7806ca6 100644
--- a/tests/Browser/Mail/Mail.php
+++ b/tests/Browser/Mail/Mail.php
@@ -1,53 +1,56 @@
<?php
namespace Tests\Browser\Mail;
-class Mail extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Mail extends \Tests\Browser\TestCase
{
public function testMailUI()
{
$this->browse(function ($browser) {
- $this->go('mail');
+ $browser->go('mail');
// check task
- $this->assertEnvEquals('task', 'mail');
-
- $objects = $this->getObjects();
-
- // 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);
-
- if (!$this->isDesktop()) {
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'mail');
+ // these objects should be there always
+ $browser->assertObjects([
+ 'qsearchbox',
+ 'mailboxlist',
+ 'messagelist',
+ 'quotadisplay',
+ 'search_filter',
+ 'countdisplay',
+ ]);
+ });
+
+ if (!$browser->isDesktop()) {
$browser->click('.back-sidebar-button');
}
$browser->assertSeeIn('#layout-sidebar .header', TESTS_USER);
// Folders list
$browser->assertVisible('#mailboxlist li.mailbox.inbox.selected');
- if (!$this->isDesktop()) {
+ if (!$browser->isDesktop()) {
$browser->click('.back-list-button');
}
// Mail preview frame
- if (!$this->isPhone()) {
+ if (!$browser->isPhone()) {
$browser->assertVisible('#messagecontframe');
}
// Toolbar menu
- $this->assertToolbarMenu(
+ $browser->assertToolbarMenu(
['more'], // active items
['reply', 'reply-all', 'forward', 'delete', 'markmessage'], // inactive items
);
// Task menu
- $this->assertTaskMenu('mail');
+ $browser->assertTaskMenu('mail');
});
}
}
diff --git a/tests/Browser/Settings/About.php b/tests/Browser/Settings/About.php
index 5d065653d..667c5078d 100644
--- a/tests/Browser/Settings/About.php
+++ b/tests/Browser/Settings/About.php
@@ -1,27 +1,31 @@
<?php
namespace Tests\Browser\Settings;
-class About extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class About extends \Tests\Browser\TestCase
{
public function testAbout()
{
$this->browse(function ($browser) {
- $this->go('settings');
+ $browser->go('settings');
- $this->clickTaskMenuItem('about');
+ $browser->clickTaskMenuItem('about');
$browser->assertSeeIn('.ui-dialog-title', 'About');
$browser->assertVisible('.ui-dialog #aboutframe');
$browser->withinFrame('#aboutframe', function ($browser) {
// check task and action
- $this->assertEnvEquals('task', 'settings');
- $this->assertEnvEquals('action', 'about');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'settings');
+ $browser->assertEnv('action', 'about');
+ });
$browser->assertSee($this->app->config->get('product_name'));
$browser->assertVisible('#pluginlist');
});
});
}
}
diff --git a/tests/Browser/Settings/Folders.php b/tests/Browser/Settings/Folders.php
index ca3ccff1a..73a2fc812 100644
--- a/tests/Browser/Settings/Folders.php
+++ b/tests/Browser/Settings/Folders.php
@@ -1,33 +1,34 @@
<?php
namespace Tests\Browser\Settings;
-class Folders extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Folders extends \Tests\Browser\TestCase
{
public function testFolders()
{
$this->browse(function ($browser) {
- $this->go('settings', 'folders');
+ $browser->go('settings', 'folders');
// task should be set to 'settings' and action to 'folders'
- $this->assertEnvEquals('task', 'settings');
- $this->assertEnvEquals('action', 'folders');
-
- $objects = $this->getObjects();
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'settings');
+ $browser->assertEnv('action', 'folders');
- // these objects should be there always
- $this->assertContains('quotadisplay', $objects);
- $this->assertContains('subscriptionlist', $objects);
+ // these objects should be there always
+ $browser->assertObjects(['quotadisplay', 'subscriptionlist']);
+ });
- if ($this->isDesktop()) {
+ if ($browser->isDesktop()) {
$browser->assertVisible('#settings-menu li.folders.selected');
}
// Folders list
$browser->assertVisible('#subscription-table li.mailbox.inbox');
// Toolbar menu
- $this->assertToolbarMenu(['create'], ['delete', 'purge']);
+ $browser->assertToolbarMenu(['create'], ['delete', 'purge']);
});
}
}
diff --git a/tests/Browser/Settings/Identities.php b/tests/Browser/Settings/Identities.php
index 62c95e80c..a65f5f25a 100644
--- a/tests/Browser/Settings/Identities.php
+++ b/tests/Browser/Settings/Identities.php
@@ -1,33 +1,35 @@
<?php
namespace Tests\Browser\Settings;
-class Identities extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Identities extends \Tests\Browser\TestCase
{
public function testIdentities()
{
$this->browse(function ($browser) {
- $this->go('settings', 'identities');
+ $browser->go('settings', 'identities');
// check task and action
- $this->assertEnvEquals('task', 'settings');
- $this->assertEnvEquals('action', 'identities');
-
- $objects = $this->getObjects();
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'settings');
+ $browser->assertEnv('action', 'identities');
- // these objects should be there always
- $this->assertContains('identitieslist', $objects);
+ // these objects should be there always
+ $browser->assertObjects(['identitieslist']);
+ });
- if ($this->isDesktop()) {
+ if ($browser->isDesktop()) {
$browser->assertVisible('#settings-menu li.identities.selected');
}
// Identities list
$browser->assertVisible('#identities-table tr:first-child.focused');
$browser->assertSeeIn('#identities-table tr:first-child td.mail', TESTS_USER);
// Toolbar menu
- $this->assertToolbarMenu(['create'], ['delete']);
+ $browser->assertToolbarMenu(['create'], ['delete']);
});
}
}
diff --git a/tests/Browser/Settings/Preferences.php b/tests/Browser/Settings/Preferences.php
index bb9a1fc48..692252500 100644
--- a/tests/Browser/Settings/Preferences.php
+++ b/tests/Browser/Settings/Preferences.php
@@ -1,37 +1,39 @@
<?php
namespace Tests\Browser\Settings;
-class Preferences extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Preferences extends \Tests\Browser\TestCase
{
public function testPreferences()
{
$this->browse(function ($browser) {
- $this->go('settings');
-
- $objects = $this->getObjects();
+ $browser->go('settings');
- $this->assertContains('sectionslist', $objects);
+ $browser->with(new App(), function ($browser) {
+ $browser->assertObjects(['sectionslist']);
+ });
$browser->assertVisible('#settings-menu li.preferences.selected');
// On phone/tablet #sections-table is initially hidden
- if ($this->isPhone() || $this->isTablet()) {
+ if (!$browser->isDesktop()) {
$browser->assertMissing('#sections-table');
$browser->click('#settings-menu li.preferences');
$browser->waitFor('#sections-table');
}
// Preferences actions
$browser->with('#sections-table', function($browser) {
$browser->assertSeeIn('tr.general', 'User Interface');
$browser->assertSeeIn('tr.mailbox', 'Mailbox View');
$browser->assertSeeIn('tr.mailview', 'Displaying Messages');
$browser->assertSeeIn('tr.compose', 'Composing Messages');
$browser->assertSeeIn('tr.addressbook', 'Contacts');
$browser->assertSeeIn('tr.folders', 'Special Folders');
$browser->assertSeeIn('tr.server', 'Server Settings');
});
});
}
}
diff --git a/tests/Browser/Settings/Preferences/General.php b/tests/Browser/Settings/Preferences/General.php
index 24b4f77b9..378766322 100644
--- a/tests/Browser/Settings/Preferences/General.php
+++ b/tests/Browser/Settings/Preferences/General.php
@@ -1,163 +1,167 @@
<?php
namespace Tests\Browser\Settings\Preferences;
-class General extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class General extends \Tests\Browser\TestCase
{
protected function tearDown()
{
parent::tearDown();
// Reset user preferences back to defaults
$db = $this->app->get_dbh();
$db->query("UPDATE users SET preferences = '' WHERE username = ?", TESTS_USER);
}
public function testGeneral()
{
$this->browse(function ($browser) {
- $this->go('settings');
+ $browser->go('settings');
- if ($this->isPhone() || $this->isTablet()) {
+ if (!$browser->isDesktop()) {
$browser->click('#settings-menu li.preferences');
$browser->waitFor('#sections-table');
}
$browser->assertVisible('#sections-table tr.general.focused');
$browser->click('#sections-table tr.general');
- if ($this->isPhone()) {
+ if ($browser->isPhone()) {
$browser->waitFor('#layout-content .footer a.button.submit:not(.disabled)');
$browser->assertVisible('#layout-content .footer a.button.prev.disabled');
$browser->assertVisible('#layout-content .footer a.button.next:not(.disabled)');
}
$browser->withinFrame('#preferences-frame', function ($browser) {
- if (!$this->isPhone()) {
+ if (!$browser->isPhone()) {
$browser->waitFor('.formbuttons button.submit');
}
// check task and action
- $this->assertEnvEquals('task', 'settings');
- $this->assertEnvEquals('action', 'edit-prefs');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'settings');
+ $browser->assertEnv('action', 'edit-prefs');
+ });
// Main Options fieldset
$browser->with('form.propform fieldset.main', function ($browser) {
$browser->assertSeeIn('legend', 'Main Options');
$browser->assertSeeIn('label[for=rcmfd_lang]', 'Language');
$browser->assertVisible('select[name=_language]');
$browser->assertSelected('select[name=_language]', 'en_US');
$browser->assertSeeIn('label[for=rcmfd_timezone]', 'Time zone');
$browser->assertVisible('select[name=_timezone]');
// we don't know what timezone has been autodetected
// $browser->assertSelected('select[name=_timezone]', 'auto');
$browser->assertSeeIn('label[for=rcmfd_time_format]', 'Time format');
$browser->assertVisible('select[name=_time_format]');
$browser->assertSelected('select[name=_time_format]', $this->app->config->get('time_format'));
$browser->assertSeeIn('label[for=rcmfd_date_format]', 'Date format');
$browser->assertVisible('select[name=_date_format]');
$browser->assertSelected('select[name=_date_format]', $this->app->config->get('date_format'));
$browser->assertSeeIn('label[for=rcmfd_prettydate]', 'Pretty dates');
- $this->assertCheckboxState('_pretty_date', $this->app->config->get('prettydate'));
+ $browser->assertCheckboxState('_pretty_date', $this->app->config->get('prettydate'));
$browser->assertSeeIn('label[for=rcmfd_displaynext]', 'Display next');
- $this->assertCheckboxState('_display_next', $this->app->config->get('display_next'));
+ $browser->assertCheckboxState('_display_next', $this->app->config->get('display_next'));
$browser->assertSeeIn('label[for=rcmfd_refresh_interval]', 'Refresh');
$browser->assertVisible('select[name=_refresh_interval]');
$browser->assertSelected('select[name=_refresh_interval]', $this->app->config->get('refresh_interval')/60);
});
// Interface Skin fieldset
$browser->with('form.propform fieldset.skin', function ($browser) {
$browser->assertSeeIn('legend', 'Interface skin');
// TODO
});
// Browser Options fieldset
$browser->with('form.propform fieldset.browser', function ($browser) {
$browser->assertSeeIn('legend', 'Browser Options');
$browser->assertSeeIn('label[for=rcmfd_standard_windows]', 'Handle popups');
- $this->assertCheckboxState('_standard_windows', $this->app->config->get('standard_windows'));
+ $browser->assertCheckboxState('_standard_windows', $this->app->config->get('standard_windows'));
});
});
});
}
/**
* Test (all) User Interface preferences change
*
* @depends testGeneral
*/
public function testPreferencesChange()
{
// Values we're changing to
// TODO: Skin or language change causes page reload, we should test this separately
$this->settings = [
'timezone' => 'Pacific/Midway',
'time_format' => 'h:i A',
'date_format' => 'd-m-Y',
'refresh_interval' => 60,
'pretty_date' => !boolval($this->app->config->get('prettydate')),
'display_next' => !boolval($this->app->config->get('display_next')),
'standard_windows' => !boolval($this->app->config->get('standard_windows')),
];
$this->browse(function ($browser) {
// Update preferences
$browser->withinFrame('#preferences-frame', function ($browser) {
$browser->select('_timezone', $this->settings['timezone']);
$browser->select('_time_format', $this->settings['time_format']);
$browser->select('_date_format', $this->settings['date_format']);
$browser->select('_refresh_interval', $this->settings['refresh_interval']);
- $this->setCheckboxState('_pretty_date', $this->settings['pretty_date']);
- $this->setCheckboxState('_display_next', $this->settings['display_next']);
- $this->setCheckboxState('_standard_windows', $this->settings['standard_windows']);
+ $browser->setCheckboxState('_pretty_date', $this->settings['pretty_date']);
+ $browser->setCheckboxState('_display_next', $this->settings['display_next']);
+ $browser->setCheckboxState('_standard_windows', $this->settings['standard_windows']);
// Submit form
- if (!$this->isPhone()) {
+ if (!$browser->isPhone()) {
$browser->click('.formbuttons button.submit');
}
});
- if ($this->isPhone()) {
+ if ($browser->isPhone()) {
$browser->click('#layout-content .footer a.submit');
}
- $this->waitForMessage('confirmation', 'Successfully saved');
+ $browser->waitForMessage('confirmation', 'Successfully saved');
// Verify if every option has been updated
$browser->withinFrame('#preferences-frame', function ($browser) {
$browser->assertSelected('_timezone', $this->settings['timezone']);
$browser->assertSelected('_time_format', $this->settings['time_format']);
$browser->assertSelected('_date_format', $this->settings['date_format']);
$browser->assertSelected('_refresh_interval', $this->settings['refresh_interval']);
- $this->assertCheckboxState('_pretty_date', $this->settings['pretty_date']);
- $this->assertCheckboxState('_display_next', $this->settings['display_next']);
- $this->assertCheckboxState('_standard_windows', $this->settings['standard_windows']);
+ $browser->assertCheckboxState('_pretty_date', $this->settings['pretty_date']);
+ $browser->assertCheckboxState('_display_next', $this->settings['display_next']);
+ $browser->assertCheckboxState('_standard_windows', $this->settings['standard_windows']);
// Assert the options have been saved in database properly
$prefs = \bootstrap::get_prefs();
$options = array_diff(array_keys($this->settings), ['refresh_interval', 'pretty_date']);
foreach ($options as $option) {
$this->assertEquals($this->settings[$option], $prefs[$option]);
}
$this->assertEquals($this->settings['pretty_date'], $prefs['prettydate']);
$this->assertEquals($this->settings['refresh_interval'], $prefs['refresh_interval']/60);
});
});
}
}
diff --git a/tests/Browser/Settings/Responses.php b/tests/Browser/Settings/Responses.php
index 5c3a09501..844923730 100644
--- a/tests/Browser/Settings/Responses.php
+++ b/tests/Browser/Settings/Responses.php
@@ -1,33 +1,35 @@
<?php
namespace Tests\Browser\Settings;
-class Responses extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Responses extends \Tests\Browser\TestCase
{
public function testIdentities()
{
$this->browse(function ($browser) {
- $this->go('settings', 'responses');
-
- // check task and action
- $this->assertEnvEquals('task', 'settings');
- $this->assertEnvEquals('action', 'responses');
+ $browser->go('settings', 'responses');
- $objects = $this->getObjects();
+ $browser->with(new App(), function ($browser) {
+ // check task and action
+ $browser->assertEnv('task', 'settings');
+ $browser->assertEnv('action', 'responses');
- // these objects should be there always
- $this->assertContains('responseslist', $objects);
+ // these objects should be there always
+ $browser->assertObjects(['responseslist']);
+ });
- if ($this->isDesktop()) {
+ if ($browser->isDesktop()) {
$browser->assertVisible('#settings-menu li.responses.selected');
}
// Responses list
$browser->assertPresent('#responses-table');
$browser->assertMissing('#responses-table tr');
// Toolbar menu
- $this->assertToolbarMenu(['create'], ['delete']);
+ $browser->assertToolbarMenu(['create'], ['delete']);
});
}
}
diff --git a/tests/Browser/Settings/Settings.php b/tests/Browser/Settings/Settings.php
index a5fd9d889..2f4e95a38 100644
--- a/tests/Browser/Settings/Settings.php
+++ b/tests/Browser/Settings/Settings.php
@@ -1,29 +1,33 @@
<?php
namespace Tests\Browser\Settings;
-class Settings extends \Tests\Browser\DuskTestCase
+use Tests\Browser\Components\App;
+
+class Settings extends \Tests\Browser\TestCase
{
public function testSettings()
{
$this->browse(function ($browser) {
- $this->go('settings');
+ $browser->go('settings');
// task should be set to 'settings'
- $this->assertEnvEquals('task', 'settings');
+ $browser->with(new App(), function ($browser) {
+ $browser->assertEnv('task', 'settings');
+ });
$browser->assertSeeIn('#layout-sidebar .header', 'Settings');
// Sidebar menu
$browser->with('#settings-menu', function($browser) {
$browser->assertSeeIn('li.preferences', 'Preferences');
$browser->assertSeeIn('li.folders', 'Folders');
$browser->assertSeeIn('li.identities', 'Identities');
$browser->assertSeeIn('li.responses', 'Responses');
});
// Task menu
- $this->assertTaskMenu('settings');
+ $browser->assertTaskMenu('settings');
});
}
}
diff --git a/tests/Browser/TestCase.php b/tests/Browser/TestCase.php
new file mode 100644
index 000000000..04e4a31f2
--- /dev/null
+++ b/tests/Browser/TestCase.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Tests\Browser;
+
+use PHPUnit\Framework\TestCase as PHPUnitTestCase;
+use Facebook\WebDriver\Chrome\ChromeOptions;
+use Facebook\WebDriver\Remote\RemoteWebDriver;
+use Facebook\WebDriver\Remote\DesiredCapabilities;
+use Laravel\Dusk\Chrome\SupportsChrome;
+use Laravel\Dusk\Concerns\ProvidesBrowser;
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Process\Process;
+
+abstract class TestCase extends PHPUnitTestCase
+{
+ use ProvidesBrowser,
+ SupportsChrome;
+
+ protected $app;
+ protected static $phpProcess;
+
+
+ /**
+ * Replace Dusk's Browser with our (extended) Browser
+ */
+ protected function newBrowser($driver)
+ {
+ return new Browser($driver);
+ }
+
+ /**
+ * Prepare for Dusk test execution.
+ *
+ * @beforeClass
+ * @return void
+ */
+ public static function prepare()
+ {
+ static::startWebServer();
+ static::startChromeDriver();
+ }
+
+ /**
+ * Create the RemoteWebDriver instance.
+ *
+ * @return \Facebook\WebDriver\Remote\RemoteWebDriver
+ */
+ protected function driver()
+ {
+ $options = (new ChromeOptions())->addArguments([
+ '--lang=en_US',
+ '--disable-gpu',
+ '--headless',
+ ]);
+
+ // For file download handling
+ $prefs = [
+ 'profile.default_content_settings.popups' => 0,
+ 'download.default_directory' => TESTS_DIR . 'downloads',
+ ];
+
+ $options->setExperimentalOption('prefs', $prefs);
+
+ if (getenv('TESTS_MODE') == 'phone') {
+ // Fake User-Agent string for mobile mode
+ $ua = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36';
+ $options->setExperimentalOption('mobileEmulation', ['userAgent' => $ua]);
+ $options->addArguments(['--window-size=375,667']);
+ }
+ else if (getenv('TESTS_MODE') == 'tablet') {
+ // Fake User-Agent string for mobile mode
+ $ua = 'Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36';
+ $options->setExperimentalOption('mobileEmulation', ['userAgent' => $ua]);
+ $options->addArguments(['--window-size=1024,768']);
+ }
+ else {
+ $options->addArguments(['--window-size=1280,720']);
+ }
+
+ // Make sure downloads dir exists and is empty
+ if (!file_exists(TESTS_DIR . 'downloads')) {
+ mkdir(TESTS_DIR . 'downloads', 0777, true);
+ }
+ else {
+ foreach (glob(TESTS_DIR . 'downloads/*') as $file) {
+ @unlink($file);
+ }
+ }
+
+ return RemoteWebDriver::create(
+ 'http://localhost:9515',
+ DesiredCapabilities::chrome()->setCapability(
+ ChromeOptions::CAPABILITY,
+ $options
+ )
+ );
+ }
+
+ /**
+ * Set up the test run
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->app = \rcmail::get_instance();
+
+ Browser::$baseUrl = 'http://localhost:8000';
+ Browser::$storeScreenshotsAt = TESTS_DIR . 'screenshots';
+ Browser::$storeConsoleLogAt = TESTS_DIR . 'console';
+
+ // This folder must exist in case Browser will try to write logs to it
+ if (!is_dir(Browser::$storeConsoleLogAt)) {
+ mkdir(Browser::$storeConsoleLogAt, 0777, true);
+ }
+
+ // Purge screenshots from the last test run
+ $pattern = sprintf('failure-%s_%s-*',
+ str_replace("\\", '_', get_class($this)),
+ $this->getName(false)
+ );
+
+ try {
+ $files = Finder::create()->files()->in(Browser::$storeScreenshotsAt)->name($pattern);
+ foreach ($files as $file) {
+ @unlink($file->getRealPath());
+ }
+ }
+ catch (\Symfony\Component\Finder\Exception\DirectoryNotFoundException $e) {
+ // ignore missing screenshots directory
+ }
+
+ // Purge console logs from the last test run
+ $pattern = sprintf('%s_%s-*',
+ str_replace("\\", '_', get_class($this)),
+ $this->getName(false)
+ );
+
+ try {
+ $files = Finder::create()->files()->in(Browser::$storeConsoleLogAt)->name($pattern);
+ foreach ($files as $file) {
+ @unlink($file->getRealPath());
+ }
+ }
+ catch (\Symfony\Component\Finder\Exception\DirectoryNotFoundException $e) {
+ // ignore missing screenshots directory
+ }
+ }
+
+ /**
+ * Starts PHP server.
+ */
+ protected static function startWebServer()
+ {
+ $path = realpath(__DIR__ . '/../../public_html');
+ $cmd = ['php', '-S', 'localhost:8000'];
+ $env = [];
+
+ static::$phpProcess = new Process($cmd, null, $env);
+ static::$phpProcess->setWorkingDirectory($path);
+ static::$phpProcess->start();
+
+ static::afterClass(function () {
+ static::$phpProcess->stop();
+ });
+ }
+}
diff --git a/tests/Browser/bootstrap.php b/tests/Browser/bootstrap.php
index 6d8c036b5..49db75944 100644
--- a/tests/Browser/bootstrap.php
+++ b/tests/Browser/bootstrap.php
@@ -1,211 +1,215 @@
<?php
/*
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) 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 functional 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(__DIR__ . '/../../') . '/' );
require_once(INSTALL_PATH . 'program/include/iniset.php');
$rcmail = rcmail::get_instance(0, 'test');
define('TESTS_DIR', realpath(__DIR__) . '/');
define('TESTS_USER', $rcmail->config->get('tests_username'));
define('TESTS_PASS', $rcmail->config->get('tests_password'));
-require_once(__DIR__ . '/DuskTestCase.php');
+require_once(__DIR__ . '/Browser.php');
+require_once(__DIR__ . '/TestCase.php');
+require_once(__DIR__ . '/Components/App.php');
+require_once(__DIR__ . '/Components/Taskmenu.php');
+require_once(__DIR__ . '/Components/Toolbarmenu.php');
/**
* Utilities for test environment setup
*/
class bootstrap
{
static $imap_ready = null;
/**
* Wipe and re-initialize database
*/
public static function init_db()
{
$rcmail = rcmail::get_instance();
$dsn = rcube_db::parse_dsn($rcmail->config->get('db_dsnw'));
$db = $rcmail->get_dbh();
if ($dsn['phptype'] == 'mysql' || $dsn['phptype'] == 'mysqli') {
// drop all existing tables first
$db->query("SET FOREIGN_KEY_CHECKS=0");
$sql_res = $db->query("SHOW TABLES");
while ($sql_arr = $db->fetch_array($sql_res)) {
$table = reset($sql_arr);
$db->query("DROP TABLE $table");
}
// init database with schema
system(sprintf('cat %s %s | mysql -h %s -u %s --password=%s %s',
realpath(INSTALL_PATH . '/SQL/mysql.initial.sql'),
realpath(TESTS_DIR . 'data/data.sql'),
escapeshellarg($dsn['hostspec']),
escapeshellarg($dsn['username']),
escapeshellarg($dsn['password']),
escapeshellarg($dsn['database'])
));
}
else if ($dsn['phptype'] == 'sqlite') {
$db->closeConnection();
// delete database file
system(sprintf('rm -f %s', escapeshellarg($dsn['database'])));
// load sample test data
// Note: exec_script() does not really work with these queries
$sql = file_get_contents(TESTS_DIR . 'data/data.sql');
$sql = preg_split('/;\n/', $sql, -1, PREG_SPLIT_NO_EMPTY);
foreach ($sql as $query) {
$result = $db->query($query);
if ($db->is_error($result)) {
die($db->is_error());
}
}
}
}
/**
* Wipe the configured IMAP account and fill with test data
*/
public static function init_imap()
{
if (!TESTS_USER) {
return false;
}
else if (self::$imap_ready !== null) {
return self::$imap_ready;
}
self::connect_imap(TESTS_USER, TESTS_PASS);
self::purge_mailbox('INBOX');
// self::ensure_mailbox('Archive', true);
return self::$imap_ready;
}
/**
* Authenticate to IMAP with the given credentials
*/
public static function connect_imap($username, $password)
{
$rcmail = rcmail::get_instance();
$imap = $rcmail->get_storage();
if ($imap->is_connected()) {
$imap->close();
self::$imap_ready = false;
}
$imap_host = $rcmail->config->get('default_host');
$a_host = parse_url($imap_host);
if ($a_host['host']) {
$imap_host = $a_host['host'];
$imap_ssl = isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'));
$imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143);
}
else {
$imap_port = 143;
$imap_ssl = false;
}
if (!$imap->connect($imap_host, $username, $password, $imap_port, $imap_ssl)) {
die("IMAP error: unable to authenticate with user " . TESTS_USER);
}
self::$imap_ready = true;
}
/**
* Import the given file into IMAP
*/
public static function import_message($filename, $mailbox = 'INBOX')
{
if (!self::init_imap()) {
die(__METHOD__ . ': IMAP connection unavailable');
}
$imap = rcmail::get_instance()->get_storage();
$imap->save_message($mailbox, file_get_contents($filename));
}
/**
* Delete all messages from the given mailbox
*/
public static function purge_mailbox($mailbox)
{
if (!self::init_imap()) {
die(__METHOD__ . ': IMAP connection unavailable');
}
$imap = rcmail::get_instance()->get_storage();
$imap->delete_message('*', $mailbox);
}
/**
* Make sure the given mailbox exists in IMAP
*/
public static function ensure_mailbox($mailbox, $empty = false)
{
if (!self::init_imap()) {
die(__METHOD__ . ': IMAP connection unavailable');
}
$imap = rcmail::get_instance()->get_storage();
$folders = $imap->list_folders();
if (!in_array($mailbox, $folders)) {
$imap->create_folder($mailbox, true);
}
else if ($empty) {
$imap->delete_message('*', $mailbox);
}
}
/**
* Check IMAP capabilities
*/
public static function get_storage()
{
if (!self::init_imap()) {
die(__METHOD__ . ': IMAP connection unavailable');
}
return rcmail::get_instance()->get_storage();
}
/**
* Return user preferences directly from database
*/
public static function get_prefs()
{
$db = rcmail::get_instance()->get_dbh();
$query = $db->query("SELECT preferences FROM users WHERE username = ?", TESTS_USER);
$record = $db->fetch_assoc($query);
return unserialize($record['preferences']);
}
}
diff --git a/tests/Browser/phpunit.xml b/tests/Browser/phpunit.xml
index ada5031c6..3c31c2d80 100644
--- a/tests/Browser/phpunit.xml
+++ b/tests/Browser/phpunit.xml
@@ -1,30 +1,30 @@
<phpunit backupGlobals="false"
bootstrap="bootstrap.php"
colors="true">
<testsuites>
<testsuite name="Logon">
- <file>Login.php</file>
- <file>Logout.php</file>
+ <file>Logon/Login.php</file>
+ <file>Logon/Logout.php</file>
</testsuite>
<testsuite name="Contacts">
<file>Contacts/Contacts.php</file>
<file>Contacts/Import.php</file>
<file>Contacts/Export.php</file>
</testsuite>
<testsuite name="Settings">
<file>Settings/Settings.php</file>
<file>Settings/Preferences.php</file>
<file>Settings/Preferences/General.php</file>
<file>Settings/Folders.php</file>
<file>Settings/Identities.php</file>
<file>Settings/Responses.php</file>
<file>Settings/About.php</file>
</testsuite>
<testsuite name="Mail">
<file>Mail/Mail.php</file>
<file>Mail/Compose.php</file>
<file>Mail/Getunread.php</file>
<file>Mail/List.php</file>
</testsuite>
</testsuites>
</phpunit>

File Metadata

Mime Type
text/x-diff
Expires
Thu, Mar 19, 9:05 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458567
Default Alt Text
(89 KB)

Event Timeline