diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78a7a8074..de45a35aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -274,7 +274,7 @@ jobs: run: docker run --rm gradido/admin:test yarn run lint ############################################################################## - # JOB: STYLELINT ADMIN INTERFACE ############################################## + # JOB: STYLELINT ADMIN INTERFACE ############################################# ############################################################################## stylelint_admin: name: Stylelint - Admin Interface @@ -577,7 +577,7 @@ jobs: end-to-end-tests: name: End-to-End Tests runs-on: ubuntu-latest - needs: [build_test_mariadb, build_test_database_up, build_test_backend, build_test_admin, build_test_frontend, build_test_nginx] + needs: [build_test_mariadb, build_test_database_up, build_test_admin, build_test_frontend, build_test_nginx] steps: ########################################################################## # CHECKOUT CODE ########################################################## @@ -601,13 +601,6 @@ jobs: path: /tmp - name: Load Docker Image (Database Up) run: docker load < /tmp/database_up.tar - - name: Download Docker Image (Backend) - uses: actions/download-artifact@v3 - with: - name: docker-backend-test - path: /tmp - - name: Load Docker Image (Backend) - run: docker load < /tmp/backend.tar - name: Download Docker Image (Frontend) uses: actions/download-artifact@v3 with: @@ -640,7 +633,11 @@ jobs: run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database - name: Boot up test system | docker-compose backend - run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend + run: | + cd backend + cp .env.test_e2e .env + cd .. + docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend - name: Sleep for 10 seconds run: sleep 10s @@ -657,6 +654,9 @@ jobs: - name: Boot up test system | docker-compose frontends run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx + - name: Boot up test system | docker-compose mailserver + run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver + - name: Sleep for 15 seconds run: sleep 15s @@ -666,12 +666,12 @@ jobs: - name: End-to-end tests | run tests id: e2e-tests run: | - cd e2e-tests/cypress/tests/ + cd e2e-tests/ yarn - yarn run cypress run --spec cypress/e2e/User.Authentication.feature + yarn run cypress run --spec cypress/e2e/User.Authentication.feature,cypress/e2e/User.Authentication.ResetPassword.feature - name: End-to-end tests | if tests failed, upload screenshots - if: steps.e2e-tests.outcome == 'failure' + if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} uses: actions/upload-artifact@v3 with: name: cypress-screenshots - path: /home/runner/work/gradido/gradido/e2e-tests/cypress/tests/cypress/screenshots/ + path: /home/runner/work/gradido/gradido/e2e-tests/cypress/screenshots/ diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e new file mode 100644 index 000000000..a5cdc4bfd --- /dev/null +++ b/backend/.env.test_e2e @@ -0,0 +1,9 @@ +# Server +JWT_EXPIRES_IN=1m + +# Email +EMAIL=true +EMAIL_TEST_MODUS=false +EMAIL_TLS=false +# for testing password reset +EMAIL_CODE_REQUEST_TIME=1 diff --git a/e2e-tests/cypress/tests/.eslintignore b/e2e-tests/.eslintignore similarity index 100% rename from e2e-tests/cypress/tests/.eslintignore rename to e2e-tests/.eslintignore diff --git a/e2e-tests/.eslintrc.js b/e2e-tests/.eslintrc.js new file mode 100644 index 000000000..98f13d176 --- /dev/null +++ b/e2e-tests/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + root: true, + env: { + node: true, + cypress: true, + }, + parser: '@typescript-eslint/parser', + plugins: ['cypress', 'prettier', '@typescript-eslint' /*, 'jest' */], + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:@typescript-eslint/recommended', + ], + // add your custom rules here + rules: { + 'no-console': ['error'], + 'no-debugger': 'error', + 'prettier/prettier': [ + 'error', + { + htmlWhitespaceSensitivity: 'ignore', + }, + ], + }, +} diff --git a/e2e-tests/.gitignore b/e2e-tests/.gitignore new file mode 100644 index 000000000..dbf8bdede --- /dev/null +++ b/e2e-tests/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +cypress/screenshots/ +cypress/videos/ +cucumber-messages.ndjson + diff --git a/e2e-tests/.prettierrc.js b/e2e-tests/.prettierrc.js new file mode 100644 index 000000000..bc1d767d7 --- /dev/null +++ b/e2e-tests/.prettierrc.js @@ -0,0 +1,9 @@ +module.exports = { + semi: false, + printWidth: 100, + singleQuote: true, + trailingComma: "all", + tabWidth: 2, + bracketSpacing: true, + endOfLine: "auto", +}; diff --git a/e2e-tests/cypress/Dockerfile b/e2e-tests/Dockerfile similarity index 97% rename from e2e-tests/cypress/Dockerfile rename to e2e-tests/Dockerfile index 8c8e00da8..1a520572e 100644 --- a/e2e-tests/cypress/Dockerfile +++ b/e2e-tests/Dockerfile @@ -11,7 +11,7 @@ ############################################################################### FROM cypress/base:16.14.2-slim -ARG DOCKER_WORKDIR=/tests/ +ARG DOCKER_WORKDIR="/tests" WORKDIR $DOCKER_WORKDIR # install dependencies diff --git a/e2e-tests/README.md b/e2e-tests/README.md index f53618ab4..67016e90e 100644 --- a/e2e-tests/README.md +++ b/e2e-tests/README.md @@ -1,7 +1,73 @@ -# Gradido end-to-end tests +# Gradido End-to-End Testing with [Cypress](https://www.cypress.io/) (CI-ready via Docker) -This is still WIP. +A setup to show-case Cypress as an end-to-end testing tool for Gradido running in a Docker container. +The tests are organized in feature files written in Gherkin syntax. -For automated end-to-end testing one of the frameworks Cypress or Playwright will be utilized. -For more details on how to run them, see the subfolders' README instructions. +## Features under test + +So far these features are initially tested +- [User authentication](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature) +- [User profile - change password](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature) +- [User registration]((https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature)) (WIP) + + +## Precondition + +Before running the tests, change to the repo's root directory (gradido). + +### Boot up the system under test + +```bash +docker-compose up +``` + +### Seed the database + +The database has to be seeded upfront to every test run. + +```bash +# change to the backend directory +cd /path/to/gradido/gradido/backend + +# install all dependencies +yarn + +# seed the database (everytime before running the tests) +yarn seed +``` + +## Execute the test + +This setup will be integrated in the Gradido Github Actions to automatically support the CI/CD process. +For now the test setup can only be used locally in two modes. + +### Run Cypress directly from the code + +```bash +# change to the tests directory +cd /path/to/gradido/e2e-tests/ + +# install all dependencies +yarn install + +# a) run the tests on command line +yarn cypress run + +# b) open the Cypress GUI to run the tests in interactive mode +yarn cypress open +``` + + +### Run Cyprss from a separate Docker container + +```bash +# change to the cypress directory +cd /path/to/gradido/e2e-tests/ + +# build a Docker image from the Dockerfile +docker build -t gradido_e2e-tests-cypress . + +# run the Docker image and execute the given tests +docker run -it --network=host gradido_e2e-tests-cypress yarn cypress-e2e +``` diff --git a/e2e-tests/cypress.config.ts b/e2e-tests/cypress.config.ts new file mode 100644 index 000000000..16ebb0e97 --- /dev/null +++ b/e2e-tests/cypress.config.ts @@ -0,0 +1,79 @@ +import { defineConfig } from 'cypress' +import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor' +import browserify from '@badeball/cypress-cucumber-preprocessor/browserify' + +let resetPasswordLink: string + +async function setupNodeEvents( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions +): Promise { + await addCucumberPreprocessorPlugin(on, config) + + on( + 'file:preprocessor', + browserify(config, { + typescript: require.resolve('typescript'), + }) + ) + + on('task', { + setResetPasswordLink: (val) => { + return (resetPasswordLink = val) + }, + getResetPasswordLink: () => { + return resetPasswordLink + }, + }) + + on('after:run', (results) => { + if (results) { + // results will be undefined in interactive mode + // eslint-disable-next-line no-console + console.log(results.status) + } + }) + + return config +} + +export default defineConfig({ + e2e: { + specPattern: '**/*.feature', + excludeSpecPattern: '*.js', + experimentalSessionAndOrigin: true, + baseUrl: 'http://localhost:3000', + chromeWebSecurity: false, + defaultCommandTimeout: 10000, + supportFile: 'cypress/support/index.ts', + viewportHeight: 720, + viewportWidth: 1280, + video: false, + retries: { + runMode: 2, + openMode: 0, + }, + env: { + backendURL: 'http://localhost:4000', + mailserverURL: 'http://localhost:1080', + loginQuery: `query ($email: String!, $password: String!, $publisherId: Int) { + login(email: $email, password: $password, publisherId: $publisherId) { + email + firstName + lastName + language + klickTipp { + newsletterState + __typename + } + hasElopage + publisherId + isAdmin + creation + __typename + } + }`, + }, + setupNodeEvents, + }, +}) diff --git a/e2e-tests/cypress/.gitignore b/e2e-tests/cypress/.gitignore deleted file mode 100644 index 7dfc547b4..000000000 --- a/e2e-tests/cypress/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -tests/node_modules/ -tests/cypress/screenshots/ -tests/cypress/videos/ -tests/cucumber-messages.ndjson diff --git a/e2e-tests/cypress/README.md b/e2e-tests/cypress/README.md deleted file mode 100644 index 4ec1ebe51..000000000 --- a/e2e-tests/cypress/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Gradido End-to-End Testing with [Cypress](https://www.cypress.io/) (CI-ready via Docker) - -A setup to show-case Cypress as an end-to-end testing tool for Gradido running in a Docker container. -The tests are organized in feature files written in Gherkin syntax. - - -## Features under test - -So far these features are initially tested -- [User authentication](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature) -- [User profile - change password](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature) -- [User registration]((https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature)) (WIP) - - -## Precondition - -Before running the tests, change to the repo's root directory (gradido). - -### Boot up the system under test - -```bash -docker-compose up -``` - -### Seed the database - -The database has to be seeded upfront to every test run. - -```bash -# change to the backend directory -cd /path/to/gradido/gradido/backend - -# install all dependencies -yarn - -# seed the database (everytime before running the tests) -yarn seed -``` - -## Execute the test - -This setup will be integrated in the Gradido Github Actions to automatically support the CI/CD process. -For now the test setup can only be used locally in two modes. - -### Run Cypress directly from the code - -```bash -# change to the tests directory -cd /path/to/gradido/e2e-tests/cypress/tests - -# install all dependencies -yarn install - -# a) run the tests on command line -yarn cypress run - -# b) open the Cypress GUI to run the tests in interactive mode -yarn cypress open -``` - - -### Run Cyprss from a separate Docker container - -```bash -# change to the cypress directory -cd /path/to/gradido/e2e-tests/cypress/ - -# build a Docker image from the Dockerfile -docker build -t gradido_e2e-tests-cypress . - -# run the Docker image and execute the given tests -docker run -it --network=host gradido_e2e-tests-cypress yarn cypress-e2e -``` diff --git a/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature b/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature new file mode 100644 index 000000000..55ca87215 --- /dev/null +++ b/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature @@ -0,0 +1,25 @@ +Feature: User Authentication - reset password + As a user + I want to reset my password from the sign in page + + # TODO for these pre-conditions utilize seeding or API check, if user exists in test system + # Background: + # Given the following "users" are in the database: + # | email | password | name | + # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | + + Scenario: Reset password from signin page successfully + Given the user navigates to page "/login" + And the user navigates to the forgot password page + When the user enters the e-mail address "bibi@bloxberg.de" + And the user submits the e-mail form + Then the user receives an e-mail containing the password reset link + When the user opens the password reset link in the browser + And the user enters the password "12345Aa_" + And the user repeats the password "12345Aa_" + And the user submits the password form + And the user clicks the sign in button + Then the user submits the credentials "bibi@bloxberg.de" "Aa12345_" + And the user cannot login + But the user submits the credentials "bibi@bloxberg.de" "12345Aa_" + And the user is logged in with username "Bibi Bloxberg" diff --git a/e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature b/e2e-tests/cypress/e2e/User.Authentication.feature similarity index 93% rename from e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature rename to e2e-tests/cypress/e2e/User.Authentication.feature index e2c459692..3b460efc6 100644 --- a/e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature +++ b/e2e-tests/cypress/e2e/User.Authentication.feature @@ -11,7 +11,7 @@ Feature: User authentication # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | Scenario: Log in successfully - Given the browser navigates to page "/login" + Given the user navigates to page "/login" When the user submits the credentials "bibi@bloxberg.de" "Aa12345_" Then the user is logged in with username "Bibi Bloxberg" diff --git a/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature b/e2e-tests/cypress/e2e/User.Registration.feature similarity index 90% rename from e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature rename to e2e-tests/cypress/e2e/User.Registration.feature index 9361d2b84..ed53bb4b0 100644 --- a/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature +++ b/e2e-tests/cypress/e2e/User.Registration.feature @@ -4,7 +4,7 @@ Feature: User registration @skip Scenario: Register successfully - Given the browser navigates to page "/register" + Given the user navigates to page "/register" When the user fills name and email "Regina" "Register" "regina@register.com" And the user agrees to the privacy policy And the user submits the registration form diff --git a/e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature b/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature similarity index 95% rename from e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature rename to e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature index ceee131fb..aa853f6ff 100644 --- a/e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature +++ b/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature @@ -12,7 +12,7 @@ Feature: User profile - change password Given the user is logged in as "bibi@bloxberg.de" "Aa12345_" Scenario: Change password successfully - Given the browser navigates to page "/profile" + Given the user navigates to page "/profile" And the user opens the change password menu When the user fills the password form with: | Old password | Aa12345_ | diff --git a/e2e-tests/cypress/e2e/models/ForgotPasswordPage.ts b/e2e-tests/cypress/e2e/models/ForgotPasswordPage.ts new file mode 100644 index 000000000..b97bc8ee7 --- /dev/null +++ b/e2e-tests/cypress/e2e/models/ForgotPasswordPage.ts @@ -0,0 +1,18 @@ +/// + +export class ForgotPasswordPage { + // selectors + emailInput = 'input[type=email]' + submitBtn = 'button[type=submit]' + successComponent = '[data-test="forgot-password-success"]' + + enterEmail(email: string) { + cy.get(this.emailInput).clear().type(email) + return this + } + + submitEmail() { + cy.get(this.submitBtn).click() + return this + } +} diff --git a/e2e-tests/cypress/e2e/models/LoginPage.ts b/e2e-tests/cypress/e2e/models/LoginPage.ts new file mode 100644 index 000000000..52aa5d19b --- /dev/null +++ b/e2e-tests/cypress/e2e/models/LoginPage.ts @@ -0,0 +1,35 @@ +/// + +export class LoginPage { + // selectors + emailInput = 'input[type=email]' + passwordInput = 'input[type=password]' + forgotPasswordLink = '[data-test="forgot-password-link"]' + submitBtn = '[type=submit]' + emailHint = '#vee_Email' + passwordHint = '#vee_Password' + + goto() { + cy.visit('/') + return this + } + + enterEmail(email: string) { + cy.get(this.emailInput).clear().type(email) + return this + } + + enterPassword(password: string) { + cy.get(this.passwordInput).clear().type(password) + return this + } + + submitLogin() { + cy.get(this.submitBtn).click() + return this + } + + openForgotPasswordPage() { + cy.get(this.forgotPasswordLink).click() + } +} diff --git a/e2e-tests/cypress/e2e/models/OverviewPage.ts b/e2e-tests/cypress/e2e/models/OverviewPage.ts new file mode 100644 index 000000000..345124c66 --- /dev/null +++ b/e2e-tests/cypress/e2e/models/OverviewPage.ts @@ -0,0 +1,10 @@ +/// + +export class OverviewPage { + navbarName = '[data-test="navbar-item-username"]' + + goto() { + cy.visit('/overview') + return this + } +} diff --git a/e2e-tests/cypress/e2e/models/ProfilePage.ts b/e2e-tests/cypress/e2e/models/ProfilePage.ts new file mode 100644 index 000000000..4d5b98a53 --- /dev/null +++ b/e2e-tests/cypress/e2e/models/ProfilePage.ts @@ -0,0 +1,35 @@ +/// + +export class ProfilePage { + // selectors + openChangePassword = '[data-test=open-password-change-form]' + oldPasswordInput = '#password-input-field' + newPasswordInput = '#new-password-input-field' + newPasswordRepeatInput = '#repeat-new-password-input-field' + submitNewPasswordBtn = '[data-test=submit-new-password-btn]' + + goto() { + cy.visit('/profile') + return this + } + + enterOldPassword(password: string) { + cy.get(this.oldPasswordInput).clear().type(password) + return this + } + + enterNewPassword(password: string) { + cy.get(this.newPasswordInput).find('input').clear().type(password) + return this + } + + enterRepeatPassword(password: string) { + cy.get(this.newPasswordRepeatInput).find('input').clear().type(password) + return this + } + + submitPasswordForm() { + cy.get(this.submitNewPasswordBtn).click() + return this + } +} diff --git a/e2e-tests/cypress/e2e/models/RegistrationPage.ts b/e2e-tests/cypress/e2e/models/RegistrationPage.ts new file mode 100644 index 000000000..8cae26a26 --- /dev/null +++ b/e2e-tests/cypress/e2e/models/RegistrationPage.ts @@ -0,0 +1,42 @@ +/// + +export class RegistrationPage { + // selectors + firstnameInput = '#registerFirstname' + lastnameInput = '#registerLastname' + emailInput = '#Email-input-field' + checkbox = '#registerCheckbox' + submitBtn = '[type=submit]' + + RegistrationThanxHeadline = '.test-message-headline' + RegistrationThanxText = '.test-message-subtitle' + + goto() { + cy.visit('/register') + return this + } + + enterFirstname(firstname: string) { + cy.get(this.firstnameInput).clear().type(firstname) + return this + } + + enterLastname(lastname: string) { + cy.get(this.lastnameInput).clear().type(lastname) + return this + } + + enterEmail(email: string) { + cy.get(this.emailInput).clear().type(email) + return this + } + + checkPrivacyCheckbox() { + cy.get(this.checkbox).click({ force: true }) + } + + submitRegistrationPage() { + cy.get(this.submitBtn).should('be.enabled') + cy.get(this.submitBtn).click() + } +} diff --git a/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts b/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts new file mode 100644 index 000000000..20134de6d --- /dev/null +++ b/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts @@ -0,0 +1,32 @@ +/// + +export class ResetPasswordPage { + // selectors + newPasswordBlock = '#new-password-input-field' + newPasswordRepeatBlock = '#repeat-new-password-input-field' + resetPasswordBtn = 'button[type=submit]' + resetPasswordMessageBlock = '[data-test="reset-password-message"]' + signinBtn = '.btn.test-message-button' + + enterNewPassword(password: string) { + cy.get(this.newPasswordBlock).find('input[type=password]').type(password) + return this + } + + repeatNewPassword(password: string) { + cy.get(this.newPasswordRepeatBlock) + .find('input[type=password]') + .type(password) + return this + } + + submitNewPassword() { + cy.get(this.resetPasswordBtn).click() + return this + } + + openSigninPage() { + cy.get(this.signinBtn).click() + return this + } +} diff --git a/e2e-tests/cypress/e2e/models/SideNavMenu.ts b/e2e-tests/cypress/e2e/models/SideNavMenu.ts new file mode 100644 index 000000000..ccd177b66 --- /dev/null +++ b/e2e-tests/cypress/e2e/models/SideNavMenu.ts @@ -0,0 +1,17 @@ +/// + +export class SideNavMenu { + // selectors + profileMenu = '[data-test=profile-menu]' + logoutMenu = '[data-test=logout-menu]' + + openUserProfile() { + cy.get(this.profileMenu).click() + return this + } + + logout() { + cy.get(this.logoutMenu).click() + return this + } +} diff --git a/e2e-tests/cypress/e2e/models/Toasts.ts b/e2e-tests/cypress/e2e/models/Toasts.ts new file mode 100644 index 000000000..efd5052fb --- /dev/null +++ b/e2e-tests/cypress/e2e/models/Toasts.ts @@ -0,0 +1,10 @@ +/// + +export class Toasts { + // selectors + toastSlot = '.b-toaster-slot' + toastTypeSuccess = '.b-toast-success' + toastTypeError = '.b-toast-danger' + toastTitle = '.gdd-toaster-title' + toastMessage = '.gdd-toaster-body' +} diff --git a/e2e-tests/cypress/e2e/models/UserEMailSite.ts b/e2e-tests/cypress/e2e/models/UserEMailSite.ts new file mode 100644 index 000000000..f46f5677b --- /dev/null +++ b/e2e-tests/cypress/e2e/models/UserEMailSite.ts @@ -0,0 +1,17 @@ +/// + +export class UserEMailSite { + // selectors + emailInbox = '.sidebar-emails-container' + emailList = '.email-list' + emailMeta = '.email-meta' + emailSubject = '.subject' + + openRecentPasswordResetEMail() { + cy.get(this.emailList) + .find('email-item') + .filter(':contains(asswor)') + .click() + expect(cy.get(this.emailSubject)).to('contain', 'asswor') + } +} diff --git a/e2e-tests/cypress/tests/cypress/fixtures/users.json b/e2e-tests/cypress/fixtures/users.json similarity index 100% rename from e2e-tests/cypress/tests/cypress/fixtures/users.json rename to e2e-tests/cypress/fixtures/users.json diff --git a/e2e-tests/cypress/support/e2e.ts b/e2e-tests/cypress/support/e2e.ts new file mode 100644 index 000000000..2f3557566 --- /dev/null +++ b/e2e-tests/cypress/support/e2e.ts @@ -0,0 +1,40 @@ +import jwtDecode from 'jwt-decode' + +Cypress.Commands.add('login', (email, password) => { + cy.clearLocalStorage('vuex') + + cy.request({ + method: 'POST', + url: Cypress.env('backendURL'), + body: { + operationName: null, + variables: { + email: email, + password: password, + }, + query: Cypress.env('loginQuery'), + }, + }).then(async (response) => { + const tokens = response.headers.token + const token = Array.isArray(tokens) ? tokens[0] : tokens + let tokenTime + + if (!token) return + // to avoid JWT InvalidTokenError, the decoding of the token is wrapped + // in a try-catch block (see + // https://github.com/auth0/jwt-decode/issues/65#issuecomment-395493807) + try { + tokenTime = jwtDecode(token).exp + } catch (tokenDecodingError) { + cy.log('JWT decoding error: ', tokenDecodingError) + } + + const vuexToken = { + token: token, + tokenTime: tokenTime, + } + + cy.visit('/') + window.localStorage.setItem('vuex', JSON.stringify(vuexToken)) + }) +}) diff --git a/e2e-tests/cypress/tests/cypress/support/index.ts b/e2e-tests/cypress/support/index.ts similarity index 63% rename from e2e-tests/cypress/tests/cypress/support/index.ts rename to e2e-tests/cypress/support/index.ts index 99ab0efc2..f8d1abacf 100644 --- a/e2e-tests/cypress/tests/cypress/support/index.ts +++ b/e2e-tests/cypress/support/index.ts @@ -1,14 +1,14 @@ /* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-explicit-any */ -/// +/// -import "./e2e"; +import './e2e' declare global { namespace Cypress { interface Chainable { - login(email: string, password: string): Chainable; + login(email: string, password: string): Chainable } } } diff --git a/e2e-tests/cypress/support/step_definitions/common_steps.ts b/e2e-tests/cypress/support/step_definitions/common_steps.ts new file mode 100644 index 000000000..c5d3004ac --- /dev/null +++ b/e2e-tests/cypress/support/step_definitions/common_steps.ts @@ -0,0 +1,39 @@ +import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor' +import { OverviewPage } from '../../e2e/models/OverviewPage' +import { SideNavMenu } from '../../e2e/models/SideNavMenu' +import { Toasts } from '../../e2e/models/Toasts' + +Given('the user navigates to page {string}', (page: string) => { + cy.visit(page) +}) + +// login related + +Given( + 'the user is logged in as {string} {string}', + (email: string, password: string) => { + cy.login(email, password) + } +) + +Then('the user is logged in with username {string}', (username: string) => { + const overviewPage = new OverviewPage() + cy.url().should('include', '/overview') + cy.get(overviewPage.navbarName).should('contain', username) +}) + +Then('the user cannot login', () => { + const toast = new Toasts() + cy.get(toast.toastSlot).within(() => { + cy.get(toast.toastTypeError) + cy.get(toast.toastTitle).should('be.visible') + cy.get(toast.toastMessage).should('be.visible') + }) +}) + +// logout + +Then('the user logs out', () => { + const sideNavMenu = new SideNavMenu() + sideNavMenu.logout() +}) diff --git a/e2e-tests/cypress/support/step_definitions/email_steps.ts b/e2e-tests/cypress/support/step_definitions/email_steps.ts new file mode 100644 index 000000000..b313442f2 --- /dev/null +++ b/e2e-tests/cypress/support/step_definitions/email_steps.ts @@ -0,0 +1,45 @@ +import { Then, When } from '@badeball/cypress-cucumber-preprocessor' +import { ResetPasswordPage } from '../../e2e/models/ResetPasswordPage' +import { UserEMailSite } from '../../e2e/models/UserEMailSite' + +const userEMailSite = new UserEMailSite() +const resetPasswordPage = new ResetPasswordPage() + +Then('the user receives an e-mail containing the password reset link', () => { + cy.origin( + Cypress.env('mailserverURL'), + { args: userEMailSite }, + (userEMailSite) => { + const linkPattern = /\/reset-password\/[0-9]+\d/ + + cy.visit('/') // navigate to user's e-maile site (on fake mail server) + cy.get(userEMailSite.emailInbox).should('be.visible') + + cy.get(userEMailSite.emailList) + .find('.email-item') + .filter(':contains(asswor)') + .first() + .click() + + cy.get(userEMailSite.emailMeta) + .find(userEMailSite.emailSubject) + .contains('asswor') + + cy.get('.email-content') + .find('.plain-text') + .contains(linkPattern) + .invoke('text') + .then((text) => { + const resetPasswordLink = text.match(linkPattern)[0] + cy.task('setResetPasswordLink', resetPasswordLink) + }) + } + ) +}) + +When('the user opens the password reset link in the browser', () => { + cy.task('getResetPasswordLink').then((passwordResetLink) => { + cy.visit(passwordResetLink) + }) + cy.get(resetPasswordPage.newPasswordRepeatBlock).should('be.visible') +}) diff --git a/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts new file mode 100644 index 000000000..5b25f5391 --- /dev/null +++ b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts @@ -0,0 +1,69 @@ +import { When, And } from '@badeball/cypress-cucumber-preprocessor' +import { ForgotPasswordPage } from '../../e2e/models/ForgotPasswordPage' +import { LoginPage } from '../../e2e/models/LoginPage' +import { ResetPasswordPage } from '../../e2e/models/ResetPasswordPage' + +const loginPage = new LoginPage() +const forgotPasswordPage = new ForgotPasswordPage() +const resetPasswordPage = new ResetPasswordPage() + +// login related + +When('the user submits no credentials', () => { + loginPage.submitLogin() +}) + +When( + 'the user submits the credentials {string} {string}', + (email: string, password: string) => { + cy.intercept('POST', '/graphql', (req) => { + if ( + req.body.hasOwnProperty('query') && + req.body.query.includes('mutation') + ) { + req.alias = 'login' + } + }) + + loginPage.enterEmail(email) + loginPage.enterPassword(password) + loginPage.submitLogin() + cy.wait('@login').then((interception) => { + expect(interception.response.statusCode).equals(200) + }) + } +) + +// password reset related + +And('the user navigates to the forgot password page', () => { + loginPage.openForgotPasswordPage() + cy.url().should('include', '/forgot-password') +}) + +When('the user enters the e-mail address {string}', (email: string) => { + forgotPasswordPage.enterEmail(email) +}) + +And('the user submits the e-mail form', () => { + forgotPasswordPage.submitEmail() + cy.get(forgotPasswordPage.successComponent).should('be.visible') +}) + +And('the user enters the password {string}', (password: string) => { + resetPasswordPage.enterNewPassword(password) +}) + +And('the user repeats the password {string}', (password: string) => { + resetPasswordPage.repeatNewPassword(password) +}) + +And('the user submits the new password', () => { + resetPasswordPage.submitNewPassword() + cy.get(resetPasswordPage.resetPasswordMessageBlock).should('be.visible') +}) + +And('the user clicks the sign in button', () => { + resetPasswordPage.openSigninPage() + cy.url().should('contain', '/login') +}) diff --git a/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts new file mode 100644 index 000000000..1dcbe69ef --- /dev/null +++ b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts @@ -0,0 +1,32 @@ +import { And, When } from '@badeball/cypress-cucumber-preprocessor' +import { ProfilePage } from '../../e2e/models/ProfilePage' +import { Toasts } from '../../e2e/models/Toasts' + +const profilePage = new ProfilePage() + +And('the user opens the change password menu', () => { + cy.get(profilePage.openChangePassword).click() + cy.get(profilePage.newPasswordRepeatInput).should('be.visible') + cy.get(profilePage.submitNewPasswordBtn).should('be.disabled') +}) + +When('the user fills the password form with:', (table) => { + let hashedTableRows = table.rowsHash() + profilePage.enterOldPassword(hashedTableRows['Old password']) + profilePage.enterNewPassword(hashedTableRows['New password']) + profilePage.enterRepeatPassword(hashedTableRows['Repeat new password']) + cy.get(profilePage.submitNewPasswordBtn).should('be.enabled') +}) + +And('the user submits the password form', () => { + profilePage.submitPasswordForm() +}) + +When('the user is presented a {string} message', (type: string) => { + const toast = new Toasts() + cy.get(toast.toastSlot).within(() => { + cy.get(toast.toastTypeSuccess) + cy.get(toast.toastTitle).should('be.visible') + cy.get(toast.toastMessage).should('be.visible') + }) +}) diff --git a/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts b/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts new file mode 100644 index 000000000..8f12338b0 --- /dev/null +++ b/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts @@ -0,0 +1,24 @@ +import { And, When } from '@badeball/cypress-cucumber-preprocessor' +import { RegistrationPage } from '../../e2e/models/RegistrationPage' + +const registrationPage = new RegistrationPage() + +When( + 'the user fills name and email {string} {string} {string}', + (firstname: string, lastname: string, email: string) => { + const registrationPage = new RegistrationPage() + registrationPage.enterFirstname(firstname) + registrationPage.enterLastname(lastname) + registrationPage.enterEmail(email) + } +) + +And('the user agrees to the privacy policy', () => { + registrationPage.checkPrivacyCheckbox() +}) + +And('the user submits the registration form', () => { + registrationPage.submitRegistrationPage() + cy.get(registrationPage.RegistrationThanxHeadline).should('be.visible') + cy.get(registrationPage.RegistrationThanxText).should('be.visible') +}) diff --git a/e2e-tests/cypress/tests/.eslintrc.js b/e2e-tests/cypress/tests/.eslintrc.js deleted file mode 100644 index 157454287..000000000 --- a/e2e-tests/cypress/tests/.eslintrc.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = { - root: true, - env: { - node: true, - }, - parser: "@typescript-eslint/parser", - plugins: ["cypress", "prettier", "@typescript-eslint"], - extends: [ - "standard", - "eslint:recommended", - "plugin:prettier/recommended", - "plugin:@typescript-eslint/recommended", - ], - rules: { - "no-console": ["error"], - "no-debugger": "error", - "prettier/prettier": [ - "error", - { - htmlWhitespaceSensitivity: "ignore", - }, - ], - }, -}; diff --git a/e2e-tests/cypress/tests/cypress.config.ts b/e2e-tests/cypress/tests/cypress.config.ts deleted file mode 100644 index 9621b7a00..000000000 --- a/e2e-tests/cypress/tests/cypress.config.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { defineConfig } from "cypress"; -import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor"; -import browserify from "@badeball/cypress-cucumber-preprocessor/browserify"; - -async function setupNodeEvents( - on: Cypress.PluginEvents, - config: Cypress.PluginConfigOptions -): Promise { - await addCucumberPreprocessorPlugin(on, config); - - on( - "file:preprocessor", - browserify(config, { - typescript: require.resolve("typescript"), - }) - ); - - on("after:run", (results) => { - if (results) { - // results will be undefined in interactive mode - // eslint-disable-next-line no-console - console.log(results.status); - } - }); - - return config; -} - -export default defineConfig({ - e2e: { - specPattern: "**/*.feature", - excludeSpecPattern: "*.js", - baseUrl: "http://localhost:3000", - chromeWebSecurity: false, - defaultCommandTimeout: 10000, - supportFile: "cypress/support/index.ts", - viewportHeight: 720, - viewportWidth: 1280, - video: false, - retries: { - runMode: 2, - openMode: 0, - }, - env: { - backendURL: "http://localhost:4000", - loginQuery: `query ($email: String!, $password: String!, $publisherId: Int) { - login(email: $email, password: $password, publisherId: $publisherId) { - email - firstName - lastName - language - klickTipp { - newsletterState - __typename - } - hasElopage - publisherId - isAdmin - creation - __typename - } -}`, - }, - setupNodeEvents, - }, -}); diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts deleted file mode 100644 index 9a0df62ee..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts +++ /dev/null @@ -1,30 +0,0 @@ -/// - -export class LoginPage { - // selectors - emailInput = "input[type=email]"; - passwordInput = "input[type=password]"; - submitBtn = "[type=submit]"; - emailHint = "#vee_Email"; - passwordHint = "#vee_Password"; - - goto() { - cy.visit("/"); - return this; - } - - enterEmail(email: string) { - cy.get(this.emailInput).clear().type(email); - return this; - } - - enterPassword(password: string) { - cy.get(this.passwordInput).clear().type(password); - return this; - } - - submitLogin() { - cy.get(this.submitBtn).click(); - return this; - } -} diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/OverviewPage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/OverviewPage.ts deleted file mode 100644 index 426c2b8b3..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/OverviewPage.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -export class OverviewPage { - navbarName = '[data-test="navbar-item-username"]'; - - goto() { - cy.visit("/overview"); - return this; - } -} diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts deleted file mode 100644 index 0532a7ff8..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts +++ /dev/null @@ -1,35 +0,0 @@ -/// - -export class ProfilePage { - // selectors - openChangePassword = "[data-test=open-password-change-form]"; - oldPasswordInput = "#password-input-field"; - newPasswordInput = "#new-password-input-field"; - newPasswordRepeatInput = "#repeat-new-password-input-field"; - submitNewPasswordBtn = "[data-test=submit-new-password-btn]"; - - goto() { - cy.visit("/profile"); - return this; - } - - enterOldPassword(password: string) { - cy.get(this.oldPasswordInput).clear().type(password); - return this; - } - - enterNewPassword(password: string) { - cy.get(this.newPasswordInput).find("input").clear().type(password); - return this; - } - - enterRepeatPassword(password: string) { - cy.get(this.newPasswordRepeatInput).find("input").clear().type(password); - return this; - } - - submitPasswordForm() { - cy.get(this.submitNewPasswordBtn).click(); - return this; - } -} diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts deleted file mode 100644 index 27a9cb8cc..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// - -export class RegistrationPage { - // selectors - firstnameInput = "#registerFirstname"; - lastnameInput = "#registerLastname"; - emailInput = "#Email-input-field"; - checkbox = "#registerCheckbox"; - submitBtn = "[type=submit]"; - - RegistrationThanxHeadline = ".test-message-headline"; - RegistrationThanxText = ".test-message-subtitle"; - - goto() { - cy.visit("/register"); - return this; - } - - enterFirstname(firstname: string) { - cy.get(this.firstnameInput).clear().type(firstname); - return this; - } - - enterLastname(lastname: string) { - cy.get(this.lastnameInput).clear().type(lastname); - return this; - } - - enterEmail(email: string) { - cy.get(this.emailInput).clear().type(email); - return this; - } - - checkPrivacyCheckbox() { - cy.get(this.checkbox).click({ force: true }); - } - - submitRegistrationPage() { - cy.get(this.submitBtn).should("be.enabled"); - cy.get(this.submitBtn).click(); - } -} diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/SideNavMenu.ts b/e2e-tests/cypress/tests/cypress/e2e/models/SideNavMenu.ts deleted file mode 100644 index 3dd9d6914..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/SideNavMenu.ts +++ /dev/null @@ -1,17 +0,0 @@ -/// - -export class SideNavMenu { - // selectors - profileMenu = "[data-test=profile-menu]"; - logoutMenu = "[data-test=logout-menu]"; - - openUserProfile() { - cy.get(this.profileMenu).click(); - return this; - } - - logout() { - cy.get(this.logoutMenu).click(); - return this; - } -} diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts b/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts deleted file mode 100644 index aabd0a45e..000000000 --- a/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -export class Toasts { - // selectors - toastSlot = ".b-toaster-slot"; - toastTypeSuccess = ".b-toast-success"; - toastTypeError = ".b-toast-danger"; - toastTitle = ".gdd-toaster-title"; - toastMessage = ".gdd-toaster-body"; -} diff --git a/e2e-tests/cypress/tests/cypress/support/e2e.ts b/e2e-tests/cypress/tests/cypress/support/e2e.ts deleted file mode 100644 index 16cc474bf..000000000 --- a/e2e-tests/cypress/tests/cypress/support/e2e.ts +++ /dev/null @@ -1,38 +0,0 @@ -import jwtDecode from "jwt-decode"; - -Cypress.Commands.add("login", (email, password) => { - cy.clearLocalStorage("vuex"); - - cy.request({ - method: "POST", - url: Cypress.env("backendURL"), - body: { - operationName: null, - variables: { - email: email, - password: password, - }, - query: Cypress.env("loginQuery"), - }, - }).then(async (response) => { - const token = response.headers.token; - let tokenTime; - - // to avoid JWT InvalidTokenError, the decoding of the token is wrapped - // in a try-catch block (see - // https://github.com/auth0/jwt-decode/issues/65#issuecomment-395493807) - try { - tokenTime = jwtDecode(token).exp; - } catch (tokenDecodingError) { - cy.log("JWT decoding error: ", tokenDecodingError); - } - - const vuexToken = { - token: token, - tokenTime: tokenTime, - }; - - cy.visit("/"); - window.localStorage.setItem("vuex", JSON.stringify(vuexToken)); - }); -}); diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts deleted file mode 100644 index f45358f3c..000000000 --- a/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor"; -import { LoginPage } from "../../e2e/models/LoginPage"; -import { OverviewPage } from "../../e2e/models/OverviewPage"; -import { SideNavMenu } from "../../e2e/models/SideNavMenu"; -import { Toasts } from "../../e2e/models/Toasts"; - -Given("the browser navigates to page {string}", (page: string) => { - cy.visit(page); -}); - -// login-related - -Given( - "the user is logged in as {string} {string}", - (email: string, password: string) => { - cy.login(email, password); - } -); - -Then("the user is logged in with username {string}", (username: string) => { - const overviewPage = new OverviewPage(); - cy.url().should("include", "/overview"); - cy.get(overviewPage.navbarName).should("contain", username); -}); - -Then("the user cannot login", () => { - const toast = new Toasts(); - cy.get(toast.toastSlot).within(() => { - cy.get(toast.toastTypeError); - cy.get(toast.toastTitle).should("be.visible"); - cy.get(toast.toastMessage).should("be.visible"); - }); -}); - -// - -When( - "the user submits the credentials {string} {string}", - (email: string, password: string) => { - const loginPage = new LoginPage(); - loginPage.enterEmail(email); - loginPage.enterPassword(password); - loginPage.submitLogin(); - } -); - -// logout - -Then("the user logs out", () => { - const sideNavMenu = new SideNavMenu(); - sideNavMenu.logout(); -}); diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_authentication_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/user_authentication_steps.ts deleted file mode 100644 index 1e5cfe88c..000000000 --- a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_authentication_steps.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; -import { LoginPage } from "../../e2e/models/LoginPage"; - -When("the user submits no credentials", () => { - const loginPage = new LoginPage(); - loginPage.submitLogin(); -}); diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts deleted file mode 100644 index 5396b66bb..000000000 --- a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { And, When } from "@badeball/cypress-cucumber-preprocessor"; -import { ProfilePage } from "../../e2e/models/ProfilePage"; -import { Toasts } from "../../e2e/models/Toasts"; - -const profilePage = new ProfilePage(); - -And("the user opens the change password menu", () => { - cy.get(profilePage.openChangePassword).click(); - cy.get(profilePage.newPasswordRepeatInput).should("be.visible"); - cy.get(profilePage.submitNewPasswordBtn).should("be.disabled"); -}); - -When("the user fills the password form with:", (table) => { - table = table.rowsHash(); - profilePage.enterOldPassword(table["Old password"]); - profilePage.enterNewPassword(table["New password"]); - profilePage.enterRepeatPassword(table["Repeat new password"]); - cy.get(profilePage.submitNewPasswordBtn).should("be.enabled"); -}); - -And("the user submits the password form", () => { - profilePage.submitPasswordForm(); -}); - -When("the user is presented a {string} message", (type: string) => { - const toast = new Toasts(); - cy.get(toast.toastSlot).within(() => { - cy.get(toast.toastTypeSuccess); - cy.get(toast.toastTitle).should("be.visible"); - cy.get(toast.toastMessage).should("be.visible"); - }); -}); diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_registration_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/user_registration_steps.ts deleted file mode 100644 index 49e121fc2..000000000 --- a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_registration_steps.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { And, When } from "@badeball/cypress-cucumber-preprocessor"; -import { RegistrationPage } from "../../e2e/models/RegistrationPage"; - -const registrationPage = new RegistrationPage(); - -When( - "the user fills name and email {string} {string} {string}", - (firstname: string, lastname: string, email: string) => { - const registrationPage = new RegistrationPage(); - registrationPage.enterFirstname(firstname); - registrationPage.enterLastname(lastname); - registrationPage.enterEmail(email); - } -); - -And("the user agrees to the privacy policy", () => { - registrationPage.checkPrivacyCheckbox(); -}); - -And("the user submits the registration form", () => { - registrationPage.submitRegistrationPage(); - cy.get(registrationPage.RegistrationThanxHeadline).should("be.visible"); - cy.get(registrationPage.RegistrationThanxText).should("be.visible"); -}); diff --git a/e2e-tests/cypress/tests/tsconfig.json b/e2e-tests/cypress/tests/tsconfig.json deleted file mode 100644 index c031a126e..000000000 --- a/e2e-tests/cypress/tests/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "target": "es2016", - "lib": ["es6", "dom"], - "baseUrl": "../node_modules", - "types": ["cypress", "node"], - "strict": true - }, - "include": ["**/*.ts"] -} diff --git a/e2e-tests/cypress/tests/package.json b/e2e-tests/package.json similarity index 100% rename from e2e-tests/cypress/tests/package.json rename to e2e-tests/package.json diff --git a/e2e-tests/tsconfig.json b/e2e-tests/tsconfig.json new file mode 100644 index 000000000..c07e512cb --- /dev/null +++ b/e2e-tests/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": ["es6", "dom"], + "baseUrl": ".", + "types": ["cypress", "node"], + "strict": true, + "esModuleInterop": true, + "moduleResolution": "node", + "paths": { + "@/*": ["cypress/*"], + "@models/*": ["cypress/e2e/models/*"], + } + }, + "include": ["**/*.ts"], +} diff --git a/e2e-tests/cypress/tests/yarn.lock b/e2e-tests/yarn.lock similarity index 100% rename from e2e-tests/cypress/tests/yarn.lock rename to e2e-tests/yarn.lock diff --git a/frontend/src/pages/ForgotPassword.vue b/frontend/src/pages/ForgotPassword.vue index 77c2ac926..773b78278 100644 --- a/frontend/src/pages/ForgotPassword.vue +++ b/frontend/src/pages/ForgotPassword.vue @@ -1,3 +1,4 @@ +