From 4c4e4ec991f35c03a0937338f8e4fe0af2af1044 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sat, 21 Oct 2023 03:23:44 +0200 Subject: [PATCH] chore(tests): Migrate login acceptance tests from behat to Cypress Signed-off-by: Ferdinand Thiessen --- .drone.yml | 30 ---- cypress/e2e/login/login.cy.ts | 146 +++++++++++++++++ tests/acceptance/config/behat.yml | 2 - .../features/bootstrap/LoginPageContext.php | 149 ------------------ tests/acceptance/features/login.feature | 55 ------- 5 files changed, 146 insertions(+), 236 deletions(-) create mode 100644 cypress/e2e/login/login.cy.ts delete mode 100644 tests/acceptance/features/bootstrap/LoginPageContext.php delete mode 100644 tests/acceptance/features/login.feature diff --git a/.drone.yml b/.drone.yml index 914c3c74e7637..c768144298fa7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1621,36 +1621,6 @@ trigger: - pull_request - push ---- -kind: pipeline -name: acceptance-login - -steps: -- name: submodules - image: ghcr.io/nextcloud/continuous-integration-alpine-git:latest - commands: - - git submodule update --init -- name: acceptance-login - image: ghcr.io/nextcloud/continuous-integration-acceptance-php8.0:latest - commands: - - tests/acceptance/run-local.sh --timeout-multiplier 10 --nextcloud-server-domain acceptance-login --selenium-server selenium:4444 allow-git-repository-modifications features/login.feature - -services: -- name: selenium - image: ghcr.io/nextcloud/continuous-integration-selenium:3.141.59 - environment: - # Reduce default log level for Selenium server (INFO) as it is too - # verbose. - JAVA_OPTS: -Dselenium.LOGGER.level=WARNING - -trigger: - branch: - - master - - stable* - event: - - pull_request - - push - --- kind: pipeline name: acceptance-users diff --git a/cypress/e2e/login/login.cy.ts b/cypress/e2e/login/login.cy.ts new file mode 100644 index 0000000000000..1383d803ad0d1 --- /dev/null +++ b/cypress/e2e/login/login.cy.ts @@ -0,0 +1,146 @@ +import type { User } from '@nextcloud/cypress' + +describe('Login', () => { + let user: User + let disabledUser: User + + after(() => cy.deleteUser(user)) + before(() => { + // disable brute force protection + cy.runOccCommand('config:system:set auth.bruteforce.protection.enabled --value false --type bool') + cy.createRandomUser().then(($user) => { + user = $user + }) + cy.createRandomUser().then(($user) => { + disabledUser = $user + cy.runOccCommand(`user:disable '${disabledUser.userId}'`) + }) + }) + + beforeEach(() => { + cy.logout() + }) + + it('log in with valid user and password', () => { + // Given I visit the Home page + cy.visit('/') + // I see the login page + cy.get('form[name="login"]').should('be.visible') + // I log in with a valid user + cy.get('form[name="login"]').within(() => { + cy.get('input[name="user"]').type(user.userId) + cy.get('input[name="password"]').type(user.password) + cy.contains('button[data-login-form-submit]', 'Log in').click() + }) + + // see that the login is done + cy.get('[data-login-form-submit]').if().should('not.contain', 'Logging in') + + // Then I see that the current page is the Files app + cy.url().should('match', /apps\/dashboard(\/|$)/) + }) + + it('try to log in with valid user and invalid password', () => { + // Given I visit the Home page + cy.visit('/') + // I see the login page + cy.get('form[name="login"]').should('be.visible') + // I log in with a valid user but invalid password + cy.get('form[name="login"]').within(() => { + cy.get('input[name="user"]').type(user.userId) + cy.get('input[name="password"]').type(`${user.password}--wrong`) + cy.contains('button', 'Log in').click() + }) + + // see that the login is done + cy.get('[data-login-form-submit]').if().should('not.contain', 'Logging in') + + // Then I see that the current page is the Login page + cy.url().should('match', /\/login/) + // And I see that a wrong password message is shown + cy.get('form[name="login"]').then(($el) => expect($el.text()).to.match(/Wrong.+password/i)) + cy.get('input[name="password"]:invalid').should('exist') + }) + + it('try to log in with valid user and invalid password', () => { + // Given I visit the Home page + cy.visit('/') + // I see the login page + cy.get('form[name="login"]').should('be.visible') + // I log in with a valid user but invalid password + cy.get('form[name="login"]').within(() => { + cy.get('input[name="user"]').type(user.userId) + cy.get('input[name="password"]').type(`${user.password}--wrong`) + cy.contains('button', 'Log in').click() + }) + + // see that the login is done + cy.get('[data-login-form-submit]').if().should('not.contain', 'Logging in') + + // Then I see that the current page is the Login page + cy.url().should('match', /\/login/) + // And I see that a wrong password message is shown + cy.get('form[name="login"]').then(($el) => expect($el.text()).to.match(/Wrong.+password/i).and.to.match(/Wrong.+username/)) + cy.get('input[name="password"]:invalid').should('exist') + }) + + it('try to log in with invalid user', () => { + // Given I visit the Home page + cy.visit('/') + // I see the login page + cy.get('form[name="login"]').should('be.visible') + // I log in with an invalid user but valid password + cy.get('form[name="login"]').within(() => { + cy.get('input[name="user"]').type(`${user.userId}--wrong`) + cy.get('input[name="password"]').type(user.password) + cy.contains('button', 'Log in').click() + }) + + // see that the login is done + cy.get('[data-login-form-submit]').if().should('not.contain', 'Logging in') + + // Then I see that the current page is the Login page + cy.url().should('match', /\/login/) + // And I see that a wrong password message is shown + cy.get('form[name="login"]').then(($el) => expect($el.text()).to.match(/Wrong.+password/i).and.to.match(/Wrong.+username/)) + cy.get('input[name="password"]:invalid').should('exist') + }) + + it('try to log in as disabled user', () => { + // Given I visit the Home page + cy.visit('/') + // I see the login page + cy.get('form[name="login"]').should('be.visible') + // When I log in with user disabledUser and password + cy.get('form[name="login"]').within(() => { + cy.get('input[name="user"]').type(disabledUser.userId) + cy.get('input[name="password"]').type(disabledUser.password) + cy.contains('button', 'Log in').click() + }) + + // see that the login is done + cy.get('[data-login-form-submit]').if().should('not.contain', 'Logging in') + + // Then I see that the current page is the Login page + cy.url().should('match', /\/login/) + // And I see that the disabled user message is shown + cy.get('form[name="login"]').then(($el) => expect($el.text()).to.match(/User.+disabled/i)) + cy.get('input[name="password"]:invalid').should('exist') + }) + + it('try to logout', () => { + cy.login(user) + + // Given I visit the Home page + cy.visit('/') + // I see the dashboard + cy.url().should('match', /apps\/dashboard(\/|$)/) + + // When click logout + cy.get('#user-menu button').should('exist').click() + cy.get('#logout a').should('contain.text', 'Log out').click() + + // Then I see that the current page is the Login page + cy.url().should('match', /\/login/) + }) +}) diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index a0aff969bae9c..9a789a2208b44 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -17,7 +17,6 @@ default: - FileListContext - FilesAppContext - FilesAppSharingContext - - LoginPageContext - NotificationsContext - PublicShareContext - SearchContext @@ -46,7 +45,6 @@ default: - FileListContext - FilesAppContext - FilesAppSharingContext - - LoginPageContext - NotificationsContext - PublicShareContext - SearchContext diff --git a/tests/acceptance/features/bootstrap/LoginPageContext.php b/tests/acceptance/features/bootstrap/LoginPageContext.php deleted file mode 100644 index fc924bbff80b0..0000000000000 --- a/tests/acceptance/features/bootstrap/LoginPageContext.php +++ /dev/null @@ -1,149 +0,0 @@ -. - * - */ - -use Behat\Behat\Context\Context; -use Behat\Behat\Hook\Scope\BeforeScenarioScope; -use PHPUnit\Framework\Assert; - -class LoginPageContext implements Context, ActorAwareInterface { - use ActorAware; - - /** - * @var FeatureContext - */ - private $featureContext; - - /** - * @var FilesAppContext - */ - private $filesAppContext; - - public static function userNameField(): Locator { - return Locator::forThe()->field("user")-> - describedAs("User name field in Login page"); - } - - public static function passwordField(): Locator { - return Locator::forThe()->field("password")-> - describedAs("Password field in Login page"); - } - - public static function loginButton(): Locator { - return Locator::forThe()->css(".button-vue[type='submit']")-> - describedAs("Login button in Login page"); - } - - public static function wrongPasswordMessage(): Locator { - return Locator::forThe()->xpath("//*[@class = 'input-field__helper-text-message input-field__helper-text-message--error' and normalize-space() = 'Wrong username or password.']")-> - describedAs("Wrong password message in Login page"); - } - - /** - * @return Locator - */ - public static function userDisabledMessage() { - return Locator::forThe()->xpath("//*[@class = 'input-field__helper-text-message input-field__helper-text-message--error' and normalize-space() = 'User disabled']")-> - describedAs('User disabled message on login page'); - } - - /** - * @When I log in with user :user and password :password - */ - public function iLogInWithUserAndPassword(string $user, string $password): void { - $this->actor->find(self::userNameField(), 10)->setValue($user); - $this->actor->find(self::passwordField())->setValue($password); - $this->actor->find(self::loginButton())->click(); - } - - /** - * @Then I see that the current page is the Login page - */ - public function iSeeThatTheCurrentPageIsTheLoginPage() { - Assert::assertStringStartsWith( - $this->actor->locatePath("/login"), - $this->actor->getSession()->getCurrentUrl()); - } - - /** - * @Then I see that a wrong password message is shown - */ - public function iSeeThatAWrongPasswordMessageIsShown() { - Assert::assertTrue( - $this->actor->find(self::wrongPasswordMessage(), 10)->isVisible()); - } - - /** - * @Then I see that the disabled user message is shown - */ - public function iSeeThatTheDisabledUserMessageIsShown() { - Assert::assertTrue( - $this->actor->find(self::userDisabledMessage(), 10)->isVisible()); - } - - /** - * @BeforeScenario - */ - public function getOtherRequiredSiblingContexts(BeforeScenarioScope $scope) { - $environment = $scope->getEnvironment(); - - $this->featureContext = $environment->getContext("FeatureContext"); - $this->filesAppContext = $environment->getContext("FilesAppContext"); - } - - /** - * @Given I am logged in - */ - public function iAmLoggedIn() { - $this->featureContext->iVisitTheHomePage(); - $this->iLogInWithUserAndPassword("user0", "123456acb"); - $this->filesAppContext->iSeeThatTheCurrentPageIsTheFilesApp(); - } - - /** - * @Given I am logged in as :userName - */ - public function iAmLoggedInAs($userName) { - $this->featureContext->iVisitTheHomePage(); - $this->iLogInWithUserAndPassword($userName, "123456acb"); - $this->filesAppContext->iSeeThatTheCurrentPageIsTheFilesApp(); - } - - /** - * @Given I am logged in as the admin - */ - public function iAmLoggedInAsTheAdmin() { - $this->featureContext->iVisitTheHomePage(); - $this->iLogInWithUserAndPassword("admin", "admin"); - $this->filesAppContext->iSeeThatTheCurrentPageIsTheFilesApp(); - } - - /** - * @Given I can not log in with user :user and password :password - */ - public function iCanNotLogInWithUserAndPassword($user, $password) { - $this->featureContext->iVisitTheHomePage(); - $this->iLogInWithUserAndPassword($user, $password); - $this->iSeeThatTheCurrentPageIsTheLoginPage(); - $this->iSeeThatAWrongPasswordMessageIsShown(); - } -} diff --git a/tests/acceptance/features/login.feature b/tests/acceptance/features/login.feature deleted file mode 100644 index 1022ec26aec3f..0000000000000 --- a/tests/acceptance/features/login.feature +++ /dev/null @@ -1,55 +0,0 @@ -@apache -Feature: login - - Scenario: log in with valid user and password - Given I visit the Home page - When I log in with user user0 and password 123456acb - Then I see that the current page is the Files app - - Scenario: try to log in with valid user and invalid password - Given I visit the Home page - When I log in with user user0 and password 654321 - Then I see that the current page is the Login page - And I see that a wrong password message is shown - -# Scenario: log in with valid user and invalid password once fixed by admin -# Given I act as John -# And I can not log in with user user0 and password 654231 -# When I act as Jane -# 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 act as John -# And I log in with user user0 and password 654321 -# Then I see that the current page is the Files app - - Scenario: try to log in with invalid user - Given I visit the Home page - When I log in with user unknownUser and password 123456acb - Then I see that the current page is the Login page - And I see that a wrong password message is shown - - Scenario: try to log in as disabled user - Given I visit the Home page - When I log in with user disabledUser and password 123456acb - Then I see that the current page is the Login page - And I see that the disabled user message is shown - - Scenario: log in with invalid user once fixed by admin - Given I act as John - And I can not log in with user unknownUser and password 123456acb - When 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 - And I create user unknownUser with password 123456acb - # And I see that the list of users contains the user unknownUser - And I act as John - And I log in with user unknownUser and password 123456acb - Then I see that the current page is the Files app - - Scenario: log out - Given I am logged in - When I log out - Then I see that the current page is the Login page