Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2571843
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
89 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Mar 19, 9:05 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458567
Default Alt Text
(89 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment