diff options
author | Morris Jobke <hey@morrisjobke.de> | 2018-03-09 11:12:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-09 11:12:04 +0100 |
commit | 5924f9cb3089d76bf33e5d497ed79e2af9b28e7a (patch) | |
tree | 34f587ae3c885a25f1030ec60edb02912412e3a7 /tests | |
parent | ed5008597f43a28114b04dfe4706cf5d6a6b5e57 (diff) | |
parent | 15c12eb0270a35fa875e519b04d8378afc6e795d (diff) | |
download | nextcloud-server-5924f9cb3089d76bf33e5d497ed79e2af9b28e7a.tar.gz nextcloud-server-5924f9cb3089d76bf33e5d497ed79e2af9b28e7a.zip |
Merge pull request #8594 from nextcloud/add-acceptance-tests-for-permissions-on-public-shared-folders
Add acceptance tests for permissions on public shared folders
Diffstat (limited to 'tests')
-rw-r--r-- | tests/acceptance/config/behat.yml | 1 | ||||
-rw-r--r-- | tests/acceptance/features/app-files.feature | 65 | ||||
-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/WaitFor.php | 78 | ||||
-rw-r--r-- | tests/acceptance/features/core/Actor.php | 18 | ||||
-rw-r--r-- | tests/acceptance/features/core/ActorContext.php | 4 |
9 files changed, 638 insertions, 299 deletions
diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 3495769457d..ba41618b895 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -12,6 +12,7 @@ default: - AppNavigationContext - CommentsAppContext - FeatureContext + - FileListContext - FilesAppContext - FilesSharingAppContext - LoginPageContext 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/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/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); } |