diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/acceptance/config/behat.yml | 2 | ||||
-rw-r--r-- | tests/acceptance/features/access-levels.feature | 9 | ||||
-rw-r--r-- | tests/acceptance/features/app-files.feature | 65 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/ContactsMenuContext.php | 147 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/FileListAncestorSetter.php | 67 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/FileListContext.php | 374 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/FilesAppContext.php | 309 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/FilesSharingAppContext.php | 21 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/SettingsMenuContext.php | 16 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/WaitFor.php | 78 | ||||
-rw-r--r-- | tests/acceptance/features/core/Actor.php | 18 | ||||
-rw-r--r-- | tests/acceptance/features/core/ActorContext.php | 4 | ||||
-rw-r--r-- | tests/acceptance/features/core/ElementWrapper.php | 2 | ||||
-rw-r--r-- | tests/acceptance/features/header.feature | 74 | ||||
-rw-r--r-- | tests/lib/Collaboration/Collaborators/MailPluginTest.php | 129 |
15 files changed, 988 insertions, 327 deletions
diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 3495769457d..0eda2b19803 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -11,7 +11,9 @@ default: - AppNavigationContext - CommentsAppContext + - ContactsMenuContext - FeatureContext + - FileListContext - FilesAppContext - FilesSharingAppContext - LoginPageContext diff --git a/tests/acceptance/features/access-levels.feature b/tests/acceptance/features/access-levels.feature index 80170296675..fb6b180be7e 100644 --- a/tests/acceptance/features/access-levels.feature +++ b/tests/acceptance/features/access-levels.feature @@ -9,15 +9,6 @@ Feature: access-levels And I see that the "Help" item in the Settings menu is shown And I see that the "Log out" item in the Settings menu is shown - Scenario: admin users can see admin-level items in the Settings menu - Given I am logged in as the admin - When I open the Settings menu - Then I see that the Settings menu is shown - And I see that the "Settings" item in the Settings menu is shown - And I see that the "Users" item in the Settings menu is shown - And I see that the "Help" item in the Settings menu is shown - And I see that the "Log out" item in the Settings menu is shown - Scenario: regular users cannot see admin-level items on the Settings page Given I am logged in When I visit the settings page diff --git a/tests/acceptance/features/app-files.feature b/tests/acceptance/features/app-files.feature index dd5340d6374..97f17344187 100644 --- a/tests/acceptance/features/app-files.feature +++ b/tests/acceptance/features/app-files.feature @@ -41,6 +41,71 @@ Feature: app-files And I open the Share menu Then I see that the Share menu is shown + Scenario: creation is not possible by default in a public shared folder + Given I act as John + And I am logged in + And I create a new folder named "Shared folder" + # To share the link the "Share" inline action has to be clicked but, as the + # details view is opened automatically when the folder is created, clicking + # on the inline action could fail if it is covered by the details view due + # to its opening animation. Instead of ensuring that the animations of the + # contents and the details view have both finished it is easier to close the + # details view and wait until it is closed before continuing. + And I close the details view + And I see that the details view is closed + And I share the link for "Shared folder" + And I write down the shared link + When I act as Jane + And I visit the shared link I wrote down + And I see that the current page is the shared link I wrote down + And I see that the file list is eventually loaded + Then I see that it is not possible to create new files + + Scenario: create folder in a public editable shared folder + Given I act as John + And I am logged in + And I create a new folder named "Editable shared folder" + # To share the link the "Share" inline action has to be clicked but, as the + # details view is opened automatically when the folder is created, clicking + # on the inline action could fail if it is covered by the details view due + # to its opening animation. Instead of ensuring that the animations of the + # contents and the details view have both finished it is easier to close the + # details view and wait until it is closed before continuing. + And I close the details view + And I see that the details view is closed + And I share the link for "Editable shared folder" + And I set the shared link as editable + And I write down the shared link + When I act as Jane + And I visit the shared link I wrote down + And I see that the current page is the shared link I wrote down + And I create a new folder named "Subfolder" + Then I see that the file list contains a file named "Subfolder" + + Scenario: owner sees folder created in the public page of an editable shared folder + Given I act as John + And I am logged in + And I create a new folder named "Editable shared folder" + # To share the link the "Share" inline action has to be clicked but, as the + # details view is opened automatically when the folder is created, clicking + # on the inline action could fail if it is covered by the details view due + # to its opening animation. Instead of ensuring that the animations of the + # contents and the details view have both finished it is easier to close the + # details view and wait until it is closed before continuing. + And I close the details view + And I see that the details view is closed + And I share the link for "Editable shared folder" + And I set the shared link as editable + And I write down the shared link + And I act as Jane + And I visit the shared link I wrote down + And I see that the current page is the shared link I wrote down + And I create a new folder named "Subfolder" + And I see that the file list contains a file named "Subfolder" + When I act as John + And I enter in the folder named "Editable shared folder" + Then I see that the file list contains a file named "Subfolder" + Scenario: set a password to a shared link Given I am logged in And I share the link for "welcome.txt" diff --git a/tests/acceptance/features/bootstrap/ContactsMenuContext.php b/tests/acceptance/features/bootstrap/ContactsMenuContext.php new file mode 100644 index 00000000000..1be38b79e70 --- /dev/null +++ b/tests/acceptance/features/bootstrap/ContactsMenuContext.php @@ -0,0 +1,147 @@ +<?php + +/** + * + * @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv) (skjnldsv@protonmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +use Behat\Behat\Context\Context; + +class ContactsMenuContext implements Context, ActorAwareInterface { + + use ActorAware; + + /** + * @return Locator + */ + public static function contactsMenuButton() { + return Locator::forThe()->xpath("//*[@id = 'header']//*[@id = 'contactsmenu']")-> + describedAs("Contacts menu button"); + } + + /** + * @return Locator + */ + public static function contactsMenu() { + return Locator::forThe()->css(".menu")-> + descendantOf(self::contactsMenuButton())-> + describedAs("Contacts menu"); + } + + /** + * @return Locator + */ + public static function contactsMenuSearchInput() { + return Locator::forThe()->id("contactsmenu-search")-> + descendantOf(self::contactsMenu())-> + describedAs("Contacts menu search input"); + } + + /** + * @return Locator + */ + public static function noResultsMessage() { + return Locator::forThe()->xpath("//*[@class = 'emptycontent' and normalize-space() = 'No contacts found']")-> + descendantOf(self::contactsMenu())-> + describedAs("No results message in Contacts menu"); + } + + /** + * @return Locator + */ + private static function menuItemFor($contactName) { + return Locator::forThe()->xpath("//*[@class = 'contact' and normalize-space() = '$contactName']")-> + descendantOf(self::contactsMenu())-> + describedAs($contactName . " contact in Contacts menu"); + } + + /** + * @When I open the Contacts menu + */ + public function iOpenTheContactsMenu() { + $this->actor->find(self::contactsMenuButton(), 10)->click(); + } + + /** + * @When I search for the user :user + */ + public function iSearchForTheUser($user) { + $this->actor->find(self::contactsMenuSearchInput(), 10)->setValue($user . "\r"); + } + + /** + * @Then I see that the Contacts menu is shown + */ + public function iSeeThatTheContactsMenuIsShown() { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::contactsMenu(), 10)->isVisible()); + } + + /** + * @Then I see that the Contacts menu search input is shown + */ + public function iSeeThatTheContactsMenuSearchInputIsShown() { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::contactsMenuSearchInput(), 10)->isVisible()); + } + + /** + * @Then I see that the no results message in the Contacts menu is shown + */ + public function iSeeThatTheNoResultsMessageInTheContactsMenuIsShown() { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::noResultsMessage(), 10)->isVisible()); + } + + /** + * @Then I see that the contact :contactName in the Contacts menu is shown + */ + public function iSeeThatTheContactInTheContactsMenuIsShown($contactName) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::menuItemFor($contactName), 10)->isVisible()); + } + + /** + * @Then I see that the contact :contactName in the Contacts menu is not shown + */ + public function iSeeThatTheContactInTheContactsMenuIsNotShown($contactName) { + $this->iSeeThatThecontactsMenuIsShown(); + + try { + PHPUnit_Framework_Assert::assertFalse( + $this->actor->find(self::menuItemFor($contactName))->isVisible()); + } catch (NoSuchElementException $exception) { + } + } + + /** + * @Then I see that the contact :contactName in the Contacts menu is eventually not shown + */ + public function iSeeThatTheContactInTheContactsMenuIsEventuallyNotShown($contactName) { + $this->iSeeThatThecontactsMenuIsShown(); + + if (!WaitFor::elementToBeEventuallyNotShown( + $this->actor, + self::menuItemFor($contactName), + $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { + PHPUnit_Framework_Assert::fail("The $contactName contact in Contacts menu is still shown after $timeout seconds"); + } + } + +} diff --git a/tests/acceptance/features/bootstrap/FileListAncestorSetter.php b/tests/acceptance/features/bootstrap/FileListAncestorSetter.php new file mode 100644 index 00000000000..2f8d3ad00e5 --- /dev/null +++ b/tests/acceptance/features/bootstrap/FileListAncestorSetter.php @@ -0,0 +1,67 @@ +<?php + +/** + * + * @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +use Behat\Behat\Context\Context; +use Behat\Behat\Hook\Scope\BeforeScenarioScope; + +/** + * Helper trait to set the ancestor of the file list. + * + * The FileListContext provides steps to interact with and check the behaviour + * of a file list. However, the FileListContext does not know the right file + * list ancestor that has to be used by the file list steps; this has to be set + * from other contexts, for example, when the Files app or the public page for a + * shared folder is opened. + * + * Contexts that "know" that certain file list ancestor has to be used by the + * FileListContext steps should use this trait and call + * "setFileListAncestorForActor" when needed. + */ +trait FileListAncestorSetter { + + /** + * @var FileListContext + */ + private $fileListContext; + + /** + * @BeforeScenario + */ + public function getSiblingFileListContext(BeforeScenarioScope $scope) { + $environment = $scope->getEnvironment(); + + $this->fileListContext = $environment->getContext("FileListContext"); + } + + /** + * Sets the file list ancestor to be used in the file list steps performed + * by the given actor. + * + * @param null|Locator $fileListAncestor the file list ancestor + * @param Actor $actor the actor + */ + private function setFileListAncestorForActor($fileListAncestor, Actor $actor) { + $this->fileListContext->setFileListAncestorForActor($fileListAncestor, $actor); + } + +} diff --git a/tests/acceptance/features/bootstrap/FileListContext.php b/tests/acceptance/features/bootstrap/FileListContext.php new file mode 100644 index 00000000000..bc225e3f9b1 --- /dev/null +++ b/tests/acceptance/features/bootstrap/FileListContext.php @@ -0,0 +1,374 @@ +<?php + +/** + * + * @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +use Behat\Behat\Context\Context; + +class FileListContext implements Context, ActorAwareInterface { + + /** + * @var Actor + */ + private $actor; + + /** + * @var array + */ + private $fileListAncestorsByActor; + + /** + * @var Locator + */ + private $fileListAncestor; + + /** + * @BeforeScenario + */ + public function initializeFileListAncestors() { + $this->fileListAncestorsByActor = array(); + $this->fileListAncestor = null; + } + + /** + * @param Actor $actor + */ + public function setCurrentActor(Actor $actor) { + $this->actor = $actor; + + if (array_key_exists($actor->getName(), $this->fileListAncestorsByActor)) { + $this->fileListAncestor = $this->fileListAncestorsByActor[$actor->getName()]; + } else { + $this->fileListAncestor = null; + } + } + + /** + * Sets the file list ancestor to be used in the steps performed by the + * given actor from that point on (until changed again). + * + * This is meant to be called from other contexts, for example, when the + * Files app or the public page for a shared folder are opened. + * + * The FileListAncestorSetter trait can be used to reduce the boilerplate + * needed to set the file list ancestor from other contexts. + * + * @param null|Locator $fileListAncestor the file list ancestor + * @param Actor $actor the actor + */ + public function setFileListAncestorForActor($fileListAncestor, Actor $actor) { + $this->fileListAncestorsByActor[$actor->getName()] = $fileListAncestor; + } + + /** + * @return Locator + */ + public static function mainWorkingIcon($fileListAncestor) { + return Locator::forThe()->css(".mask.icon-loading")-> + descendantOf($fileListAncestor)-> + describedAs("Main working icon in file list"); + } + + /** + * @return Locator + */ + public static function createMenuButton($fileListAncestor) { + return Locator::forThe()->css("#controls .button.new")-> + descendantOf($fileListAncestor)-> + describedAs("Create menu button in file list"); + } + + /** + * @return Locator + */ + private static function createMenuItemFor($fileListAncestor, $newType) { + return Locator::forThe()->xpath("//div[contains(concat(' ', normalize-space(@class), ' '), ' newFileMenu ')]//span[normalize-space() = '$newType']/ancestor::li")-> + descendantOf($fileListAncestor)-> + describedAs("Create $newType menu item in file list"); + } + + /** + * @return Locator + */ + public static function createNewFolderMenuItem($fileListAncestor) { + return self::createMenuItemFor($fileListAncestor, "New folder"); + } + + /** + * @return Locator + */ + public static function createNewFolderMenuItemNameInput($fileListAncestor) { + return Locator::forThe()->css(".filenameform input")-> + descendantOf(self::createNewFolderMenuItem($fileListAncestor))-> + describedAs("Name input in create new folder menu item in file list"); + } + + /** + * @return Locator + */ + public static function rowForFile($fileListAncestor, $fileName) { + return Locator::forThe()->xpath("//*[@id = 'fileList']//span[contains(concat(' ', normalize-space(@class), ' '), ' nametext ') and normalize-space() = '$fileName']/ancestor::tr")-> + descendantOf($fileListAncestor)-> + describedAs("Row for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function rowForFilePreceding($fileListAncestor, $fileName1, $fileName2) { + return Locator::forThe()->xpath("//preceding-sibling::tr//span[contains(concat(' ', normalize-space(@class), ' '), ' nametext ') and normalize-space() = '$fileName1']/ancestor::tr")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName2))-> + describedAs("Row for file $fileName1 preceding $fileName2 in file list"); + } + + /** + * @return Locator + */ + public static function favoriteMarkForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".favorite-mark")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName))-> + describedAs("Favorite mark for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function notFavoritedStateIconForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".icon-star")-> + descendantOf(self::favoriteMarkForFile($fileListAncestor, $fileName))-> + describedAs("Not favorited state icon for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function favoritedStateIconForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".icon-starred")-> + descendantOf(self::favoriteMarkForFile($fileListAncestor, $fileName))-> + describedAs("Favorited state icon for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function mainLinkForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".name")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName))-> + describedAs("Main link for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function renameInputForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css("input.filename")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName))-> + describedAs("Rename input for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function shareActionForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".action-share")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName))-> + describedAs("Share action for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function fileActionsMenuButtonForFile($fileListAncestor, $fileName) { + return Locator::forThe()->css(".action-menu")-> + descendantOf(self::rowForFile($fileListAncestor, $fileName))-> + describedAs("File actions menu button for file $fileName in file list"); + } + + /** + * @return Locator + */ + public static function fileActionsMenu() { + return Locator::forThe()->css(".fileActionsMenu")-> + describedAs("File actions menu in file list"); + } + + /** + * @return Locator + */ + private static function fileActionsMenuItemFor($itemText) { + return Locator::forThe()->xpath("//a[normalize-space() = '$itemText']")-> + descendantOf(self::fileActionsMenu())-> + describedAs($itemText . " item in file actions menu in file list"); + } + + /** + * @return Locator + */ + public static function addToFavoritesMenuItem() { + return self::fileActionsMenuItemFor("Add to favorites"); + } + + /** + * @return Locator + */ + public static function removeFromFavoritesMenuItem() { + return self::fileActionsMenuItemFor("Remove from favorites"); + } + + /** + * @return Locator + */ + public static function detailsMenuItem() { + return self::fileActionsMenuItemFor("Details"); + } + + /** + * @return Locator + */ + public static function renameMenuItem() { + return self::fileActionsMenuItemFor("Rename"); + } + + /** + * @return Locator + */ + public static function viewFileInFolderMenuItem() { + return self::fileActionsMenuItemFor("View in folder"); + } + + /** + * @Given I create a new folder named :folderName + */ + public function iCreateANewFolderNamed($folderName) { + $this->actor->find(self::createMenuButton($this->fileListAncestor), 10)->click(); + + $this->actor->find(self::createNewFolderMenuItem($this->fileListAncestor), 2)->click(); + $this->actor->find(self::createNewFolderMenuItemNameInput($this->fileListAncestor), 2)->setValue($folderName . "\r"); + } + + /** + * @Given I enter in the folder named :folderName + */ + public function iEnterInTheFolderNamed($folderName) { + $this->actor->find(self::mainLinkForFile($this->fileListAncestor, $folderName), 10)->click(); + } + + /** + * @Given I open the details view for :fileName + */ + public function iOpenTheDetailsViewFor($fileName) { + $this->actor->find(self::fileActionsMenuButtonForFile($this->fileListAncestor, $fileName), 10)->click(); + + $this->actor->find(self::detailsMenuItem(), 2)->click(); + } + + /** + * @Given I rename :fileName1 to :fileName2 + */ + public function iRenameTo($fileName1, $fileName2) { + $this->actor->find(self::fileActionsMenuButtonForFile($this->fileListAncestor, $fileName1), 10)->click(); + + $this->actor->find(self::renameMenuItem(), 2)->click(); + + $this->actor->find(self::renameInputForFile($this->fileListAncestor, $fileName1), 10)->setValue($fileName2 . "\r"); + } + + /** + * @Given I mark :fileName as favorite + */ + public function iMarkAsFavorite($fileName) { + $this->iSeeThatIsNotMarkedAsFavorite($fileName); + + $this->actor->find(self::fileActionsMenuButtonForFile($this->fileListAncestor, $fileName), 10)->click(); + + $this->actor->find(self::addToFavoritesMenuItem(), 2)->click(); + } + + /** + * @Given I unmark :fileName as favorite + */ + public function iUnmarkAsFavorite($fileName) { + $this->iSeeThatIsMarkedAsFavorite($fileName); + + $this->actor->find(self::fileActionsMenuButtonForFile($this->fileListAncestor, $fileName), 10)->click(); + + $this->actor->find(self::removeFromFavoritesMenuItem(), 2)->click(); + } + + /** + * @When I view :fileName in folder + */ + public function iViewInFolder($fileName) { + $this->actor->find(self::fileActionsMenuButtonForFile($this->fileListAncestor, $fileName), 10)->click(); + + $this->actor->find(self::viewFileInFolderMenuItem(), 2)->click(); + } + + /** + * @Then I see that the file list is eventually loaded + */ + public function iSeeThatTheFileListIsEventuallyLoaded() { + if (!WaitFor::elementToBeEventuallyNotShown( + $this->actor, + self::mainWorkingIcon($this->fileListAncestor), + $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { + PHPUnit_Framework_Assert::fail("The main working icon for the file list is still shown after $timeout seconds"); + } + } + + /** + * @Then I see that it is not possible to create new files + */ + public function iSeeThatItIsNotPossibleToCreateNewFiles() { + // Once a file list is loaded the "Create" menu button is always in the + // DOM, so it is checked if it is visible or not. + PHPUnit_Framework_Assert::assertFalse($this->actor->find(self::createMenuButton($this->fileListAncestor))->isVisible()); + } + + /** + * @Then I see that the file list contains a file named :fileName + */ + public function iSeeThatTheFileListContainsAFileNamed($fileName) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForFile($this->fileListAncestor, $fileName), 10)); + } + + /** + * @Then I see that :fileName1 precedes :fileName2 in the file list + */ + public function iSeeThatPrecedesInTheFileList($fileName1, $fileName2) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForFilePreceding($this->fileListAncestor, $fileName1, $fileName2), 10)); + } + + /** + * @Then I see that :fileName is marked as favorite + */ + public function iSeeThatIsMarkedAsFavorite($fileName) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::favoritedStateIconForFile($this->fileListAncestor, $fileName), 10)); + } + + /** + * @Then I see that :fileName is not marked as favorite + */ + public function iSeeThatIsNotMarkedAsFavorite($fileName) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::notFavoritedStateIconForFile($this->fileListAncestor, $fileName), 10)); + } + +} diff --git a/tests/acceptance/features/bootstrap/FilesAppContext.php b/tests/acceptance/features/bootstrap/FilesAppContext.php index 117f3b54fb8..50997d98b0f 100644 --- a/tests/acceptance/features/bootstrap/FilesAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesAppContext.php @@ -26,6 +26,7 @@ use Behat\Behat\Context\Context; class FilesAppContext implements Context, ActorAwareInterface { use ActorAware; + use FileListAncestorSetter; /** * @return array @@ -216,6 +217,18 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function allowUploadAndEditingRadioButton() { + // forThe()->radio("Allow upload and editing") can not be used here; + // that would return the radio button itself, but the element that the + // user interacts with is the label. + return Locator::forThe()->xpath("//label[normalize-space() = 'Allow upload and editing']")-> + descendantOf(self::currentSectionDetailsView())-> + describedAs("Allow upload and editing radio button in the details view in Files app"); + } + + /** + * @return Locator + */ public static function passwordProtectCheckbox() { // forThe()->checkbox("Password protect") can not be used here; that // would return the checkbox itself, but the element that the user @@ -242,185 +255,6 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** - * @return Locator - */ - public static function createMenuButton() { - return Locator::forThe()->css("#controls .button.new")-> - descendantOf(self::currentSectionMainView())-> - describedAs("Create menu button in Files app"); - } - - /** - * @return Locator - */ - public static function createNewFolderMenuItem() { - return self::createMenuItemFor("New folder"); - } - - /** - * @return Locator - */ - public static function createNewFolderMenuItemNameInput() { - return Locator::forThe()->css(".filenameform input")-> - descendantOf(self::createNewFolderMenuItem())-> - describedAs("Name input in create new folder menu item in Files app"); - } - - /** - * @return Locator - */ - private static function createMenuItemFor($newType) { - return Locator::forThe()->xpath("//div[contains(concat(' ', normalize-space(@class), ' '), ' newFileMenu ')]//span[normalize-space() = '$newType']/ancestor::li")-> - descendantOf(self::currentSectionMainView())-> - describedAs("Create $newType menu item in Files app"); - } - - /** - * @return Locator - */ - public static function rowForFile($fileName) { - return Locator::forThe()->xpath("//*[@id = 'fileList']//span[contains(concat(' ', normalize-space(@class), ' '), ' nametext ') and normalize-space() = '$fileName']/ancestor::tr")-> - descendantOf(self::currentSectionMainView())-> - describedAs("Row for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function rowForFilePreceding($fileName1, $fileName2) { - return Locator::forThe()->xpath("//preceding-sibling::tr//span[contains(concat(' ', normalize-space(@class), ' '), ' nametext ') and normalize-space() = '$fileName1']/ancestor::tr")-> - descendantOf(self::rowForFile($fileName2))-> - describedAs("Row for file $fileName1 preceding $fileName2 in Files app"); - } - - /** - * @return Locator - */ - public static function favoriteMarkForFile($fileName) { - return Locator::forThe()->css(".favorite-mark")->descendantOf(self::rowForFile($fileName))-> - describedAs("Favorite mark for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function notFavoritedStateIconForFile($fileName) { - return Locator::forThe()->css(".icon-star")->descendantOf(self::favoriteMarkForFile($fileName))-> - describedAs("Not favorited state icon for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function favoritedStateIconForFile($fileName) { - return Locator::forThe()->css(".icon-starred")->descendantOf(self::favoriteMarkForFile($fileName))-> - describedAs("Favorited state icon for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function mainLinkForFile($fileName) { - return Locator::forThe()->css(".name")->descendantOf(self::rowForFile($fileName))-> - describedAs("Main link for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function renameInputForFile($fileName) { - return Locator::forThe()->css("input.filename")->descendantOf(self::rowForFile($fileName))-> - describedAs("Rename input for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function shareActionForFile($fileName) { - return Locator::forThe()->css(".action-share")->descendantOf(self::rowForFile($fileName))-> - describedAs("Share action for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function fileActionsMenuButtonForFile($fileName) { - return Locator::forThe()->css(".action-menu")->descendantOf(self::rowForFile($fileName))-> - describedAs("File actions menu button for file $fileName in Files app"); - } - - /** - * @return Locator - */ - public static function fileActionsMenu() { - return Locator::forThe()->css(".fileActionsMenu")-> - describedAs("File actions menu in Files app"); - } - - /** - * @return Locator - */ - public static function detailsMenuItem() { - return self::fileActionsMenuItemFor("Details"); - } - - /** - * @return Locator - */ - public static function renameMenuItem() { - return self::fileActionsMenuItemFor("Rename"); - } - - /** - * @return Locator - */ - public static function addToFavoritesMenuItem() { - return self::fileActionsMenuItemFor("Add to favorites"); - } - - /** - * @return Locator - */ - public static function removeFromFavoritesMenuItem() { - return self::fileActionsMenuItemFor("Remove from favorites"); - } - - /** - * @return Locator - */ - public static function viewFileInFolderMenuItem() { - return self::fileActionsMenuItemFor("View in folder"); - } - - /** - * @return Locator - */ - private static function fileActionsMenuItemFor($itemText) { - return Locator::forThe()->xpath("//a[normalize-space() = '$itemText']")-> - descendantOf(self::fileActionsMenu())-> - describedAs($itemText . " item in file actions menu in Files app"); - } - - /** - * @Given I create a new folder named :folderName - */ - public function iCreateANewFolderNamed($folderName) { - $this->actor->find(self::createMenuButton(), 10)->click(); - - $this->actor->find(self::createNewFolderMenuItem(), 2)->click(); - $this->actor->find(self::createNewFolderMenuItemNameInput(), 2)->setValue($folderName . "\r"); - } - - /** - * @Given I open the details view for :fileName - */ - public function iOpenTheDetailsViewFor($fileName) { - $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click(); - - $this->actor->find(self::detailsMenuItem(), 2)->click(); - } - - /** * @Given I close the details view */ public function iCloseTheDetailsView() { @@ -442,43 +276,10 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** - * @Given I rename :fileName1 to :fileName2 - */ - public function iRenameTo($fileName1, $fileName2) { - $this->actor->find(self::fileActionsMenuButtonForFile($fileName1), 10)->click(); - - $this->actor->find(self::renameMenuItem(), 2)->click(); - - $this->actor->find(self::renameInputForFile($fileName1), 10)->setValue($fileName2 . "\r"); - } - - /** - * @Given I mark :fileName as favorite - */ - public function iMarkAsFavorite($fileName) { - $this->iSeeThatIsNotMarkedAsFavorite($fileName); - - $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click(); - - $this->actor->find(self::addToFavoritesMenuItem(), 2)->click(); - } - - /** - * @Given I unmark :fileName as favorite - */ - public function iUnmarkAsFavorite($fileName) { - $this->iSeeThatIsMarkedAsFavorite($fileName); - - $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click(); - - $this->actor->find(self::removeFromFavoritesMenuItem(), 2)->click(); - } - - /** * @Given I share the link for :fileName */ public function iShareTheLinkFor($fileName) { - $this->actor->find(self::shareActionForFile($fileName), 10)->click(); + $this->actor->find(FileListContext::shareActionForFile(self::currentSectionMainView(), $fileName), 10)->click(); $this->actor->find(self::shareLinkCheckbox(), 5)->click(); } @@ -490,7 +291,8 @@ class FilesAppContext implements Context, ActorAwareInterface { // The shared link field always exists in the DOM (once the "Sharing" // tab is loaded), but its value is the actual shared link only when it // is visible. - if (!$this->waitForElementToBeEventuallyShown( + if (!WaitFor::elementToBeEventuallyShown( + $this->actor, self::shareLinkField(), $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { PHPUnit_Framework_Assert::fail("The shared link was not shown yet after $timeout seconds"); @@ -500,15 +302,6 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** - * @When I view :fileName in folder - */ - public function iViewInFolder($fileName) { - $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click(); - - $this->actor->find(self::viewFileInFolderMenuItem(), 2)->click(); - } - - /** * @When I check the tag :tag in the dropdown for tags in the details view */ public function iCheckTheTagInTheDropdownForTagsInTheDetailsView($tag) { @@ -527,6 +320,13 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @When I set the shared link as editable + */ + public function iSetTheSharedLinkAsEditable() { + $this->actor->find(self::allowUploadAndEditingRadioButton(), 10)->click(); + } + + /** * @When I protect the shared link with the password :password */ public function iProtectTheSharedLinkWithThePassword($password) { @@ -542,6 +342,8 @@ class FilesAppContext implements Context, ActorAwareInterface { PHPUnit_Framework_Assert::assertStringStartsWith( $this->actor->locatePath("/apps/files/"), $this->actor->getSession()->getCurrentUrl()); + + $this->setFileListAncestorForActor(self::currentSectionMainView(), $this->actor); } /** @@ -578,34 +380,6 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** - * @Then I see that the file list contains a file named :fileName - */ - public function iSeeThatTheFileListContainsAFileNamed($fileName) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForFile($fileName), 10)); - } - - /** - * @Then I see that :fileName1 precedes :fileName2 in the file list - */ - public function iSeeThatPrecedesInTheFileList($fileName1, $fileName2) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForFilePreceding($fileName1, $fileName2), 10)); - } - - /** - * @Then I see that :fileName is marked as favorite - */ - public function iSeeThatIsMarkedAsFavorite($fileName) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::favoritedStateIconForFile($fileName), 10)); - } - - /** - * @Then I see that :fileName is not marked as favorite - */ - public function iSeeThatIsNotMarkedAsFavorite($fileName) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::notFavoritedStateIconForFile($fileName), 10)); - } - - /** * @Then I see that the file name shown in the details view is :fileName */ public function iSeeThatTheFileNameShownInTheDetailsViewIs($fileName) { @@ -665,7 +439,8 @@ class FilesAppContext implements Context, ActorAwareInterface { * @When I see that the :tabName tab in the details view is eventually loaded */ public function iSeeThatTheTabInTheDetailsViewIsEventuallyLoaded($tabName) { - if (!$this->waitForElementToBeEventuallyNotShown( + if (!WaitFor::elementToBeEventuallyNotShown( + $this->actor, self::loadingIconForTabInCurrentSectionDetailsViewNamed($tabName), $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { PHPUnit_Framework_Assert::fail("The $tabName tab in the details view has not been loaded after $timeout seconds"); @@ -683,7 +458,8 @@ class FilesAppContext implements Context, ActorAwareInterface { * @Then I see that the working icon for password protect is eventually not shown */ public function iSeeThatTheWorkingIconForPasswordProtectIsEventuallyNotShown() { - if (!$this->waitForElementToBeEventuallyNotShown( + if (!WaitFor::elementToBeEventuallyNotShown( + $this->actor, self::passwordProtectWorkingIcon(), $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { PHPUnit_Framework_Assert::fail("The working icon for password protect is still shown after $timeout seconds"); @@ -700,31 +476,4 @@ class FilesAppContext implements Context, ActorAwareInterface { $this->iSeeThatTheWorkingIconForPasswordProtectIsEventuallyNotShown(); } - private function waitForElementToBeEventuallyShown($elementLocator, $timeout = 10, $timeoutStep = 1) { - $actor = $this->actor; - - $elementShownCallback = function() use ($actor, $elementLocator) { - try { - return $actor->find($elementLocator)->isVisible(); - } catch (NoSuchElementException $exception) { - return false; - } - }; - - return Utils::waitFor($elementShownCallback, $timeout, $timeoutStep); - } - - private function waitForElementToBeEventuallyNotShown($elementLocator, $timeout = 10, $timeoutStep = 1) { - $actor = $this->actor; - - $elementNotShownCallback = function() use ($actor, $elementLocator) { - try { - return !$actor->find($elementLocator)->isVisible(); - } catch (NoSuchElementException $exception) { - return true; - } - }; - - return Utils::waitFor($elementNotShownCallback, $timeout, $timeoutStep); - } } diff --git a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php index 4b7dd08c83e..4f9dabc60e6 100644 --- a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php @@ -26,6 +26,7 @@ use Behat\Behat\Context\Context; class FilesSharingAppContext implements Context, ActorAwareInterface { use ActorAware; + use FileListAncestorSetter; /** * @return Locator @@ -156,6 +157,8 @@ class FilesSharingAppContext implements Context, ActorAwareInterface { PHPUnit_Framework_Assert::assertEquals( $this->actor->getSharedNotebook()["shared link"], $this->actor->getSession()->getCurrentUrl()); + + $this->setFileListAncestorForActor(null, $this->actor); } /** @@ -182,8 +185,8 @@ class FilesSharingAppContext implements Context, ActorAwareInterface { // Unlike other menus, the Share menu is always present in the DOM, so // the element could be found when it was no made visible yet due to the // command not having been processed by the browser. - if (!$this->waitForElementToBeEventuallyShown( - self::shareMenu(), $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { + if (!WaitFor::elementToBeEventuallyShown( + $this->actor, self::shareMenu(), $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { PHPUnit_Framework_Assert::fail("The Share menu is not visible yet after $timeout seconds"); } @@ -202,18 +205,4 @@ class FilesSharingAppContext implements Context, ActorAwareInterface { PHPUnit_Framework_Assert::assertContains($text, $this->actor->find(self::textPreview(), 10)->getText()); } - private function waitForElementToBeEventuallyShown($elementLocator, $timeout = 10, $timeoutStep = 1) { - $actor = $this->actor; - - $elementShownCallback = function() use ($actor, $elementLocator) { - try { - return $actor->find($elementLocator)->isVisible(); - } catch (NoSuchElementException $exception) { - return false; - } - }; - - return Utils::waitFor($elementShownCallback, $timeout, $timeoutStep); - } - } diff --git a/tests/acceptance/features/bootstrap/SettingsMenuContext.php b/tests/acceptance/features/bootstrap/SettingsMenuContext.php index 401575c78f0..eddf2599d78 100644 --- a/tests/acceptance/features/bootstrap/SettingsMenuContext.php +++ b/tests/acceptance/features/bootstrap/SettingsMenuContext.php @@ -3,6 +3,7 @@ /** * * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) + * @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv) (skjnldsv@protonmail.com) * * @license GNU AGPL version 3 or any later version * @@ -76,6 +77,14 @@ class SettingsMenuContext implements Context, ActorAwareInterface { } /** + * @return array + */ + public function menuItems() { + return $this->actor->find(self::settingsMenu(), 10) + ->getWrappedElement()->findAll('xpath', '//a'); + } + + /** * @When I open the Settings menu */ public function iOpenTheSettingsMenu() { @@ -117,6 +126,13 @@ class SettingsMenuContext implements Context, ActorAwareInterface { } /** + * @Then I see that the Settings menu has only :items items + */ + public function iSeeThatTheSettingsMenuHasOnlyXItems($items) { + PHPUnit_Framework_Assert::assertCount(intval($items), self::menuItems()); + } + + /** * @Then I see that the :itemText item in the Settings menu is shown */ public function iSeeThatTheItemInTheSettingsMenuIsShown($itemText) { diff --git a/tests/acceptance/features/bootstrap/WaitFor.php b/tests/acceptance/features/bootstrap/WaitFor.php new file mode 100644 index 00000000000..038de3e42c2 --- /dev/null +++ b/tests/acceptance/features/bootstrap/WaitFor.php @@ -0,0 +1,78 @@ +<?php + +/** + * + * @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Helper class with common "wait for" functions. + */ +class WaitFor { + + /** + * Waits for the element to be visible. + * + * @param Actor $actor the Actor used to find the element. + * @param Locator $elementLocator the locator for the element. + * @param float $timeout the number of seconds (decimals allowed) to wait at + * most for the element to be visible. + * @param float $timeoutStep the number of seconds (decimals allowed) to + * wait before checking the visibility again. + * @return boolean true if the element is visible before (or exactly when) + * the timeout expires, false otherwise. + */ + public static function elementToBeEventuallyShown(Actor $actor, Locator $elementLocator, $timeout = 10, $timeoutStep = 1) { + $elementShownCallback = function() use ($actor, $elementLocator) { + try { + return $actor->find($elementLocator)->isVisible(); + } catch (NoSuchElementException $exception) { + return false; + } + }; + + return Utils::waitFor($elementShownCallback, $timeout, $timeoutStep); + } + + /** + * Waits for the element to be hidden (either not visible or not found in + * the DOM). + * + * @param Actor $actor the Actor used to find the element. + * @param Locator $elementLocator the locator for the element. + * @param float $timeout the number of seconds (decimals allowed) to wait at + * most for the element to be hidden. + * @param float $timeoutStep the number of seconds (decimals allowed) to + * wait before checking the visibility again. + * @return boolean true if the element is hidden before (or exactly when) + * the timeout expires, false otherwise. + */ + public static function elementToBeEventuallyNotShown(Actor $actor, Locator $elementLocator, $timeout = 10, $timeoutStep = 1) { + $elementNotShownCallback = function() use ($actor, $elementLocator) { + try { + return !$actor->find($elementLocator)->isVisible(); + } catch (NoSuchElementException $exception) { + return true; + } + }; + + return Utils::waitFor($elementNotShownCallback, $timeout, $timeoutStep); + } + +} diff --git a/tests/acceptance/features/core/Actor.php b/tests/acceptance/features/core/Actor.php index bf2f5a7367d..f47373593e9 100644 --- a/tests/acceptance/features/core/Actor.php +++ b/tests/acceptance/features/core/Actor.php @@ -61,6 +61,11 @@ class Actor { /** + * @var string + */ + private $name; + + /** * @var \Behat\Mink\Session */ private $session; @@ -83,12 +88,14 @@ class Actor { /** * Creates a new Actor. * + * @param string $name the name of the actor. * @param \Behat\Mink\Session $session the Mink Session used to control its * web browser. * @param string $baseUrl the base URL used when solving relative URLs. * @param array $sharedNotebook the notebook shared between all actors. */ - public function __construct(\Behat\Mink\Session $session, $baseUrl, &$sharedNotebook) { + public function __construct($name, \Behat\Mink\Session $session, $baseUrl, &$sharedNotebook) { + $this->name = $name; $this->session = $session; $this->baseUrl = $baseUrl; $this->sharedNotebook = &$sharedNotebook; @@ -96,6 +103,15 @@ class Actor { } /** + * Returns the name of this Actor. + * + * @return string the name of this Actor. + */ + public function getName() { + return $this->name; + } + + /** * Sets the base URL. * * @param string $baseUrl the base URL used when solving relative URLs. diff --git a/tests/acceptance/features/core/ActorContext.php b/tests/acceptance/features/core/ActorContext.php index d6fb63694ec..2cdc4b01ff1 100644 --- a/tests/acceptance/features/core/ActorContext.php +++ b/tests/acceptance/features/core/ActorContext.php @@ -135,7 +135,7 @@ class ActorContext extends RawMinkContext { $this->actors = array(); $this->sharedNotebook = array(); - $this->actors["default"] = new Actor($this->getSession(), $this->getMinkParameter("base_url"), $this->sharedNotebook); + $this->actors["default"] = new Actor("default", $this->getSession(), $this->getMinkParameter("base_url"), $this->sharedNotebook); $this->actors["default"]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); $this->currentActor = $this->actors["default"]; @@ -159,7 +159,7 @@ class ActorContext extends RawMinkContext { */ public function iActAs($actorName) { if (!array_key_exists($actorName, $this->actors)) { - $this->actors[$actorName] = new Actor($this->getSession($actorName), $this->getMinkParameter("base_url"), $this->sharedNotebook); + $this->actors[$actorName] = new Actor($actorName, $this->getSession($actorName), $this->getMinkParameter("base_url"), $this->sharedNotebook); $this->actors[$actorName]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); } diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php index 6f814921125..7d7a86149ed 100644 --- a/tests/acceptance/features/core/ElementWrapper.php +++ b/tests/acceptance/features/core/ElementWrapper.php @@ -121,7 +121,7 @@ class ElementWrapper { * @return \Behat\Mink\Element\Element the wrapped element. */ public function getWrappedElement() { - return $element; + return $this->element; } /** diff --git a/tests/acceptance/features/header.feature b/tests/acceptance/features/header.feature new file mode 100644 index 00000000000..a24c096d0e2 --- /dev/null +++ b/tests/acceptance/features/header.feature @@ -0,0 +1,74 @@ +Feature: header + + Scenario: admin users can see admin-level items in the Settings menu + Given I am logged in as the admin + When I open the Settings menu + Then I see that the Settings menu is shown + And I see that the Settings menu has only 5 items + And I see that the "Settings" item in the Settings menu is shown + And I see that the "Apps" item in the Settings menu is shown + And I see that the "Users" item in the Settings menu is shown + And I see that the "Help" item in the Settings menu is shown + And I see that the "Log out" item in the Settings menu is shown + + Scenario: normal users can see basic items in the Settings menu + Given I am logged in + When I open the Settings menu + Then I see that the Settings menu is shown + And I see that the Settings menu has only 3 items + And I see that the "Settings" item in the Settings menu is shown + And I see that the "Help" item in the Settings menu is shown + And I see that the "Log out" item in the Settings menu is shown + + Scenario: other users are seen in the contacts menu + Given I am logged in as the admin + When I open the Contacts menu + Then I see that the Contacts menu is shown + And I see that the contact "user0" in the Contacts menu is shown + And I see that the contact "admin" in the Contacts menu is not shown + + Scenario: just added users are seen in the contacts menu + Given I am logged in as the admin + And I open the User settings + And I click the New user button + And I see that the new user form is shown + And I create user user1 with password 123456acb + And I see that the list of users contains the user user1 + When I open the Contacts menu + Then I see that the Contacts menu is shown + And I see that the contact "user0" in the Contacts menu is shown + And I see that the contact "user1" in the Contacts menu is shown + And I see that the contact "admin" in the Contacts menu is not shown + + Scenario: search for other users in the contacts menu + Given I am logged in as the admin + And I open the User settings + And I click the New user button + And I see that the new user form is shown + And I create user user1 with password 123456acb + And I see that the list of users contains the user user1 + And I open the Contacts menu + And I see that the Contacts menu is shown + And I see that the contact "user0" in the Contacts menu is shown + And I see that the contact "user1" in the Contacts menu is shown + And I see that the Contacts menu search input is shown + When I search for the user "user0" + # First check that "user1" is no longer shown to ensure that the search was + # made; checking that "user0" is shown or that "admin" is not shown does not + # guarantee that (as they were already being shown and not being shown, + # respectively, before the search started). + Then I see that the contact "user1" in the Contacts menu is eventually not shown + And I see that the contact "user0" in the Contacts menu is shown + And I see that the contact "admin" in the Contacts menu is not shown + + Scenario: search for unknown users in the contacts menu + Given I am logged in as the admin + And I open the Contacts menu + And I see that the Contacts menu is shown + And I see that the contact "user0" in the Contacts menu is shown + And I see that the Contacts menu search input is shown + When I search for the user "unknownuser" + Then I see that the no results message in the Contacts menu is shown + And I see that the contact "user0" in the Contacts menu is not shown + And I see that the contact "admin" in the Contacts menu is not shown + diff --git a/tests/lib/Collaboration/Collaborators/MailPluginTest.php b/tests/lib/Collaboration/Collaborators/MailPluginTest.php index 9c4836c2eb3..775941bd440 100644 --- a/tests/lib/Collaboration/Collaborators/MailPluginTest.php +++ b/tests/lib/Collaboration/Collaborators/MailPluginTest.php @@ -32,6 +32,7 @@ use OCP\Contacts\IManager; use OCP\Federation\ICloudIdManager; use OCP\IConfig; use OCP\IGroupManager; +use OCP\IUser; use OCP\IUserSession; use OCP\Share; use Test\TestCase; @@ -98,6 +99,12 @@ class MailPluginTest extends TestCase { $this->instantiatePlugin(); + $currentUser = $this->createMock(IUser::class); + $currentUser->method('getUID') + ->willReturn('current'); + $this->userSession->method('getUser') + ->willReturn($currentUser); + $this->contactsManager->expects($this->any()) ->method('search') ->with($searchTerm, ['EMAIL', 'FN']) @@ -113,15 +120,15 @@ class MailPluginTest extends TestCase { public function dataGetEmail() { return [ - ['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, true], - ['test', [], false, ['emails' => [], 'exact' => ['emails' => []]], false, true], + ['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, false], + ['test', [], false, ['emails' => [], 'exact' => ['emails' => []]], false, false], [ 'test@remote.com', [], true, ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], false, - true, + false, ], [ // no valid email address 'test@remote', @@ -129,7 +136,7 @@ class MailPluginTest extends TestCase { true, ['emails' => [], 'exact' => ['emails' => []]], false, - true, + false, ], [ 'test@remote.com', @@ -137,7 +144,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], false, - true, + false, ], [ 'test', @@ -160,7 +167,7 @@ class MailPluginTest extends TestCase { true, ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => ['emails' => []]], false, - true, + false, ], [ 'test', @@ -183,7 +190,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => []]], false, - true, + false, ], [ 'test@remote.com', @@ -206,7 +213,7 @@ class MailPluginTest extends TestCase { true, ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], false, - true, + false, ], [ 'test@remote.com', @@ -229,7 +236,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], false, - true, + false, ], [ 'username@localhost', @@ -252,7 +259,7 @@ class MailPluginTest extends TestCase { true, ['emails' => [], 'exact' => ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]]]], true, - true, + false, ], [ 'username@localhost', @@ -275,7 +282,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]]]], true, - true, + false, ], // contact with space [ @@ -299,7 +306,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'user name@localhost']]]]], true, - true, + false, ], // remote with space, no contact [ @@ -323,7 +330,7 @@ class MailPluginTest extends TestCase { false, ['emails' => [], 'exact' => ['emails' => []]], false, - true, + false, ], // Local user found by email [ @@ -337,10 +344,96 @@ class MailPluginTest extends TestCase { ] ], false, - ['users' => [], 'exact' => ['users' => [['label' => 'User (test@example.com)','value' => ['shareType' => 0, 'shareWith' => 'test'],]]]], + ['users' => [], 'exact' => ['users' => [['label' => 'User (test@example.com)','value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test'],]]]], true, false, - ] + ], + // Current local user found by email => no result + [ + 'test@example.com', + [ + [ + 'FN' => 'User', + 'EMAIL' => ['test@example.com'], + 'CLOUD' => ['current@localhost'], + 'isLocalSystemBook' => true, + ] + ], + true, + ['exact' => []], + false, + false, + ], + // Pagination and "more results" for user matches byyyyyyy emails + [ + 'test@example', + [ + [ + 'FN' => 'User1', + 'EMAIL' => ['test@example.com'], + 'CLOUD' => ['test1@localhost'], + 'isLocalSystemBook' => true, + ], + [ + 'FN' => 'User2', + 'EMAIL' => ['test@example.de'], + 'CLOUD' => ['test2@localhost'], + 'isLocalSystemBook' => true, + ], + [ + 'FN' => 'User3', + 'EMAIL' => ['test@example.org'], + 'CLOUD' => ['test3@localhost'], + 'isLocalSystemBook' => true, + ], + [ + 'FN' => 'User4', + 'EMAIL' => ['test@example.net'], + 'CLOUD' => ['test4@localhost'], + 'isLocalSystemBook' => true, + ], + ], + true, + ['users' => [ + ['label' => 'User1 (test@example.com)', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'User2 (test@example.de)', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], 'emails' => [], 'exact' => ['users' => [], 'emails' => []]], + false, + true, + ], + // Pagination and "more results" for normal emails + [ + 'test@example', + [ + [ + 'FN' => 'User1', + 'EMAIL' => ['test@example.com'], + 'CLOUD' => ['test1@localhost'], + ], + [ + 'FN' => 'User2', + 'EMAIL' => ['test@example.de'], + 'CLOUD' => ['test2@localhost'], + ], + [ + 'FN' => 'User3', + 'EMAIL' => ['test@example.org'], + 'CLOUD' => ['test3@localhost'], + ], + [ + 'FN' => 'User4', + 'EMAIL' => ['test@example.net'], + 'CLOUD' => ['test4@localhost'], + ], + ], + true, + ['emails' => [ + ['label' => 'User1 (test@example.com)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@example.com']], + ['label' => 'User2 (test@example.de)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@example.de']], + ], 'exact' => ['emails' => []]], + false, + true, + ], ]; } @@ -422,7 +515,7 @@ class MailPluginTest extends TestCase { ], ['users' => [['label' => 'User (test@example.com)','value' => ['shareType' => 0, 'shareWith' => 'test'],]], 'emails' => [], 'exact' => ['emails' => [], 'users' => []]], false, - true, + false, [ "currentUser" => ["group1"], "User" => ["group1"] @@ -442,7 +535,7 @@ class MailPluginTest extends TestCase { ], ['emails'=> [], 'exact' => ['emails' => []]], false, - true, + false, [ "currentUser" => ["group1"], "User" => ["group2"] @@ -462,7 +555,7 @@ class MailPluginTest extends TestCase { ], ['emails' => [], 'exact' => ['emails' => [['label' => 'test@example.com', 'value' => ['shareType' => 4,'shareWith' => 'test@example.com']]]]], false, - true, + false, [ "currentUser" => ["group1"], "User" => ["group2"] |