mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #2695 from gradido/2694-refactor-linting-like-backend
refactor(other): add linting rules like in backend modul
This commit is contained in:
commit
49d065cb5e
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -647,7 +647,7 @@ 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,cypress/e2e/User.Authentication.ResetPassword.feature
|
||||
- name: End-to-end tests | if tests failed, upload screenshots
|
||||
@ -655,4 +655,4 @@ jobs:
|
||||
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/
|
||||
|
||||
26
e2e-tests/.eslintrc.js
Normal file
26
e2e-tests/.eslintrc.js
Normal file
@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
4
e2e-tests/.gitignore
vendored
Normal file
4
e2e-tests/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
cypress/cucumber-messages.ndjson
|
||||
9
e2e-tests/.prettierrc.js
Normal file
9
e2e-tests/.prettierrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
endOfLine: "auto",
|
||||
};
|
||||
@ -11,7 +11,7 @@
|
||||
###############################################################################
|
||||
FROM cypress/base:16.14.2-slim
|
||||
|
||||
ARG DOCKER_WORKDIR=/tests/
|
||||
ARG DOCKER_WORKDIR="/tests"
|
||||
WORKDIR $DOCKER_WORKDIR
|
||||
|
||||
# install dependencies
|
||||
@ -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/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
|
||||
```
|
||||
|
||||
64
e2e-tests/cucumber-messages.ndjson
Normal file
64
e2e-tests/cucumber-messages.ndjson
Normal file
@ -0,0 +1,64 @@
|
||||
{"testRunStarted":{"timestamp":{"seconds":1676286317,"nanos":602000000}}}
|
||||
{"source":{"data":"Feature: User authentication\n As a user\n I want to be able to sign in - only with valid credentials\n In order to be able to posts and do other contributions as myself\n Furthermore I want to be able to stay logged in and logout again\n\n # TODO for these pre-conditions utilize seeding or API check, if user exists in test system\n # Background:\n # Given the following \"users\" are in the database:\n # | email | password | name |\n # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg |\n\n Scenario: Log in successfully\n Given the user navigates to page \"/login\"\n When the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\"\n Then the user is logged in with username \"Bibi Bloxberg\"\n\n","uri":"cypress/e2e/User.Authentication.feature","mediaType":"text/x.cucumber.gherkin+plain"}}
|
||||
{"gherkinDocument":{"feature":{"tags":[],"location":{"line":1,"column":1},"language":"en","keyword":"Feature","name":"User authentication","description":" As a user\n I want to be able to sign in - only with valid credentials\n In order to be able to posts and do other contributions as myself\n Furthermore I want to be able to stay logged in and logout again","children":[{"scenario":{"id":"a0e60b61-d279-428a-a801-276b5142499f","tags":[],"location":{"line":13,"column":3},"keyword":"Scenario","name":"Log in successfully","description":"","steps":[{"id":"904f82d6-cd31-4b76-a43f-fb3b53cd30e1","location":{"line":14,"column":5},"keyword":"Given ","keywordType":"Context","text":"the user navigates to page \"/login\""},{"id":"f2b5ed61-f51c-4822-b086-b7ab4852ae2e","location":{"line":15,"column":5},"keyword":"When ","keywordType":"Action","text":"the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\""},{"id":"cff1f6a9-23c7-43de-b49b-d472c47b32dc","location":{"line":16,"column":5},"keyword":"Then ","keywordType":"Outcome","text":"the user is logged in with username \"Bibi Bloxberg\""}],"examples":[]}}]},"comments":[{"location":{"line":7,"column":1},"text":" # TODO for these pre-conditions utilize seeding or API check, if user exists in test system"},{"location":{"line":8,"column":1},"text":" # Background:"},{"location":{"line":9,"column":1},"text":" # Given the following \"users\" are in the database:"},{"location":{"line":10,"column":1},"text":" # | email | password | name |"},{"location":{"line":11,"column":1},"text":" # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg |"}],"uri":"cypress/e2e/User.Authentication.feature"}}
|
||||
{"pickle":{"id":"594b4e85-f837-4444-ac71-6ac177cb10b3","uri":"cypress/e2e/User.Authentication.feature","astNodeIds":["a0e60b61-d279-428a-a801-276b5142499f"],"tags":[],"name":"Log in successfully","language":"en","steps":[{"id":"1ae06614-195e-419b-aef6-7b9fbc101dad","text":"the user navigates to page \"/login\"","type":"Context","astNodeIds":["904f82d6-cd31-4b76-a43f-fb3b53cd30e1"]},{"id":"a883467f-a8dc-41c0-82b1-e31ed6ce7996","text":"the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\"","type":"Action","astNodeIds":["f2b5ed61-f51c-4822-b086-b7ab4852ae2e"]},{"id":"bc98b28e-ea68-4d2f-b849-f303edb219c4","text":"the user is logged in with username \"Bibi Bloxberg\"","type":"Outcome","astNodeIds":["cff1f6a9-23c7-43de-b49b-d472c47b32dc"]}]}}
|
||||
{"stepDefinition":{"id":"5d4d7728-0f60-407e-9ba1-6ab14446d7b3","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"7ef374e8-2748-43c2-9d39-e254d0724de9","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"d4b6c30d-d239-44bb-9963-b273e3aba5bf","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"testCase":{"id":"df8b15f1-e497-49c4-918d-a25a71996742","pickleId":"594b4e85-f837-4444-ac71-6ac177cb10b3","testSteps":[{"id":"1ae06614-195e-419b-aef6-7b9fbc101dad","pickleStepId":"1ae06614-195e-419b-aef6-7b9fbc101dad","stepDefinitionIds":["5d4d7728-0f60-407e-9ba1-6ab14446d7b3"]},{"id":"a883467f-a8dc-41c0-82b1-e31ed6ce7996","pickleStepId":"a883467f-a8dc-41c0-82b1-e31ed6ce7996","stepDefinitionIds":["7ef374e8-2748-43c2-9d39-e254d0724de9"]},{"id":"bc98b28e-ea68-4d2f-b849-f303edb219c4","pickleStepId":"bc98b28e-ea68-4d2f-b849-f303edb219c4","stepDefinitionIds":["d4b6c30d-d239-44bb-9963-b273e3aba5bf"]}]}}
|
||||
{"testCaseStarted":{"id":"0677b26e-1829-46ae-a27f-6eac9506d2fa","testCaseId":"df8b15f1-e497-49c4-918d-a25a71996742","attempt":0,"timestamp":{"seconds":1676286322,"nanos":42000000}}}
|
||||
{"testStepStarted":{"testStepId":"1ae06614-195e-419b-aef6-7b9fbc101dad","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","timestamp":{"seconds":1676286322,"nanos":58000000}}}
|
||||
{"testStepFinished":{"testStepId":"1ae06614-195e-419b-aef6-7b9fbc101dad","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","testStepResult":{"status":"PASSED","duration":{"seconds":4,"nanos":699000000}},"timestamp":{"seconds":1676286326,"nanos":757000000}}}
|
||||
{"testStepStarted":{"testStepId":"a883467f-a8dc-41c0-82b1-e31ed6ce7996","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","timestamp":{"seconds":1676286326,"nanos":768000000}}}
|
||||
{"testStepFinished":{"testStepId":"a883467f-a8dc-41c0-82b1-e31ed6ce7996","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","testStepResult":{"status":"PASSED","duration":{"seconds":2,"nanos":194000000}},"timestamp":{"seconds":1676286328,"nanos":962000000}}}
|
||||
{"testStepStarted":{"testStepId":"bc98b28e-ea68-4d2f-b849-f303edb219c4","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","timestamp":{"seconds":1676286328,"nanos":969000000}}}
|
||||
{"testStepFinished":{"testStepId":"bc98b28e-ea68-4d2f-b849-f303edb219c4","testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","testStepResult":{"status":"PASSED","duration":{"seconds":2,"nanos":-179000000}},"timestamp":{"seconds":1676286330,"nanos":790000000}}}
|
||||
{"testCaseFinished":{"testCaseStartedId":"0677b26e-1829-46ae-a27f-6eac9506d2fa","timestamp":{"seconds":1676286330,"nanos":806000000},"willBeRetried":false}}
|
||||
{"source":{"data":"Feature: User Authentication - reset password\n As a user\n I want to reset my password from the sign in page\n\n # TODO for these pre-conditions utilize seeding or API check, if user exists in test system\n # Background:\n # Given the following \"users\" are in the database:\n # | email | password | name |\n # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg |\n\n Scenario: Reset password from signin page successfully\n Given the user navigates to page \"/login\"\n And the user navigates to the forgot password page\n When the user enters the e-mail address \"bibi@bloxberg.de\"\n And the user submits the e-mail form\n Then the user receives an e-mail containing the password reset link\n When the user opens the password reset link in the browser\n And the user enters the password \"12345Aa_\"\n And the user repeats the password \"12345Aa_\"\n And the user submits the password form\n And the user clicks the sign in button\n Then the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\"\n And the user cannot login\n But the user submits the credentials \"bibi@bloxberg.de\" \"12345Aa_\"\n And the user is logged in with username \"Bibi Bloxberg\"\n","uri":"cypress/e2e/User.Authentication.ResetPassword.feature","mediaType":"text/x.cucumber.gherkin+plain"}}
|
||||
{"gherkinDocument":{"feature":{"tags":[],"location":{"line":1,"column":1},"language":"en","keyword":"Feature","name":"User Authentication - reset password","description":" As a user\n I want to reset my password from the sign in page","children":[{"scenario":{"id":"8e0e49f7-45ab-4a17-9504-45cff1415b1e","tags":[],"location":{"line":11,"column":3},"keyword":"Scenario","name":"Reset password from signin page successfully","description":"","steps":[{"id":"10d7ec81-19d9-4c52-9987-22d9a90b584d","location":{"line":12,"column":5},"keyword":"Given ","keywordType":"Context","text":"the user navigates to page \"/login\""},{"id":"02bf5e72-d84f-4bcf-87d8-38d5afd035c3","location":{"line":13,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user navigates to the forgot password page"},{"id":"dfdeedc3-6b79-44f3-9673-b01071bef0b6","location":{"line":14,"column":5},"keyword":"When ","keywordType":"Action","text":"the user enters the e-mail address \"bibi@bloxberg.de\""},{"id":"5c288ecb-447f-48c8-8756-f3e218bd309b","location":{"line":15,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user submits the e-mail form"},{"id":"ade71b55-6471-4eeb-9216-ab597963ba83","location":{"line":16,"column":5},"keyword":"Then ","keywordType":"Outcome","text":"the user receives an e-mail containing the password reset link"},{"id":"7f0ea3d7-18c8-48a6-a23c-8a0c2ab66d5e","location":{"line":17,"column":5},"keyword":"When ","keywordType":"Action","text":"the user opens the password reset link in the browser"},{"id":"01e974c7-ed2a-4fae-a30b-75bd5662a286","location":{"line":18,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user enters the password \"12345Aa_\""},{"id":"1440ce50-80fe-4e0a-a948-66bf83867e09","location":{"line":19,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user repeats the password \"12345Aa_\""},{"id":"d0fc532d-ab9b-4240-9d73-4ca329c3f98e","location":{"line":20,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user submits the password form"},{"id":"9860b7f0-c08c-494b-b4dc-65c1201e6d50","location":{"line":21,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user clicks the sign in button"},{"id":"bcc6cea7-9e05-4d9d-b9fb-bb0e78c56382","location":{"line":22,"column":5},"keyword":"Then ","keywordType":"Outcome","text":"the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\""},{"id":"36382e35-9bdf-4780-86df-4739aa1e53a3","location":{"line":23,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user cannot login"},{"id":"a8678804-db7f-498c-b887-b7236ac03b55","location":{"line":24,"column":5},"keyword":"But ","keywordType":"Conjunction","text":"the user submits the credentials \"bibi@bloxberg.de\" \"12345Aa_\""},{"id":"9977e940-f7fc-48eb-b13f-b7ecd2b4f55f","location":{"line":25,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"the user is logged in with username \"Bibi Bloxberg\""}],"examples":[]}}]},"comments":[{"location":{"line":5,"column":1},"text":" # TODO for these pre-conditions utilize seeding or API check, if user exists in test system"},{"location":{"line":6,"column":1},"text":" # Background:"},{"location":{"line":7,"column":1},"text":" # Given the following \"users\" are in the database:"},{"location":{"line":8,"column":1},"text":" # | email | password | name |"},{"location":{"line":9,"column":1},"text":" # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg |"}],"uri":"cypress/e2e/User.Authentication.ResetPassword.feature"}}
|
||||
{"pickle":{"id":"24f9bcc4-a1d7-4181-8f8b-5f6af6d9832f","uri":"cypress/e2e/User.Authentication.ResetPassword.feature","astNodeIds":["8e0e49f7-45ab-4a17-9504-45cff1415b1e"],"tags":[],"name":"Reset password from signin page successfully","language":"en","steps":[{"id":"40973f19-55d1-4ee1-8a8e-692b39df8408","text":"the user navigates to page \"/login\"","type":"Context","astNodeIds":["10d7ec81-19d9-4c52-9987-22d9a90b584d"]},{"id":"23d91a4d-950a-4697-8fab-f503a00bdff9","text":"the user navigates to the forgot password page","type":"Context","astNodeIds":["02bf5e72-d84f-4bcf-87d8-38d5afd035c3"]},{"id":"d4a72f40-5cb3-4448-95da-3ee4b4dd3725","text":"the user enters the e-mail address \"bibi@bloxberg.de\"","type":"Action","astNodeIds":["dfdeedc3-6b79-44f3-9673-b01071bef0b6"]},{"id":"fe4a8ade-35e6-445f-8fc6-a677749c23e4","text":"the user submits the e-mail form","type":"Action","astNodeIds":["5c288ecb-447f-48c8-8756-f3e218bd309b"]},{"id":"866472af-0e12-4e16-b92a-d328250b0e32","text":"the user receives an e-mail containing the password reset link","type":"Outcome","astNodeIds":["ade71b55-6471-4eeb-9216-ab597963ba83"]},{"id":"1573af3c-39b9-4a40-94ec-2252bc543c18","text":"the user opens the password reset link in the browser","type":"Action","astNodeIds":["7f0ea3d7-18c8-48a6-a23c-8a0c2ab66d5e"]},{"id":"343457b6-37c0-4dde-baad-31271212df53","text":"the user enters the password \"12345Aa_\"","type":"Action","astNodeIds":["01e974c7-ed2a-4fae-a30b-75bd5662a286"]},{"id":"2ce2f26b-9cc0-47e9-9b3f-f8e0cff3c6c2","text":"the user repeats the password \"12345Aa_\"","type":"Action","astNodeIds":["1440ce50-80fe-4e0a-a948-66bf83867e09"]},{"id":"8ec7c2ad-ec75-42c3-b40e-6cb41b45a316","text":"the user submits the password form","type":"Action","astNodeIds":["d0fc532d-ab9b-4240-9d73-4ca329c3f98e"]},{"id":"20084ca2-45c5-4aa0-b8cc-3090421a1981","text":"the user clicks the sign in button","type":"Action","astNodeIds":["9860b7f0-c08c-494b-b4dc-65c1201e6d50"]},{"id":"318683e6-23d0-4983-8268-be0c86242e99","text":"the user submits the credentials \"bibi@bloxberg.de\" \"Aa12345_\"","type":"Outcome","astNodeIds":["bcc6cea7-9e05-4d9d-b9fb-bb0e78c56382"]},{"id":"3e46d169-05ec-49fc-b84b-02e9486cfe9d","text":"the user cannot login","type":"Outcome","astNodeIds":["36382e35-9bdf-4780-86df-4739aa1e53a3"]},{"id":"2a62df07-9fbe-44b5-b543-9990ff8b6df0","text":"the user submits the credentials \"bibi@bloxberg.de\" \"12345Aa_\"","type":"Outcome","astNodeIds":["a8678804-db7f-498c-b887-b7236ac03b55"]},{"id":"c52958a7-dfba-4e29-9844-0faf070f1b17","text":"the user is logged in with username \"Bibi Bloxberg\"","type":"Outcome","astNodeIds":["9977e940-f7fc-48eb-b13f-b7ecd2b4f55f"]}]}}
|
||||
{"stepDefinition":{"id":"e5b7a6d9-792e-4cf1-9335-74ab0a548768","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"4fd7cf9c-e5c7-4c90-8e4a-01bccd8b8252","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"68741ec6-fff2-4c79-8cee-e8aa28790d99","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"f8bb3265-6e86-4a51-8c0f-cef6f8c6c419","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"b00d66f5-0efd-46b6-a3b2-8e57248e336f","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"2d1b4b54-a56f-4bfe-b52a-6e6173160da3","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"fd75a778-338e-4381-86cf-b42bfc506181","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"4e88908a-e314-4762-b235-b29b55651b78","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"512c8ff3-1bb0-4da2-adbe-f4cf6581d1a4","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"df4cfb85-1def-4b22-b067-25c775474824","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"299aec62-a03f-4f9e-8de8-d44a9c736a5c","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"9718ad70-b334-46a1-878b-96d078853162","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"9c60fe6c-8b1e-4b10-b8f6-2870f31838d9","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"stepDefinition":{"id":"255e6c36-30da-4817-8198-1f14f8813f85","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}}
|
||||
{"testCase":{"id":"c84a9141-dd71-43ad-9169-7a29316dca69","pickleId":"24f9bcc4-a1d7-4181-8f8b-5f6af6d9832f","testSteps":[{"id":"40973f19-55d1-4ee1-8a8e-692b39df8408","pickleStepId":"40973f19-55d1-4ee1-8a8e-692b39df8408","stepDefinitionIds":["e5b7a6d9-792e-4cf1-9335-74ab0a548768"]},{"id":"23d91a4d-950a-4697-8fab-f503a00bdff9","pickleStepId":"23d91a4d-950a-4697-8fab-f503a00bdff9","stepDefinitionIds":["4fd7cf9c-e5c7-4c90-8e4a-01bccd8b8252"]},{"id":"d4a72f40-5cb3-4448-95da-3ee4b4dd3725","pickleStepId":"d4a72f40-5cb3-4448-95da-3ee4b4dd3725","stepDefinitionIds":["68741ec6-fff2-4c79-8cee-e8aa28790d99"]},{"id":"fe4a8ade-35e6-445f-8fc6-a677749c23e4","pickleStepId":"fe4a8ade-35e6-445f-8fc6-a677749c23e4","stepDefinitionIds":["f8bb3265-6e86-4a51-8c0f-cef6f8c6c419"]},{"id":"866472af-0e12-4e16-b92a-d328250b0e32","pickleStepId":"866472af-0e12-4e16-b92a-d328250b0e32","stepDefinitionIds":["b00d66f5-0efd-46b6-a3b2-8e57248e336f"]},{"id":"1573af3c-39b9-4a40-94ec-2252bc543c18","pickleStepId":"1573af3c-39b9-4a40-94ec-2252bc543c18","stepDefinitionIds":["2d1b4b54-a56f-4bfe-b52a-6e6173160da3"]},{"id":"343457b6-37c0-4dde-baad-31271212df53","pickleStepId":"343457b6-37c0-4dde-baad-31271212df53","stepDefinitionIds":["fd75a778-338e-4381-86cf-b42bfc506181"]},{"id":"2ce2f26b-9cc0-47e9-9b3f-f8e0cff3c6c2","pickleStepId":"2ce2f26b-9cc0-47e9-9b3f-f8e0cff3c6c2","stepDefinitionIds":["4e88908a-e314-4762-b235-b29b55651b78"]},{"id":"8ec7c2ad-ec75-42c3-b40e-6cb41b45a316","pickleStepId":"8ec7c2ad-ec75-42c3-b40e-6cb41b45a316","stepDefinitionIds":["512c8ff3-1bb0-4da2-adbe-f4cf6581d1a4"]},{"id":"20084ca2-45c5-4aa0-b8cc-3090421a1981","pickleStepId":"20084ca2-45c5-4aa0-b8cc-3090421a1981","stepDefinitionIds":["df4cfb85-1def-4b22-b067-25c775474824"]},{"id":"318683e6-23d0-4983-8268-be0c86242e99","pickleStepId":"318683e6-23d0-4983-8268-be0c86242e99","stepDefinitionIds":["299aec62-a03f-4f9e-8de8-d44a9c736a5c"]},{"id":"3e46d169-05ec-49fc-b84b-02e9486cfe9d","pickleStepId":"3e46d169-05ec-49fc-b84b-02e9486cfe9d","stepDefinitionIds":["9718ad70-b334-46a1-878b-96d078853162"]},{"id":"2a62df07-9fbe-44b5-b543-9990ff8b6df0","pickleStepId":"2a62df07-9fbe-44b5-b543-9990ff8b6df0","stepDefinitionIds":["9c60fe6c-8b1e-4b10-b8f6-2870f31838d9"]},{"id":"c52958a7-dfba-4e29-9844-0faf070f1b17","pickleStepId":"c52958a7-dfba-4e29-9844-0faf070f1b17","stepDefinitionIds":["255e6c36-30da-4817-8198-1f14f8813f85"]}]}}
|
||||
{"testCaseStarted":{"id":"c71f19b8-2733-403d-a854-4c3760dd9eca","testCaseId":"c84a9141-dd71-43ad-9169-7a29316dca69","attempt":0,"timestamp":{"seconds":1676286336,"nanos":35000000}}}
|
||||
{"testStepStarted":{"testStepId":"40973f19-55d1-4ee1-8a8e-692b39df8408","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286336,"nanos":53000000}}}
|
||||
{"testStepFinished":{"testStepId":"40973f19-55d1-4ee1-8a8e-692b39df8408","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":3,"nanos":0}},"timestamp":{"seconds":1676286339,"nanos":53000000}}}
|
||||
{"testStepStarted":{"testStepId":"23d91a4d-950a-4697-8fab-f503a00bdff9","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286339,"nanos":57000000}}}
|
||||
{"testStepFinished":{"testStepId":"23d91a4d-950a-4697-8fab-f503a00bdff9","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":215000000}},"timestamp":{"seconds":1676286339,"nanos":272000000}}}
|
||||
{"testStepStarted":{"testStepId":"d4a72f40-5cb3-4448-95da-3ee4b4dd3725","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286339,"nanos":275000000}}}
|
||||
{"testStepFinished":{"testStepId":"d4a72f40-5cb3-4448-95da-3ee4b4dd3725","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":520000000}},"timestamp":{"seconds":1676286339,"nanos":795000000}}}
|
||||
{"testStepStarted":{"testStepId":"fe4a8ade-35e6-445f-8fc6-a677749c23e4","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286339,"nanos":798000000}}}
|
||||
{"testStepFinished":{"testStepId":"fe4a8ade-35e6-445f-8fc6-a677749c23e4","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":1,"nanos":-664000000}},"timestamp":{"seconds":1676286340,"nanos":134000000}}}
|
||||
{"testStepStarted":{"testStepId":"866472af-0e12-4e16-b92a-d328250b0e32","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286340,"nanos":137000000}}}
|
||||
{"testStepFinished":{"testStepId":"866472af-0e12-4e16-b92a-d328250b0e32","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":2,"nanos":411000000}},"timestamp":{"seconds":1676286342,"nanos":548000000}}}
|
||||
{"testStepStarted":{"testStepId":"1573af3c-39b9-4a40-94ec-2252bc543c18","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286342,"nanos":551000000}}}
|
||||
{"testStepFinished":{"testStepId":"1573af3c-39b9-4a40-94ec-2252bc543c18","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":1,"nanos":94000000}},"timestamp":{"seconds":1676286343,"nanos":645000000}}}
|
||||
{"testStepStarted":{"testStepId":"343457b6-37c0-4dde-baad-31271212df53","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286343,"nanos":649000000}}}
|
||||
{"testStepFinished":{"testStepId":"343457b6-37c0-4dde-baad-31271212df53","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":1,"nanos":-601000000}},"timestamp":{"seconds":1676286344,"nanos":48000000}}}
|
||||
{"testStepStarted":{"testStepId":"2ce2f26b-9cc0-47e9-9b3f-f8e0cff3c6c2","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286344,"nanos":52000000}}}
|
||||
{"testStepFinished":{"testStepId":"2ce2f26b-9cc0-47e9-9b3f-f8e0cff3c6c2","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":313000000}},"timestamp":{"seconds":1676286344,"nanos":365000000}}}
|
||||
{"testStepStarted":{"testStepId":"8ec7c2ad-ec75-42c3-b40e-6cb41b45a316","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286344,"nanos":368000000}}}
|
||||
{"testStepFinished":{"testStepId":"8ec7c2ad-ec75-42c3-b40e-6cb41b45a316","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":118000000}},"timestamp":{"seconds":1676286344,"nanos":486000000}}}
|
||||
{"testStepStarted":{"testStepId":"20084ca2-45c5-4aa0-b8cc-3090421a1981","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286344,"nanos":489000000}}}
|
||||
{"testStepFinished":{"testStepId":"20084ca2-45c5-4aa0-b8cc-3090421a1981","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":377000000}},"timestamp":{"seconds":1676286344,"nanos":866000000}}}
|
||||
{"testStepStarted":{"testStepId":"318683e6-23d0-4983-8268-be0c86242e99","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286344,"nanos":869000000}}}
|
||||
{"testStepFinished":{"testStepId":"318683e6-23d0-4983-8268-be0c86242e99","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":2,"nanos":-643000000}},"timestamp":{"seconds":1676286346,"nanos":226000000}}}
|
||||
{"testStepStarted":{"testStepId":"3e46d169-05ec-49fc-b84b-02e9486cfe9d","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286346,"nanos":228000000}}}
|
||||
{"testStepFinished":{"testStepId":"3e46d169-05ec-49fc-b84b-02e9486cfe9d","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":0,"nanos":113000000}},"timestamp":{"seconds":1676286346,"nanos":341000000}}}
|
||||
{"testStepStarted":{"testStepId":"2a62df07-9fbe-44b5-b543-9990ff8b6df0","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286346,"nanos":344000000}}}
|
||||
{"testStepFinished":{"testStepId":"2a62df07-9fbe-44b5-b543-9990ff8b6df0","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":1,"nanos":371000000}},"timestamp":{"seconds":1676286347,"nanos":715000000}}}
|
||||
{"testStepStarted":{"testStepId":"c52958a7-dfba-4e29-9844-0faf070f1b17","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286347,"nanos":718000000}}}
|
||||
{"testStepFinished":{"testStepId":"c52958a7-dfba-4e29-9844-0faf070f1b17","testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","testStepResult":{"status":"PASSED","duration":{"seconds":2,"nanos":-345000000}},"timestamp":{"seconds":1676286349,"nanos":373000000}}}
|
||||
{"testCaseFinished":{"testCaseStartedId":"c71f19b8-2733-403d-a854-4c3760dd9eca","timestamp":{"seconds":1676286349,"nanos":385000000},"willBeRetried":false}}
|
||||
79
e2e-tests/cypress.config.ts
Normal file
79
e2e-tests/cypress.config.ts
Normal file
@ -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<Cypress.PluginConfigOptions> {
|
||||
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,
|
||||
},
|
||||
})
|
||||
4
e2e-tests/cypress/.gitignore
vendored
4
e2e-tests/cypress/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
tests/node_modules/
|
||||
tests/cypress/screenshots/
|
||||
tests/cypress/videos/
|
||||
tests/cucumber-messages.ndjson
|
||||
@ -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
|
||||
```
|
||||
18
e2e-tests/cypress/e2e/models/ForgotPasswordPage.ts
Normal file
18
e2e-tests/cypress/e2e/models/ForgotPasswordPage.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
35
e2e-tests/cypress/e2e/models/LoginPage.ts
Normal file
35
e2e-tests/cypress/e2e/models/LoginPage.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
10
e2e-tests/cypress/e2e/models/OverviewPage.ts
Normal file
10
e2e-tests/cypress/e2e/models/OverviewPage.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
export class OverviewPage {
|
||||
navbarName = '[data-test="navbar-item-username"]'
|
||||
|
||||
goto() {
|
||||
cy.visit('/overview')
|
||||
return this
|
||||
}
|
||||
}
|
||||
35
e2e-tests/cypress/e2e/models/ProfilePage.ts
Normal file
35
e2e-tests/cypress/e2e/models/ProfilePage.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
42
e2e-tests/cypress/e2e/models/RegistrationPage.ts
Normal file
42
e2e-tests/cypress/e2e/models/RegistrationPage.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
32
e2e-tests/cypress/e2e/models/ResetPasswordPage.ts
Normal file
32
e2e-tests/cypress/e2e/models/ResetPasswordPage.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
17
e2e-tests/cypress/e2e/models/SideNavMenu.ts
Normal file
17
e2e-tests/cypress/e2e/models/SideNavMenu.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
10
e2e-tests/cypress/e2e/models/Toasts.ts
Normal file
10
e2e-tests/cypress/e2e/models/Toasts.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
export class Toasts {
|
||||
// selectors
|
||||
toastSlot = '.b-toaster-slot'
|
||||
toastTypeSuccess = '.b-toast-success'
|
||||
toastTypeError = '.b-toast-danger'
|
||||
toastTitle = '.gdd-toaster-title'
|
||||
toastMessage = '.gdd-toaster-body'
|
||||
}
|
||||
17
e2e-tests/cypress/e2e/models/UserEMailSite.ts
Normal file
17
e2e-tests/cypress/e2e/models/UserEMailSite.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/// <reference types='cypress' />
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
40
e2e-tests/cypress/support/e2e.ts
Normal file
40
e2e-tests/cypress/support/e2e.ts
Normal file
@ -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))
|
||||
})
|
||||
})
|
||||
@ -1,14 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/// <reference types="cypress" />
|
||||
/// <reference types='cypress' />
|
||||
|
||||
import "./e2e";
|
||||
import './e2e'
|
||||
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): Chainable<any>;
|
||||
login(email: string, password: string): Chainable<any>
|
||||
}
|
||||
}
|
||||
}
|
||||
39
e2e-tests/cypress/support/step_definitions/common_steps.ts
Normal file
39
e2e-tests/cypress/support/step_definitions/common_steps.ts
Normal file
@ -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()
|
||||
})
|
||||
45
e2e-tests/cypress/support/step_definitions/email_steps.ts
Normal file
45
e2e-tests/cypress/support/step_definitions/email_steps.ts
Normal file
@ -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')
|
||||
})
|
||||
@ -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')
|
||||
})
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
@ -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')
|
||||
})
|
||||
@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -1,79 +0,0 @@
|
||||
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<Cypress.PluginConfigOptions> {
|
||||
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,
|
||||
},
|
||||
});
|
||||
@ -1,18 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
export class OverviewPage {
|
||||
navbarName = '[data-test="navbar-item-username"]';
|
||||
|
||||
goto() {
|
||||
cy.visit("/overview");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
export class Toasts {
|
||||
// selectors
|
||||
toastSlot = ".b-toaster-slot";
|
||||
toastTypeSuccess = ".b-toast-success";
|
||||
toastTypeError = ".b-toast-danger";
|
||||
toastTitle = ".gdd-toaster-title";
|
||||
toastMessage = ".gdd-toaster-body";
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
});
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
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();
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
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");
|
||||
});
|
||||
@ -1,69 +0,0 @@
|
||||
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");
|
||||
});
|
||||
@ -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");
|
||||
});
|
||||
});
|
||||
@ -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");
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"lib": ["es6", "dom"],
|
||||
"baseUrl": "../node_modules",
|
||||
"types": ["cypress", "node"],
|
||||
"strict": true
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
16
e2e-tests/tsconfig.json
Normal file
16
e2e-tests/tsconfig.json
Normal file
@ -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"],
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user