Page MenuHomePhorge

No OneTemporary

Size
22 KB
Referenced Files
None
Subscribers
None
diff --git a/tests/Browser/Contacts/Export.php b/tests/Browser/Contacts/Export.php
new file mode 100644
index 000000000..5d757df2d
--- /dev/null
+++ b/tests/Browser/Contacts/Export.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Tests\Browser\Contacts;
+
+class Export extends \Tests\Browser\DuskTestCase
+{
+ /**
+ * Test exporting all contacts
+ */
+ public function testExportAll()
+ {
+ \bootstrap::init_db();
+
+ $this->browse(function ($browser) {
+ $this->go('addressbook');
+
+ $this->clickToolbarMenuItem('export');
+ });
+
+ // 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->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');
+
+ $vcard_content = $this->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);
+ $this->removeDownloadedFile('contacts.vcf');
+ }
+}
diff --git a/tests/Browser/Contacts/Import.php b/tests/Browser/Contacts/Import.php
index 50514f23b..fd04df0cd 100644
--- a/tests/Browser/Contacts/Import.php
+++ b/tests/Browser/Contacts/Import.php
@@ -1,103 +1,105 @@
<?php
namespace Tests\Browser\Contacts;
class Import extends \Tests\Browser\DuskTestCase
{
/**
* Test basic elements of contacts import UI
*/
public function testImportUI()
{
+ \bootstrap::init_db();
+
$this->browse(function ($browser) {
$this->go('addressbook');
$this->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->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->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
index 5398d455d..15fe07b95 100644
--- a/tests/Browser/DuskTestCase.php
+++ b/tests/Browser/DuskTestCase.php
@@ -1,422 +1,460 @@
<?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 = __DIR__ . '/screenshots';
- Browser::$storeConsoleLogAt = __DIR__ . '/console';
+ 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)
+ protected function clickToolbarMenuItem($name, $dropdown_action = null)
{
- $this->browse(function (Browser $browser) use ($name) {
+ $this->browse(function (Browser $browser) use ($name, $dropdown_action) {
if ($this->isPhone()) {
$browser->click('.toolbar-menu-button');
}
- $browser->click("#toolbar-menu a.{$name}");
+ $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();
-// $browser->waitUntilMissing('#toolbar-menu');
}
});
}
+ 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;
}
- /**
- * Get HTML IDs of defined buttons for specified Roundcube action
- */
- protected function getButtons($action)
- {
- $this->browse(function (Browser $browser) use ($action, &$buttons) {
- $buttons = $browser->script("return rcmail.buttons['$action']");
- $buttons = $buttons[0];
- });
-
- if (is_array($buttons)) {
- foreach ($buttons as $idx => $button) {
- $buttons[$idx] = $button['id'];
- }
- }
-
- return (array) $buttons;
- }
-
/**
* 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/phpunit.xml b/tests/Browser/phpunit.xml
index 77a041b95..ada5031c6 100644
--- a/tests/Browser/phpunit.xml
+++ b/tests/Browser/phpunit.xml
@@ -1,29 +1,30 @@
<phpunit backupGlobals="false"
bootstrap="bootstrap.php"
colors="true">
<testsuites>
<testsuite name="Logon">
<file>Login.php</file>
<file>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, 8:57 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
457056
Default Alt Text
(22 KB)

Event Timeline