diff --git a/.travis.yml b/.travis.yml index d50caac22bde..bc880d1c246e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ env: - TC=litmus-v2 - SRV_HOST_NAME=owncloud - SRV_HOST_PORT=8889 + - REMOTE_FED_SRV_HOST_NAME=owncloud + - REMOTE_FED_SRV_HOST_PORT=8989 matrix: - DB=sqlite diff --git a/tests/travis/start_php_dev_server.sh b/tests/travis/start_php_dev_server.sh index 14d3ef85b69a..d975be5ce19a 100644 --- a/tests/travis/start_php_dev_server.sh +++ b/tests/travis/start_php_dev_server.sh @@ -51,6 +51,11 @@ fi start_php_dev_server $SRV_HOST_NAME:$SRV_HOST_PORT primary +if [ ! -z "$REMOTE_FED_SRV_HOST_NAME" ] && [ ! -z "$REMOTE_FED_SRV_HOST_PORT" ] +then + start_php_dev_server $REMOTE_FED_SRV_HOST_NAME:$REMOTE_FED_SRV_HOST_PORT REMOTE_FEDERATION +fi + if [ ! -z "$IPV4_HOST_NAME" ] && [ "$SRV_HOST_NAME" != "$IPV4_HOST_NAME" ] then start_php_dev_server $IPV4_HOST_NAME:$SRV_HOST_PORT IPv4 diff --git a/tests/travis/start_ui_tests.sh b/tests/travis/start_ui_tests.sh index 3c53cff30c30..f8068dc2630a 100755 --- a/tests/travis/start_ui_tests.sh +++ b/tests/travis/start_ui_tests.sh @@ -90,12 +90,18 @@ else fi BASE_URL="http://$SRV_HOST_NAME" +REMOTE_FED_BASE_URL=$REMOTE_FED_SRV_HOST_NAME if [ ! -z "$SRV_HOST_PORT" ] && [ "$SRV_HOST_PORT" != "80" ] then BASE_URL="$BASE_URL:$SRV_HOST_PORT" fi +if [ ! -z "$REMOTE_FED_SRV_HOST_PORT" ] && [ "$REMOTE_FED_SRV_HOST_PORT" != "80" ] +then + REMOTE_FED_BASE_URL="$REMOTE_FED_BASE_URL:$REMOTE_FED_SRV_HOST_PORT" +fi + IPV4_URL="$BASE_URL" if [ ! -z "$IPV4_HOST_NAME" ] @@ -125,6 +131,11 @@ then IPV6_URL="$IPV6_URL/$SRV_HOST_URL" fi +if [ -n "$REMOTE_FED_SRV_HOST_URL" ] +then + REMOTE_FED_BASE_URL="$REMOTE_FED_BASE_URL/$REMOTE_FED_SRV_HOST_URL" +fi + if [ "$BROWSER" == "firefox" ] then #set screen resolution so that hopefully dragable elements will be visible @@ -143,6 +154,7 @@ echo "Running tests on '$BROWSER' ($BROWSER_VERSION) on $PLATFORM" export BEHAT_PARAMS='{"extensions" : {"Behat\\MinkExtension" : {"browser_name": "'$BROWSER'", "base_url" : "'$BASE_URL'", "selenium2":{"capabilities": {"browser": "'$BROWSER'", "version": "'$BROWSER_VERSION'", "platform": "'$PLATFORM'", "name": "'$TRAVIS_REPO_SLUG' - '$TRAVIS_JOB_NUMBER'", "extra_capabilities": {'$EXTRA_CAPABILITIES'}}, "wd_host":"http://'$SAUCE_USERNAME:$SAUCE_ACCESS_KEY'@localhost:4445/wd/hub"}}}}' export IPV4_URL export IPV6_URL +export REMOTE_FED_BASE_URL lib/composer/bin/behat -c $BEHAT_YML $BEHAT_SUITE_OPTION $BEHAT_TAG_OPTION $BEHAT_TAGS $BEHAT_FEATURE -v diff --git a/tests/ui/features/bootstrap/BasicStructure.php b/tests/ui/features/bootstrap/BasicStructure.php index e3cc7f3d6367..de4101052352 100644 --- a/tests/ui/features/bootstrap/BasicStructure.php +++ b/tests/ui/features/bootstrap/BasicStructure.php @@ -386,6 +386,17 @@ public function getUserPassword($username) { return (string) $password; } + /** + * gets the base url but without "http(s)://" in front of it + * + * @return string + */ + public function getBaseUrlWithoutScheme() { + return preg_replace( + "(^https?://)", "", $this->getMinkParameter('base_url') + ); + } + /** * substitutes codes like %base_url% with the value * if the given value does not have anything to be substituted @@ -406,6 +417,21 @@ public function substituteInLineCodes($value) { "base_url" ] ], + [ + "code" => "%remote_server%", + "function" => "getenv", + "parameter" => [ + "REMOTE_FED_BASE_URL" + ] + ], + [ + "code" => "%local_server%", + "function" => [ + $this, + "getBaseUrlWithoutScheme" + ], + "parameter" => [ ] + ], [ "code" => "%regularuser%", "function" => [ diff --git a/tests/ui/features/bootstrap/FeatureContext.php b/tests/ui/features/bootstrap/FeatureContext.php index bb1f24f12ed5..0be48c568b6b 100644 --- a/tests/ui/features/bootstrap/FeatureContext.php +++ b/tests/ui/features/bootstrap/FeatureContext.php @@ -85,6 +85,31 @@ public function notificationsShouldBeDisplayedWithTheText(TableNode $table) { } } + /** + * @Then dialogs should be displayed + * @param TableNode $table of expected dialogs format must be: + * | title | content | + * @return void + */ + public function dialogsShouldBeDisplayed(TableNode $table) { + $dialogs = $this->owncloudPage->getOcDialogs(); + $dialogCounter = 0; + foreach ($table as $expectedDialog) { + PHPUnit_Framework_Assert::assertEquals( + $expectedDialog['title'], + $dialogs[$dialogCounter]->getTitle() + ); + $expectedDialog['content'] = $this->substituteInLineCodes( + $expectedDialog['content'] + ); + PHPUnit_Framework_Assert::assertEquals( + $expectedDialog['content'], + $dialogs[$dialogCounter]->getMessage() + ); + $dialogCounter++; + } + } + /** * @Then I should be redirected to a page with the title :title * @param string $title diff --git a/tests/ui/features/bootstrap/LoginContext.php b/tests/ui/features/bootstrap/LoginContext.php index 07eb5439970e..d7bc9210961e 100644 --- a/tests/ui/features/bootstrap/LoginContext.php +++ b/tests/ui/features/bootstrap/LoginContext.php @@ -66,6 +66,24 @@ public function iLoginWithUsernameAndPassword($username, $password) { $this->filesPage->waitTillPageIsLoaded($this->getSession()); } + /** + * @When I login with username :username and password :password to :server + * @param string $username + * @param string $password + * @param string $server + * @return void + */ + public function iLoginWithUsernameAndPasswordToSrv( + $username, $password, $server + ) { + $server = $this->featureContext->substituteInLineCodes($server); + $this->loginPage->setPagePath( + $server . $this->loginPage->getOriginalPath() + ); + $this->loginPage->open(); + $this->iLoginWithUsernameAndPassword($username, $password); + } + /** * @When I login with username :username and invalid password :password * @param string $username diff --git a/tests/ui/features/bootstrap/SharingContext.php b/tests/ui/features/bootstrap/SharingContext.php index 9733bf8e7e1c..430b39e9723d 100644 --- a/tests/ui/features/bootstrap/SharingContext.php +++ b/tests/ui/features/bootstrap/SharingContext.php @@ -40,6 +40,7 @@ class SharingContext extends RawMinkContext implements Context { private $regularUserNames; private $regularGroupName; private $regularGroupNames; + private $featureContext; /** * SharingContext constructor. @@ -51,17 +52,23 @@ public function __construct(FilesPage $filesPage) { } /** - * @Given the file/folder :folder is shared with the user :user + * @Given /^the (?:file|folder) "([^"]*)" is shared with the (?:(remote)\s)?user "([^"]*)"$/ * @param string $folder + * @param string $remote * @param string $user * @return void */ - public function theFileFolderIsSharedWithTheUser($folder, $user) { + public function theFileFolderIsSharedWithTheUser($folder, $remote, $user) { $this->filesPage->waitTillPageIsloaded($this->getSession()); $this->sharingDialog = $this->filesPage->openSharingDialog( $folder, $this->getSession() ); - $this->sharingDialog->shareWithUser($user, $this->getSession()); + $user = $this->featureContext->substituteInLineCodes($user); + if ($remote === "remote") { + $this->sharingDialog->shareWithRemoteUser($user, $this->getSession()); + } else { + $this->sharingDialog->shareWithUser($user, $this->getSession()); + } $this->iCloseTheShareDialog(); } @@ -124,12 +131,23 @@ public function iTypeInTheShareWithField($input) { public function theSharingPermissionsOfAreSetTo( $userName, $fileName, TableNode $permissionsTable ) { + $userName = $this->featureContext->substituteInLineCodes($userName); $this->theShareDialogForTheFileFolderIsOpen($fileName); $this->sharingDialog->setSharingPermissions( $userName, $permissionsTable->getRowsHash() ); } + /** + * @When the offered remote shares are accepted + * @return void + */ + public function theOfferedRemoteSharesAreAccepted() { + foreach (array_reverse($this->filesPage->getOcDialogs()) as $ocDialog) { + $ocDialog->accept($this->getSession()); + } + } + /** * @Then all users and groups that contain the string :requiredString in their name should be listed in the autocomplete list * @param string $requiredString @@ -270,7 +288,7 @@ public function theFileFolderShouldBeMarkedAsSharedBy( */ public function itShouldNotBePossibleToShare($name) { try { - $this->theFileFolderIsSharedWithTheUser($name, null); + $this->theFileFolderIsSharedWithTheUser($name, null, null); } catch (ElementNotFoundException $e) { PHPUnit_Framework_Assert::assertSame( 'could not find share-with-field', @@ -290,10 +308,10 @@ public function before(BeforeScenarioScope $scope) { // Get the environment $environment = $scope->getEnvironment(); // Get all the contexts you need in this context - $featureContext = $environment->getContext('FeatureContext'); - $this->regularUserNames = $featureContext->getRegularUserNames(); - $this->regularUserName = $featureContext->getRegularUserName(); - $this->regularGroupNames = $featureContext->getRegularGroupNames(); - $this->regularGroupName = $featureContext->getRegularGroupName(); + $this->featureContext = $environment->getContext('FeatureContext'); + $this->regularUserNames = $this->featureContext->getRegularUserNames(); + $this->regularUserName = $this->featureContext->getRegularUserName(); + $this->regularGroupNames = $this->featureContext->getRegularGroupNames(); + $this->regularGroupName = $this->featureContext->getRegularGroupName(); } } diff --git a/tests/ui/features/lib/FilesPageBasic.php b/tests/ui/features/lib/FilesPageBasic.php index 46fa3ad966c4..d6da324581c4 100644 --- a/tests/ui/features/lib/FilesPageBasic.php +++ b/tests/ui/features/lib/FilesPageBasic.php @@ -218,7 +218,7 @@ public function openFile($name, Session $session) { public function deleteFile($name, Session $session) { $row = $this->findFileRowByName($name, $session); $row->delete(); - $this->waitForAjaxCallsToStartAndFinish($session); + $this->waitForOutstandingAjaxCalls($session); } /** diff --git a/tests/ui/features/lib/FilesPageElement/FileRow.php b/tests/ui/features/lib/FilesPageElement/FileRow.php index 86b618f1e0ca..f361ff0a3438 100644 --- a/tests/ui/features/lib/FilesPageElement/FileRow.php +++ b/tests/ui/features/lib/FilesPageElement/FileRow.php @@ -185,6 +185,7 @@ public function rename($toName) { public function delete() { $actionMenu = $this->openFileActionsMenu(); $actionMenu->delete(); + $this->waitTillElementIsNull($this->fileBusyIndicatorXpath); } /** diff --git a/tests/ui/features/lib/FilesPageElement/SharingDialog.php b/tests/ui/features/lib/FilesPageElement/SharingDialog.php index 9b9fa1166a73..3d7382655bf6 100644 --- a/tests/ui/features/lib/FilesPageElement/SharingDialog.php +++ b/tests/ui/features/lib/FilesPageElement/SharingDialog.php @@ -45,6 +45,7 @@ class SharingDialog extends OwncloudPage { protected $autocompleteItemsTextXpath = "//*[@class='autocomplete-item-text']"; protected $shareWithCloseXpath = "/..//*[@class='close icon-close']"; protected $suffixToIdentifyGroups = " (group)"; + protected $suffixToIdentifyRemoteUsers = " (remote)"; protected $sharerInformationXpath = ".//*[@class='reshare']"; protected $sharedWithAndByRegEx = "^(?:[A-Z]\s)?Shared with you(?: and the group (.*))? by (.*)$"; protected $thumbnailContainerXpath = ".//*[contains(@class,'thumbnailContainer')]"; @@ -184,6 +185,19 @@ public function shareWithUser($name, Session $session) { return $this->shareWithUserOrGroup($name, $name, $session); } + /** + * + * @param string $name + * @param Session $session + * @throws \SensioLabs\Behat\PageObjectExtension\PageObject\Exception\ElementNotFoundException + * @return void + */ + public function shareWithRemoteUser($name, Session $session) { + return $this->shareWithUserOrGroup( + $name, $name . $this->suffixToIdentifyRemoteUsers, $session + ); + } + /** * * @param string $name diff --git a/tests/ui/features/lib/OwncloudPage.php b/tests/ui/features/lib/OwncloudPage.php index 6839ae384207..56754af0b488 100644 --- a/tests/ui/features/lib/OwncloudPage.php +++ b/tests/ui/features/lib/OwncloudPage.php @@ -28,13 +28,22 @@ use Behat\Mink\Element\NodeElement; use WebDriver\Exception as WebDriverException; use WebDriver\Key; +use Page\OwncloudPageElement\OCDialog; /** * Owncloud page. */ class OwncloudPage extends Page { - protected $userNameDispayId = "expandDisplayName"; + protected $userNameDisplayId = "expandDisplayName"; + protected $ocDialogXpath = ".//*[@class='oc-dialog']"; + + /** + * used to store the unchanged path string when $path gets changed + * + * @var string + */ + protected $originalPath = null; /** * @param Session $session @@ -138,13 +147,33 @@ public function getNotifications() { return $notificationsText; } + /** + * Get all open oc dialogs + * + * @return OCDialog[] + */ + public function getOcDialogs() { + $ocDialogs = []; + $ocDialogElements = $this->findAll("xpath", $this->ocDialogXpath); + foreach ($ocDialogElements as $element) { + /** + * + * @var \Page\OwncloudPageElement\OCDialog $ocDialog + */ + $ocDialog = $this->getPage("OwncloudPageElement\\OCDialog"); + $ocDialog->setElement($element); + $ocDialogs[] = $ocDialog; + } + return $ocDialogs; + } + /** * Open the settings menu * * @return Page */ public function openSettingsMenu() { - $this->findById($this->userNameDispayId)->click(); + $this->findById($this->userNameDisplayId)->click(); return $this->getPage("OwncloudPageElement\\SettingsMenu"); } /** @@ -153,7 +182,7 @@ public function openSettingsMenu() { * @return string */ public function getMyUsername() { - return $this->findById($this->userNameDispayId)->getText(); + return $this->findById($this->userNameDisplayId)->getText(); } /** @@ -165,6 +194,31 @@ public function getPagePath() { return $this->getPath(); } + /** + * + * @param string $path + * @return void + */ + public function setPagePath($path) { + if ($this->originalPath === null) { + $this->originalPath = $this->path; + } + $this->path = $path; + } + + /** + * returns the unchanged path + * + * @return string + */ + public function getOriginalPath() { + if ($this->originalPath !== null) { + return $this->originalPath; + } else { + return $this->getPath(); + } + } + /** * Gets the Coordinates of a Mink Element * diff --git a/tests/ui/features/lib/OwncloudPageElement/OCDialog.php b/tests/ui/features/lib/OwncloudPageElement/OCDialog.php new file mode 100644 index 000000000000..66d04d6f84cf --- /dev/null +++ b/tests/ui/features/lib/OwncloudPageElement/OCDialog.php @@ -0,0 +1,107 @@ + + * @copyright 2017 Artur Neumann artur@jankaritech.com + * + * This code 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 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 + * + */ + +namespace Page\OwncloudPageElement; + +use Behat\Mink\Element\NodeElement; +use Page\OwncloudPage; +use Behat\Mink\Session; +use SensioLabs\Behat\PageObjectExtension\PageObject\Exception\ElementNotFoundException; + +/** + * The oc-dialog + * + */ +class OCDialog extends OwncloudPage { + + /** + * @var NodeElement of this element + */ + protected $dialogElement; + protected $titleClassXpath = ".//*[@class='oc-dialog-title']"; + protected $contentClassXpath = ".//*[@class='oc-dialog-content']"; + /** + * the accept button, regardless of the label + * + * @var string + */ + protected $primaryButtonXpath = "//button[@class='primary']"; + + /** + * sets the NodeElement for the current file row + * a little bit like __construct() but as we access this "sub-page-object" + * from an other Page Object by $this->getPage("OwncloudPageElement\\OCDialog") + * there is no real __construct() that can take arguments + * + * @param \Behat\Mink\Element\NodeElement $dialogElement + * @return void + */ + public function setElement(NodeElement $dialogElement) { + $this->dialogElement = $dialogElement; + } + + /** + * + * @throws ElementNotFoundException + * @return string + */ + public function getTitle() { + $title = $this->dialogElement->find("xpath", $this->titleClassXpath); + if (is_null($title)) { + throw new ElementNotFoundException("could not find title"); + } + return $title->getText(); + } + + /** + * + * @throws ElementNotFoundException + * @return string + */ + public function getMessage() { + $contentElement = $this->dialogElement->find("xpath", $this->contentClassXpath); + if (is_null($contentElement)) { + throw new ElementNotFoundException("could not find content element"); + } + return $contentElement->getText(); + } + + /** + * clicks the accept (primary) button + * + * @param Session $session + * @throws ElementNotFoundException + * @return void + */ + public function accept(Session $session) { + $primaryButton = $this->dialogElement->find( + "xpath", $this->primaryButtonXpath + ); + if (is_null($primaryButton)) { + throw new ElementNotFoundException("could not find primary button"); + } + $primaryButton->click(); + $this->waitForOutstandingAjaxCalls($session); + } +} + \ No newline at end of file diff --git a/tests/ui/features/other/federationSharing.feature b/tests/ui/features/other/federationSharing.feature new file mode 100644 index 000000000000..24145a10d7c3 --- /dev/null +++ b/tests/ui/features/other/federationSharing.feature @@ -0,0 +1,51 @@ +Feature: FederationSharing + + Background: + Given these users exist: + |username|password|displayname|email | + |user1 |1234 |User One |u1@oc.com.np| + |user2 |1234 |User Two |u2@oc.com.np| + And I am on the login page + And I login with username "user1" and password "1234" + And I logout + And I login with username "user2" and password "1234" + + Scenario: test the single steps of federation sharing + When the folder "simple-folder" is shared with the remote user "user1@%remote_server%" + And the folder "simple-empty-folder" is shared with the remote user "user1@%remote_server%" + And I logout + And I login with username "user1" and password "1234" to "http://%remote_server%" + Then dialogs should be displayed + | title | content | + | Remote share | Do you want to add the remote share /simple-folder from user2@%local_server%/? | + | Remote share | Do you want to add the remote share /simple-empty-folder from user2@%local_server%/? | + When the offered remote shares are accepted + Then the folder "simple-folder (2)" should be listed + When I open the folder "simple-folder (2)" + Then the file "lorem.txt" should be listed + + Scenario: share a folder with an remote user and prohibit deleting + When the folder "simple-folder" is shared with the remote user "user1@%remote_server%" + And the sharing permissions of "user1@%remote_server% (remote)" for "simple-folder" are set to + | delete | no | + And I logout + And I login with username "user1" and password "1234" to "http://%remote_server%" + And the offered remote shares are accepted + And I open the folder "simple-folder (2)" + Then it should not be possible to delete the file "lorem.txt" + + Scenario: rename and delete files in a received share + When the folder "simple-folder" is shared with the remote user "user1@%remote_server%" + And I logout + And I login with username "user1" and password "1234" to "http://%remote_server%" + And the offered remote shares are accepted + And I open the folder "simple-folder (2)" + And I rename the file "lorem.txt" to "renamed file.txt" + And I delete the file "data.zip" + And I logout + And I login with username "user2" and password "1234" to "%base_url%" + And I open the folder "simple-folder" + Then the file "renamed file.txt" should be listed + But the file "lorem.txt" should not be listed + And the file "data.zip" should not be listed + \ No newline at end of file