diff options
author | Morris Jobke <hey@morrisjobke.de> | 2018-05-23 00:32:46 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-23 00:32:46 +0200 |
commit | a2c518ee5a3d452973052a627e15bcb3bb812dd3 (patch) | |
tree | 51732ffcac6d5c445dfd0f3773d27fbfdb36d522 /tests/acceptance | |
parent | b40bae816a5515b6420c0d82bf64eb187b2cd894 (diff) | |
parent | 760b01e8dbad054919fe766112b52d0b9cfb2d32 (diff) | |
download | nextcloud-server-a2c518ee5a3d452973052a627e15bcb3bb812dd3.tar.gz nextcloud-server-a2c518ee5a3d452973052a627e15bcb3bb812dd3.zip |
Merge pull request #8824 from nextcloud/settings-vue
Vue migration: settings
Diffstat (limited to 'tests/acceptance')
-rw-r--r-- | tests/acceptance/config/behat.yml | 2 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/AppNavigationContext.php | 64 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/AppSettingsContext.php | 102 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/DialogContext.php | 68 | ||||
-rw-r--r-- | tests/acceptance/features/bootstrap/UsersSettingsContext.php | 156 | ||||
-rw-r--r-- | tests/acceptance/features/core/ElementWrapper.php | 38 | ||||
-rw-r--r-- | tests/acceptance/features/login.feature | 1 | ||||
-rw-r--r-- | tests/acceptance/features/users.feature | 115 |
8 files changed, 522 insertions, 24 deletions
diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 7eee0a4da9c..5f1b27ffe2d 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -8,8 +8,10 @@ default: - NextcloudTestServerContext - AppNavigationContext + - AppSettingsContext - CommentsAppContext - ContactsMenuContext + - DialogContext - FeatureContext - FileListContext - FilesAppContext diff --git a/tests/acceptance/features/bootstrap/AppNavigationContext.php b/tests/acceptance/features/bootstrap/AppNavigationContext.php index 9ef71a06065..e6e6bcc8479 100644 --- a/tests/acceptance/features/bootstrap/AppNavigationContext.php +++ b/tests/acceptance/features/bootstrap/AppNavigationContext.php @@ -1,9 +1,10 @@ <?php /** - * + * * @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 * * This program is free software: you can redistribute it and/or modify @@ -32,24 +33,43 @@ class AppNavigationContext implements Context, ActorAwareInterface { */ public static function appNavigation() { return Locator::forThe()->id("app-navigation")-> - describedAs("App navigation"); + describedAs("App navigation"); } /** * @return Locator */ public static function appNavigationSectionItemFor($sectionText) { - return Locator::forThe()->xpath("//li[normalize-space() = '$sectionText']")-> - descendantOf(self::appNavigation())-> - describedAs($sectionText . " section item in App Navigation"); + return Locator::forThe()->xpath("//li/a[normalize-space() = '$sectionText']/..")-> + descendantOf(self::appNavigation())-> + describedAs($sectionText . " section item in App Navigation"); } /** * @return Locator */ public static function appNavigationCurrentSectionItem() { - return Locator::forThe()->css(".active")->descendantOf(self::appNavigation())-> - describedAs("Current section item in App Navigation"); + return Locator::forThe()->css(".active")-> + descendantOf(self::appNavigation())-> + describedAs("Current section item in App Navigation"); + } + + /** + * @return Locator + */ + public static function buttonForTheSection($class, $section) { + return Locator::forThe()->css("." . $class)-> + descendantOf(self::appNavigationSectionItemFor($section))-> + describedAs("The $class button on the $section section in App Navigation"); + } + + /** + * @return Locator + */ + public static function counterForTheSection($section) { + return Locator::forThe()->css(".app-navigation-entry-utils-counter")-> + descendantOf(self::appNavigationSectionItemFor($section))-> + describedAs("The counter for the $section section in App Navigation"); } /** @@ -60,10 +80,38 @@ class AppNavigationContext implements Context, ActorAwareInterface { } /** + * @Given I click the :class button on the :section section + */ + public function iClickTheButtonInTheSection($class, $section) { + $this->actor->find(self::buttonForTheSection($class, $section), 10)->click(); + } + + /** * @Then I see that the current section is :section */ public function iSeeThatTheCurrentSectionIs($section) { PHPUnit_Framework_Assert::assertEquals($this->actor->find(self::appNavigationCurrentSectionItem(), 10)->getText(), $section); } + /** + * @Then I see that the section :section is shown + */ + public function iSeeThatTheSectionIsShown($section) { + WaitFor::elementToBeEventuallyShown($this->actor, self::appNavigationSectionItemFor($section)); + } + + /** + * @Then I see that the section :section is not shown + */ + public function iSeeThatTheSectionIsNotShown($section) { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::appNavigationSectionItemFor($section)); + } + + /** + * @Then I see that the section :section has a count of :count + */ + public function iSeeThatTheSectionHasACountOf($section, $count) { + PHPUnit_Framework_Assert::assertEquals($this->actor->find(self::counterForTheSection($section), 10)->getText(), $count); + } + } diff --git a/tests/acceptance/features/bootstrap/AppSettingsContext.php b/tests/acceptance/features/bootstrap/AppSettingsContext.php new file mode 100644 index 00000000000..5cf54cb5280 --- /dev/null +++ b/tests/acceptance/features/bootstrap/AppSettingsContext.php @@ -0,0 +1,102 @@ +<?php + +/** + * + * @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 + * + * 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 AppSettingsContext implements Context, ActorAwareInterface { + + use ActorAware; + + /** + * @return Locator + */ + public static function appSettings() { + return Locator::forThe()->id("app-settings")-> + describedAs("App settings"); + } + /** + * @return Locator + */ + public static function appSettingsContent() { + return Locator::forThe()->id("app-settings-content")-> + descendantOf(self::appSettings())-> + describedAs("App settings"); + } + + /** + * @return Locator + */ + public static function appSettingsOpenButton() { + return Locator::forThe()->xpath("//div[@id = 'app-settings-header']/button")-> + descendantOf(self::appSettings())-> + describedAs("The button to open the app settings"); + } + + /** + * @return Locator + */ + public static function checkboxInTheSettings($id) { + return Locator::forThe()->xpath("//input[@id = '$id']")-> + descendantOf(self::appSettingsContent())-> + describedAs("The $id checkbox in the settings"); + } + + /** + * @return Locator + */ + public static function checkboxLabelInTheSettings($id) { + return Locator::forThe()->xpath("//label[@for = '$id']")-> + descendantOf(self::appSettingsContent())-> + describedAs("The label for the $id checkbox in the settings"); + } + + /** + * @Given I open the settings + */ + public function iOpenTheSettings() { + $this->actor->find(self::appSettingsOpenButton())->click(); + } + + /** + * @Given I toggle the :id checkbox in the settings + */ + public function iToggleTheCheckboxInTheSettingsTo($id) { + $locator = self::CheckboxInTheSettings($id); + + // If locator is not visible, fallback to label + if (!$this->actor->find(self::CheckboxInTheSettings($id))->isVisible()) { + $locator = self::checkboxLabelInTheSettings($id); + } + + $this->actor->find($locator)->click(); + } + + /** + * @Then I see that the settings are opened + */ + public function iSeeThatTheSettingsAreOpened() { + WaitFor::elementToBeEventuallyShown($this->actor, self::appSettingsContent()); + } + +} diff --git a/tests/acceptance/features/bootstrap/DialogContext.php b/tests/acceptance/features/bootstrap/DialogContext.php new file mode 100644 index 00000000000..8f387992b5f --- /dev/null +++ b/tests/acceptance/features/bootstrap/DialogContext.php @@ -0,0 +1,68 @@ +<?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 DialogContext implements Context, ActorAwareInterface { + + use ActorAware; + + /** + * @return Locator + */ + public static function theDialog() { + return Locator::forThe()->css(".oc-dialog")-> + describedAs("The dialog"); + } + + /** + * @return Locator + */ + public static function theDialogButton($text) { + return Locator::forThe()->xpath("//button[normalize-space() = '$text']")-> + descendantOf(self::theDialog())-> + describedAs($text . " button of the dialog"); + } + + /** + * @Given I click the :text button of the confirmation dialog + */ + public function iClickTheDialogButton($text) { + $this->actor->find(self::theDialogButton($text), 10)->click(); + } + + /** + * @Then I see that the confirmation dialog is shown + */ + public function iSeeThatTheConfirmationDialogIsShown() { + WaitFor::elementToBeEventuallyShown($this->actor, self::theDialog()); + } + + /** + * @Then I see that the confirmation dialog is not shown + */ + public function iSeeThatTheConfirmationDialogIsNotShown() { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::theDialog()); + } + +} diff --git a/tests/acceptance/features/bootstrap/UsersSettingsContext.php b/tests/acceptance/features/bootstrap/UsersSettingsContext.php index 9ddea57f413..995bbdd8725 100644 --- a/tests/acceptance/features/bootstrap/UsersSettingsContext.php +++ b/tests/acceptance/features/bootstrap/UsersSettingsContext.php @@ -1,8 +1,9 @@ <?php /** - * + * * @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 * @@ -31,7 +32,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function newUserForm() { - return Locator::forThe()->id("newuserHeader")-> + return Locator::forThe()->id("new-user")-> describedAs("New user form in Users Settings"); } @@ -63,7 +64,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function createNewUserButton() { - return Locator::forThe()->xpath("//form[@id = 'newuser']//input[@type = 'submit']")-> + return Locator::forThe()->xpath("//form[@id = 'new-user']//input[@type = 'submit']")-> describedAs("Create user button in Users Settings"); } @@ -71,24 +72,72 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function rowForUser($user) { - return Locator::forThe()->xpath("//table[@id = 'userlist']//td[normalize-space() = '$user']/..")-> + return Locator::forThe()->xpath("//div[@id='app-content']/div/div[normalize-space() = '$user']/..")-> describedAs("Row for user $user in Users Settings"); } /** + * Warning: you need to watch out for the proper classes order + * + * @return Locator + */ + public static function classCellForUser($class, $user) { + return Locator::forThe()->xpath("//*[@class='$class']")-> + descendantOf(self::rowForUser($user))-> + describedAs("$class cell for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function inputForUserInCell($cell, $user) { + return Locator::forThe()->css("input")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("$cell input for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function optionInInputForUser($cell, $user) { + return Locator::forThe()->css(".multiselect__option--highlight")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("Selected $cell option in $cell input for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function actionsMenuOf($user) { + return Locator::forThe()->css(".icon-more")-> + descendantOf(self::rowForUser($user))-> + describedAs("Actions menu for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function theAction($action, $user) { + return Locator::forThe()->xpath("//button[normalize-space() = '$action']")-> + descendantOf(self::rowForUser($user))-> + describedAs("$action action for the user $user row in Users Settings"); + } + + /** * @return Locator */ - public static function passwordCellForUser($user) { - return Locator::forThe()->css(".password")->descendantOf(self::rowForUser($user))-> - describedAs("Password cell for user $user in Users Settings"); + public static function theColumn($column) { + return Locator::forThe()->xpath("//div[@class='user-list-grid']//div[normalize-space() = '$column']")-> + describedAs("The $column column in Users Settings"); } /** * @return Locator */ - public static function passwordInputForUser($user) { - return Locator::forThe()->css("input")->descendantOf(self::passwordCellForUser($user))-> - describedAs("Password input for user $user in Users Settings"); + public static function selectedSelectOption($cell, $user) { + return Locator::forThe()->css(".multiselect__single")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("The selected option of the $cell select for the user $user in Users Settings"); } /** @@ -99,6 +148,20 @@ class UsersSettingsContext implements Context, ActorAwareInterface { } /** + * @When I click the :action action in the :user actions menu + */ + public function iClickTheAction($action, $user) { + $this->actor->find(self::theAction($action, $user))->click(); + } + + /** + * @When I open the actions menu for the user :user + */ + public function iOpenTheActionsMenuOf($user) { + $this->actor->find(self::actionsMenuOf($user))->click(); + } + + /** * @When I create user :user with password :password */ public function iCreateUserWithPassword($user, $password) { @@ -108,18 +171,40 @@ class UsersSettingsContext implements Context, ActorAwareInterface { } /** - * @When I set the password for :user to :password + * @When I set the :field for :user to :value */ - public function iSetThePasswordForUserTo($user, $password) { - $this->actor->find(self::passwordCellForUser($user), 10)->click(); - $this->actor->find(self::passwordInputForUser($user), 2)->setValue($password . "\r"); + public function iSetTheFieldForUserTo($field, $user, $value) { + $this->actor->find(self::inputForUserInCell($field, $user), 2)->setValue($value . "\r"); + } + + /** + * @When I assign the user :user to the group :group + */ + public function iAssignTheUserToTheGroup($user, $group) { + $this->actor->find(self::inputForUserInCell('groups', $user))->setValue($group); + $this->actor->find(self::optionInInputForUser('groups', $user))->click(); + } + + /** + * @When I set the user :user quota to :quota + */ + public function iSetTheUserQuotaTo($user, $quota) { + $this->actor->find(self::inputForUserInCell('quota', $user))->setValue($quota); + $this->actor->find(self::optionInInputForUser('quota', $user))->click(); } /** * @Then I see that the list of users contains the user :user */ public function iSeeThatTheListOfUsersContainsTheUser($user) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForUser($user), 10)); + WaitFor::elementToBeEventuallyShown($this->actor, self::rowForUser($user)); + } + + /** + * @Then I see that the list of users does not contains the user :user + */ + public function iSeeThatTheListOfUsersDoesNotContainsTheUser($user) { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::rowForUser($user)); } /** @@ -130,4 +215,45 @@ class UsersSettingsContext implements Context, ActorAwareInterface { $this->actor->find(self::newUserForm(), 10)->isVisible()); } + /** + * @Then I see that the :action action in the :user actions menu is shown + */ + public function iSeeTheAction($action, $user) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::theAction($action, $user), 10)->isVisible()); + } + + /** + * @Then I see that the :column column is shown + */ + public function iSeeThatTheColumnIsShown($column) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::theColumn($column), 10)->isVisible()); + } + + /** + * @Then I see that the :field of :user is :value + */ + public function iSeeThatTheFieldOfUserIs($field, $user, $value) { + PHPUnit_Framework_Assert::assertEquals( + $this->actor->find(self::inputForUserInCell($field, $user), 10)->getValue(), $value); + } + + /** + * @Then I see that the :cell cell for user :user is done loading + */ + public function iSeeThatTheCellForUserIsDoneLoading($cell, $user) { + WaitFor::elementToBeEventuallyShown($this->actor, self::classCellForUser($cell.' icon-loading-small', $user)); + WaitFor::elementToBeEventuallyNotShown($this->actor, self::classCellForUser($cell.' icon-loading-small', $user)); + } + + /** + * @Then I see that the user quota of :user is :quota + */ + public function iSeeThatTheuserQuotaIs($user, $quota) { + PHPUnit_Framework_Assert::assertEquals( + $this->actor->find(self::selectedSelectOption('quota', $user), 2)->getText(), $quota); + } + + } diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php index 7d7a86149ed..c866ea699fe 100644 --- a/tests/acceptance/features/core/ElementWrapper.php +++ b/tests/acceptance/features/core/ElementWrapper.php @@ -148,6 +148,18 @@ class ElementWrapper { } /** + * Returns whether the wrapped element is checked or not. + * + * @return bool true if the wrapped element is checked, false otherwise. + */ + public function isChecked() { + $commandCallback = function() { + return $this->element->isChecked(); + }; + return $this->executeCommand($commandCallback, "check state could not be got"); + } + + /** * Returns the text of the wrapped element. * * If the wrapped element is not visible the returned text is an empty @@ -206,6 +218,32 @@ class ElementWrapper { } /** + * Check the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + */ + public function check() { + $commandCallback = function() { + $this->element->check(); + }; + $this->executeCommand($commandCallback, "could not be checked"); + } + + /** + * uncheck the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + */ + public function uncheck() { + $commandCallback = function() { + $this->element->uncheck(); + }; + $this->executeCommand($commandCallback, "could not be unchecked"); + } + + /** * Executes the given command. * * If a StaleElementReference or a NoSuchElement exception is thrown the diff --git a/tests/acceptance/features/login.feature b/tests/acceptance/features/login.feature index 3db8c4945ec..3a31d3f88bd 100644 --- a/tests/acceptance/features/login.feature +++ b/tests/acceptance/features/login.feature @@ -18,7 +18,6 @@ Feature: login And I am logged in as the admin And I open the User settings And I set the password for user0 to 654321 - And I see that the "Password successfully changed" notification is shown And I act as John And I log in with user user0 and password 654321 Then I see that the current page is the Files app diff --git a/tests/acceptance/features/users.feature b/tests/acceptance/features/users.feature new file mode 100644 index 00000000000..9bce32a2daa --- /dev/null +++ b/tests/acceptance/features/users.feature @@ -0,0 +1,115 @@ +Feature: users + + Scenario: create a new user + Given I act as Jane + And 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 + When I create user unknownUser with password 123456acb + Then I see that the list of users contains the user unknownUser + + Scenario: delete a user + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I open the actions menu for the user user0 + And I see that the "Delete user" action in the user0 actions menu is shown + When I click the "Delete user" action in the user0 actions menu + Then I see that the list of users does not contains the user user0 + + Scenario: disable a user + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I open the actions menu for the user user0 + And I see that the "Disable user" action in the user0 actions menu is shown + When I click the "Disable user" action in the user0 actions menu + Then I see that the list of users does not contains the user user0 + When I open the "Disabled users" section + Then I see that the list of users contains the user user0 + + Scenario: assign user to a group + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # When I assign the user user0 to the group admin + # Then I see that the section Admins is shown + # And I see that the section Admins has a count of 2 + + Scenario: create and delete a group + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # And I assign the user user0 to the group Group1 + # And I see that the section Group1 is shown + # And I click the "icon-delete" button on the Group1 section + # And I see that the confirmation dialog is shown + # When I click the "Yes" button of the confirmation dialog + # Then I see that the section Group1 is not shown + + Scenario: change columns visibility + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I open the settings + And I see that the settings are opened + When I toggle the showLanguages checkbox in the settings + Then I see that the "Languages" column is shown + When I toggle the showLastLogin checkbox in the settings + Then I see that the "Last login" column is shown + When I toggle the showStoragePath checkbox in the settings + Then I see that the "Storage location" column is shown + When I toggle the showUserBackend checkbox in the settings + Then I see that the "User backend" column is shown + + Scenario: change display name + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the displayName of user0 is user0 + When I set the displayName for user0 to user1 + And I see that the displayName cell for user user0 is done loading + Then I see that the displayName of user0 is user1 + + Scenario: change password + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the password of user0 is "" + When I set the password for user0 to 123456 + And I see that the password cell for user user0 is done loading + # password input is emptied on change + Then I see that the password of user0 is "" + + Scenario: change email + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the mailAddress of user0 is "" + When I set the mailAddress for user0 to "test@nextcloud.com" + And I see that the mailAddress cell for user user0 is done loading + Then I see that the mailAddress of user0 is "test@nextcloud.com" + + Scenario: change user quota + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the user quota of user0 is Unlimited + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # When I set the user user0 quota to 1GB + # And I see that the quota cell for user user0 is done loading + # Then I see that the user quota of user0 is "1 GB"
\ No newline at end of file |