mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge remote-tracking branch 'origin/master' into 2220-feature-reconfigure-log4js
This commit is contained in:
commit
ff93c41c19
@ -1,4 +1,4 @@
|
|||||||
CONFIG_VERSION=v9.2022-07-07
|
CONFIG_VERSION=v10.2022-09-20
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
PORT=4000
|
PORT=4000
|
||||||
@ -37,6 +37,8 @@ LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
|||||||
|
|
||||||
# EMail
|
# EMail
|
||||||
EMAIL=false
|
EMAIL=false
|
||||||
|
EMAIL_TEST_MODUS=false
|
||||||
|
EMAIL_TEST_RECEIVER=stage1@gradido.net
|
||||||
EMAIL_USERNAME=gradido_email
|
EMAIL_USERNAME=gradido_email
|
||||||
EMAIL_SENDER=info@gradido.net
|
EMAIL_SENDER=info@gradido.net
|
||||||
EMAIL_PASSWORD=xxx
|
EMAIL_PASSWORD=xxx
|
||||||
|
|||||||
@ -36,6 +36,8 @@ LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
|||||||
|
|
||||||
# EMail
|
# EMail
|
||||||
EMAIL=$EMAIL
|
EMAIL=$EMAIL
|
||||||
|
EMAIL_TEST_MODUS=$EMAIL_TEST_MODUS
|
||||||
|
EMAIL_TEST_RECEIVER=$EMAIL_TEST_RECEIVER
|
||||||
EMAIL_USERNAME=$EMAIL_USERNAME
|
EMAIL_USERNAME=$EMAIL_USERNAME
|
||||||
EMAIL_SENDER=$EMAIL_SENDER
|
EMAIL_SENDER=$EMAIL_SENDER
|
||||||
EMAIL_PASSWORD=$EMAIL_PASSWORD
|
EMAIL_PASSWORD=$EMAIL_PASSWORD
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const constants = {
|
|||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
CONFIG_VERSION: {
|
CONFIG_VERSION: {
|
||||||
DEFAULT: 'DEFAULT',
|
DEFAULT: 'DEFAULT',
|
||||||
EXPECTED: 'v9.2022-07-07',
|
EXPECTED: 'v10.2022-09-20',
|
||||||
CURRENT: '',
|
CURRENT: '',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -67,6 +67,8 @@ const loginServer = {
|
|||||||
|
|
||||||
const email = {
|
const email = {
|
||||||
EMAIL: process.env.EMAIL === 'true' || false,
|
EMAIL: process.env.EMAIL === 'true' || false,
|
||||||
|
EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' || 'false',
|
||||||
|
EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER || 'stage1@gradido.net',
|
||||||
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
|
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
|
||||||
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
|
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
|
||||||
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx',
|
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx',
|
||||||
|
|||||||
@ -73,7 +73,7 @@ describe('sendEMail', () => {
|
|||||||
it('calls sendMail of transporter', () => {
|
it('calls sendMail of transporter', () => {
|
||||||
expect((createTransport as jest.Mock).mock.results[0].value.sendMail).toBeCalledWith({
|
expect((createTransport as jest.Mock).mock.results[0].value.sendMail).toBeCalledWith({
|
||||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
||||||
to: 'receiver@mail.org',
|
to: `${CONFIG.EMAIL_TEST_RECEIVER}`,
|
||||||
cc: 'support@gradido.net',
|
cc: 'support@gradido.net',
|
||||||
subject: 'Subject',
|
subject: 'Subject',
|
||||||
text: 'Text text text',
|
text: 'Text text text',
|
||||||
|
|||||||
@ -19,6 +19,12 @@ export const sendEMail = async (emailDef: {
|
|||||||
logger.info(`Emails are disabled via config...`)
|
logger.info(`Emails are disabled via config...`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (CONFIG.EMAIL_TEST_MODUS) {
|
||||||
|
logger.info(
|
||||||
|
`Testmodus=ON: change receiver from ${emailDef.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
|
||||||
|
)
|
||||||
|
emailDef.to = CONFIG.EMAIL_TEST_RECEIVER
|
||||||
|
}
|
||||||
const transporter = createTransport({
|
const transporter = createTransport({
|
||||||
host: CONFIG.EMAIL_SMTP_URL,
|
host: CONFIG.EMAIL_SMTP_URL,
|
||||||
port: Number(CONFIG.EMAIL_SMTP_PORT),
|
port: Number(CONFIG.EMAIL_SMTP_PORT),
|
||||||
|
|||||||
@ -76,6 +76,9 @@ const createServer = async (
|
|||||||
logger,
|
logger,
|
||||||
})
|
})
|
||||||
apollo.applyMiddleware({ app, path: '/' })
|
apollo.applyMiddleware({ app, path: '/' })
|
||||||
|
logger.info(
|
||||||
|
`running with PRODUCTION=${CONFIG.PRODUCTION}, sending EMAIL enabled=${CONFIG.EMAIL} and EMAIL_TEST_MODUS=${CONFIG.EMAIL_TEST_MODUS} ...`,
|
||||||
|
)
|
||||||
logger.debug('createServer...successful')
|
logger.debug('createServer...successful')
|
||||||
return { apollo, app, con }
|
return { apollo, app, con }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,10 +26,11 @@ COMMUNITY_REDEEM_CONTRIBUTION_URL=https://stage1.gradido.net/redeem/CL-{code}
|
|||||||
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
||||||
|
|
||||||
# backend
|
# backend
|
||||||
BACKEND_CONFIG_VERSION=v9.2022-07-07
|
BACKEND_CONFIG_VERSION=v10.2022-09-20
|
||||||
|
|
||||||
JWT_EXPIRES_IN=10m
|
JWT_EXPIRES_IN=10m
|
||||||
GDT_API_URL=https://gdt.gradido.net
|
GDT_API_URL=https://gdt.gradido.net
|
||||||
|
ENV_NAME=stage1
|
||||||
|
|
||||||
TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log
|
TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log
|
||||||
|
|
||||||
@ -40,6 +41,8 @@ KLICKTIPP_APIKEY_DE=
|
|||||||
KLICKTIPP_APIKEY_EN=
|
KLICKTIPP_APIKEY_EN=
|
||||||
|
|
||||||
EMAIL=true
|
EMAIL=true
|
||||||
|
EMAIL_TEST_MODUS=false
|
||||||
|
EMAIL_TEST_RECEIVER=test_team@gradido.net
|
||||||
EMAIL_USERNAME=peter@lustig.de
|
EMAIL_USERNAME=peter@lustig.de
|
||||||
EMAIL_SENDER=peter@lustig.de
|
EMAIL_SENDER=peter@lustig.de
|
||||||
EMAIL_PASSWORD=1234
|
EMAIL_PASSWORD=1234
|
||||||
|
|||||||
@ -131,6 +131,10 @@ envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env
|
|||||||
# Configure admin
|
# Configure admin
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
|
||||||
|
|
||||||
|
# create cronjob to delete yarn output in /tmp
|
||||||
|
# crontab -e
|
||||||
|
# hourly job: 0 * * * * find /tmp -name "yarn--*" -cmin +60 -exec rm -r {} \; > /dev/null
|
||||||
|
# daily job: 0 4 * * * find /tmp -name "yarn--*" -ctime +1 -exec rm -r {} \; > /dev/null
|
||||||
# Start gradido
|
# Start gradido
|
||||||
# Note: on first startup some errors will occur - nothing serious
|
# Note: on first startup some errors will occur - nothing serious
|
||||||
./start.sh
|
./start.sh
|
||||||
@ -95,5 +95,13 @@
|
|||||||
> cp .env.dist .env
|
> cp .env.dist .env
|
||||||
> nano .env
|
> nano .env
|
||||||
>> Adjust values accordingly
|
>> Adjust values accordingly
|
||||||
|
# Define cronjob to compensate yarn output in /tmp
|
||||||
|
> yarn creates output in /tmp directory, which must be deleted regularly and will be done per cronjob
|
||||||
|
> on stage1 a hourly job is necessary by setting the following job in the crontab for the gradido user
|
||||||
|
> crontab -e opens the crontab in edit-mode and insert the following entry:
|
||||||
|
> "0 * * * * find /tmp -name "yarn--*" -cmin +60 -exec rm -r {} \; > /dev/null"
|
||||||
|
> on stage2 a daily job is necessary by setting the following job in the crontab for the gradido user
|
||||||
|
> crontab -e opens the crontab in edit-mode and insert the following entry:
|
||||||
|
> "0 4 * * * find /tmp -name "yarn--*" -ctime +1 -exec rm -r {} \; > /dev/null"
|
||||||
# TODO the install.sh is not yet ready to run directly - consider to use it as pattern to do it manually
|
# TODO the install.sh is not yet ready to run directly - consider to use it as pattern to do it manually
|
||||||
> ./install.sh
|
> ./install.sh
|
||||||
|
|||||||
7
e2e-tests/README.md
Normal file
7
e2e-tests/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Gradido end-to-end tests
|
||||||
|
|
||||||
|
This is still WIP.
|
||||||
|
|
||||||
|
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.
|
||||||
4
e2e-tests/cypress/.gitignore
vendored
Normal file
4
e2e-tests/cypress/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
tests/node_modules/
|
||||||
|
tests/cypress/screenshots/
|
||||||
|
tests/cypress/videos/
|
||||||
|
tests/cucumber-messages.ndjson
|
||||||
36
e2e-tests/cypress/Dockerfile
Normal file
36
e2e-tests/cypress/Dockerfile
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Dockerfile to create a ready-to-use Cypress Docker image for end-to-end
|
||||||
|
# testing.
|
||||||
|
#
|
||||||
|
# Based on the images containing several browsers, provided by Cypress.io
|
||||||
|
# (https://github.com/cypress-io/cypress-docker-images/tree/master/browsers)
|
||||||
|
# this Dockerfile is based a slim Linux Dockerfile using Node.js 16.14.2.
|
||||||
|
#
|
||||||
|
# Here the latest stable versions of the browsers Chromium and Firefox are
|
||||||
|
# installed before installing Cypress.
|
||||||
|
###############################################################################
|
||||||
|
FROM cypress/base:16.14.2-slim
|
||||||
|
|
||||||
|
ARG DOCKER_WORKDIR=/tests/
|
||||||
|
WORKDIR $DOCKER_WORKDIR
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
RUN apt-get -qq update > /dev/null && \
|
||||||
|
apt-get -qq install -y bzip2 mplayer wget > /dev/null
|
||||||
|
|
||||||
|
# install Chromium browser
|
||||||
|
RUN apt-get -qq install -y chromium > /dev/null
|
||||||
|
|
||||||
|
# install Firefox browser
|
||||||
|
RUN wget --no-verbose -O /tmp/firefox.tar.bz2 "https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US" && \
|
||||||
|
tar -C /opt -xjf /tmp/firefox.tar.bz2 && \
|
||||||
|
rm /tmp/firefox.tar.bz2 && \
|
||||||
|
ln -fs /opt/firefox/firefox /usr/bin/firefox
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
RUN rm -rf /var/lib/apt/lists/* && apt-get -qq clean > /dev/null
|
||||||
|
|
||||||
|
COPY tests/package.json tests/yarn.lock $DOCKER_WORKDIR
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
COPY tests/ $DOCKER_WORKDIR
|
||||||
24
e2e-tests/cypress/README.md
Normal file
24
e2e-tests/cypress/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Gradido End-to-End Testing with [Cypress](https://www.cypress.io/) (CI-ready via Docker)
|
||||||
|
|
||||||
|
|
||||||
|
A sample setup to show-case Cypress as an end-to-end testing tool for Gradido running in a Docker container.
|
||||||
|
Here we have a simple UI-based happy path login test running against the DEV system.
|
||||||
|
|
||||||
|
## Precondition
|
||||||
|
Since dependencies and configurations for Github Actions integration is not set up yet, please run in root directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
to boot up the DEV system, before running the test.
|
||||||
|
|
||||||
|
## Execute the test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# build a Docker image from the Dockerfile
|
||||||
|
docker build -t gradido_e2e-tests-cypress .
|
||||||
|
|
||||||
|
# run the Docker container and execute the given tests
|
||||||
|
docker run -it --network=host gradido_e2e-tests-cypress yarn run cypress-e2e-tests
|
||||||
|
```
|
||||||
1
e2e-tests/cypress/tests/.eslintignore
Normal file
1
e2e-tests/cypress/tests/.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
24
e2e-tests/cypress/tests/.eslintrc.js
Normal file
24
e2e-tests/cypress/tests/.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
64
e2e-tests/cypress/tests/cypress.config.ts
Normal file
64
e2e-tests/cypress/tests/cypress.config.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
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<Cypress.PluginConfigOptions> {
|
||||||
|
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,
|
||||||
|
supportFile: "cypress/support/index.ts",
|
||||||
|
viewportHeight: 720,
|
||||||
|
viewportWidth: 1280,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
Feature: User authentication
|
||||||
|
As a user
|
||||||
|
I want to be able to sign in - only with valid credentials
|
||||||
|
In order to be able to posts and do other contributions as myself
|
||||||
|
Furthermore I want to be able to stay logged in and logout again
|
||||||
|
|
||||||
|
# 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: Log in successfully
|
||||||
|
Given the browser navigates to page "/login"
|
||||||
|
When the user submits the credentials "bibi@bloxberg.de" "Aa12345_"
|
||||||
|
Then the user is logged in with username "Bibi Bloxberg"
|
||||||
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
Feature: User profile - change password
|
||||||
|
As a user
|
||||||
|
I want the option to change my password on my profile page.
|
||||||
|
|
||||||
|
Background:
|
||||||
|
# TODO for these pre-conditions utilize seeding or API check, if user exists in test system
|
||||||
|
# Given the following "users" are in the database:
|
||||||
|
# | email | password | name |
|
||||||
|
# | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | |
|
||||||
|
|
||||||
|
# TODO instead of credentials use the name of an user object (see seeds in backend)
|
||||||
|
Given the user is logged in as "bibi@bloxberg.de" "Aa12345_"
|
||||||
|
|
||||||
|
Scenario: Change password successfully
|
||||||
|
Given the browser navigates to page "/profile"
|
||||||
|
And the user opens the change password menu
|
||||||
|
When the user fills the password form with:
|
||||||
|
| Old password | Aa12345_ |
|
||||||
|
| New password | 12345Aa_ |
|
||||||
|
| Repeat new password | 12345Aa_ |
|
||||||
|
And the user submits the password form
|
||||||
|
And the user is presented a "success" message
|
||||||
|
And the user logs out
|
||||||
|
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"
|
||||||
30
e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts
Normal file
30
e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
export class LoginPage {
|
||||||
|
// selectors
|
||||||
|
emailInput = "#Email-input-field";
|
||||||
|
passwordInput = "#Password-input-field";
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
e2e-tests/cypress/tests/cypress/e2e/models/OverviewPage.ts
Normal file
10
e2e-tests/cypress/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/tests/cypress/e2e/models/ProfilePage.ts
Normal file
35
e2e-tests/cypress/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).clear().type(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
enterRepeatPassword(password: string) {
|
||||||
|
cy.get(this.newPasswordRepeatInput).clear().type(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitPasswordForm() {
|
||||||
|
cy.get(this.submitNewPasswordBtn).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
e2e-tests/cypress/tests/cypress/e2e/models/SideNavMenu.ts
Normal file
17
e2e-tests/cypress/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts
Normal file
7
e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
export class Toasts {
|
||||||
|
// selectors
|
||||||
|
toastTitle = ".gdd-toaster-title";
|
||||||
|
toastMessage = ".gdd-toaster-body";
|
||||||
|
}
|
||||||
7
e2e-tests/cypress/tests/cypress/fixtures/users.json
Normal file
7
e2e-tests/cypress/tests/cypress/fixtures/users.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"email": "bibi@bloxberg.de",
|
||||||
|
"password": "Aa12345_",
|
||||||
|
"name": "Bibi Bloxberg"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
e2e-tests/cypress/tests/cypress/support/e2e.ts
Normal file
38
e2e-tests/cypress/tests/cypress/support/e2e.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
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));
|
||||||
|
});
|
||||||
|
});
|
||||||
14
e2e-tests/cypress/tests/cypress/support/index.ts
Normal file
14
e2e-tests/cypress/tests/cypress/support/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-namespace */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import "./e2e";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace Cypress {
|
||||||
|
interface Chainable<Subject> {
|
||||||
|
login(email: string, password: string): Chainable<any>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
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.toastTitle).should("contain.text", "Error!");
|
||||||
|
cy.get(toast.toastMessage).should(
|
||||||
|
"contain.text",
|
||||||
|
"No user with this credentials."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
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();
|
||||||
|
});
|
||||||
@ -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) => {
|
||||||
|
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.toastTitle).should("contain.text", "Success");
|
||||||
|
cy.get(toast.toastMessage).should(
|
||||||
|
"contain.text",
|
||||||
|
"Your password has been changed."
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -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");
|
||||||
|
});
|
||||||
39
e2e-tests/cypress/tests/package.json
Normal file
39
e2e-tests/cypress/tests/package.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "gradido-e2e-tests-cypress",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "End-to-end tests with Cypress",
|
||||||
|
"main": "yarn run cypress run",
|
||||||
|
"repository": "https://github.com/gradido/gradido/e2e-tests/cypress",
|
||||||
|
"author": "Mathias Lenz",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"private": false,
|
||||||
|
"cypress-cucumber-preprocessor": {
|
||||||
|
"nonGlobalStepDefinitions": true,
|
||||||
|
"json": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"cypress": "cypress run",
|
||||||
|
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@badeball/cypress-cucumber-preprocessor": "^12.0.0",
|
||||||
|
"@cypress/browserify-preprocessor": "^3.0.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||||
|
"@typescript-eslint/parser": "^5.38.0",
|
||||||
|
"cypress": "^10.4.0",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-config-standard": "^16.0.3",
|
||||||
|
"eslint-loader": "^4.0.2",
|
||||||
|
"eslint-plugin-cypress": "^2.12.1",
|
||||||
|
"eslint-plugin-import": "^2.23.4",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
e2e-tests/cypress/tests/tsconfig.json
Normal file
10
e2e-tests/cypress/tests/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": "../node_modules",
|
||||||
|
"types": ["cypress", "node"],
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
||||||
5169
e2e-tests/cypress/tests/yarn.lock
Normal file
5169
e2e-tests/cypress/tests/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
42
e2e-tests/playwright/Dockerfile
Normal file
42
e2e-tests/playwright/Dockerfile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Dockerfile to create a ready-to-use Playwright Docker image for end-to-end
|
||||||
|
# testing.
|
||||||
|
#
|
||||||
|
# To avoid hardcoded versoning of Playwright, this Dockerfile is a custom
|
||||||
|
# version of the ready-to-use Dockerfile privided by Playwright developement
|
||||||
|
# (https://github.com/microsoft/playwright/blob/main/utils/docker/Dockerfile.focal)
|
||||||
|
#
|
||||||
|
# Here the latest stable versions of the browsers Chromium, Firefox, and Webkit
|
||||||
|
# (Safari) are installed, icluding all dependencies based on Ubuntu specified by
|
||||||
|
# Playwright developement.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
FROM ubuntu:focal
|
||||||
|
|
||||||
|
# set a timezone for the Playwright browser dependency installation
|
||||||
|
ARG TZ=Europe/Berlin
|
||||||
|
|
||||||
|
ARG DOCKER_WORKDIR=/tests/
|
||||||
|
WORKDIR $DOCKER_WORKDIR
|
||||||
|
|
||||||
|
# package manager preparation
|
||||||
|
RUN apt-get -qq update && apt-get install -qq -y curl gpg > /dev/null
|
||||||
|
# for Node.js
|
||||||
|
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
|
||||||
|
# for Yarn
|
||||||
|
RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
||||||
|
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
|
||||||
|
# install node v16 and Yarn
|
||||||
|
RUN apt-get -qq update && apt-get install -qq -y nodejs yarn
|
||||||
|
|
||||||
|
COPY tests/package.json tests/yarn.lock $DOCKER_WORKDIR
|
||||||
|
|
||||||
|
# install Playwright with all dependencies
|
||||||
|
# for the browsers chromium, firefox, and webkit
|
||||||
|
RUN yarn install && yarn playwright install --with-deps
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
RUN rm -rf /var/lib/apt/lists/* && apt-get -qq clean
|
||||||
|
|
||||||
|
COPY tests/ $DOCKER_WORKDIR
|
||||||
24
e2e-tests/playwright/README.md
Normal file
24
e2e-tests/playwright/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Gradido End-to-End Testing with [Playwright](https://playwright.dev/) (CI-ready via Docker)
|
||||||
|
|
||||||
|
|
||||||
|
A sample setup to show-case Playwright (using Typescript) as an end-to-end testing tool for Gradido runniing in a Docker container.
|
||||||
|
Here we have a simple UI-based happy path login test running against the DEV system.
|
||||||
|
|
||||||
|
## Precondition
|
||||||
|
Since dependencies and configurations for Github Actions integration is not set up yet, please run in root directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
to boot up the DEV system, before running the test.
|
||||||
|
|
||||||
|
## Execute the test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# build a Docker image from the Dockerfile
|
||||||
|
docker build -t gradido_e2e-tests-playwright .
|
||||||
|
|
||||||
|
# run the Docker container and execute the given tests
|
||||||
|
docker run -it --network=host gradido_e2e-tests-playwright yarn playwright-e2e-tests
|
||||||
|
```
|
||||||
8
e2e-tests/playwright/tests/global-setup.ts
Normal file
8
e2e-tests/playwright/tests/global-setup.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { FullConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
async function globalSetup(config: FullConfig) {
|
||||||
|
process.env.EMAIL = 'bibi@bloxberg.de';
|
||||||
|
process.env.PASSWORD = 'Aa12345_';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default globalSetup;
|
||||||
15
e2e-tests/playwright/tests/gradido_login.spec.ts
Normal file
15
e2e-tests/playwright/tests/gradido_login.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { LoginPage } from './models/login_page';
|
||||||
|
import { WelcomePage } from './models/welcome_page';
|
||||||
|
|
||||||
|
|
||||||
|
test('Gradido login test (happy path)', async ({ page }) => {
|
||||||
|
const { EMAIL, PASSWORD } = process.env;
|
||||||
|
const loginPage = new LoginPage(page);
|
||||||
|
await loginPage.goto();
|
||||||
|
await loginPage.enterEmail(EMAIL);
|
||||||
|
await loginPage.enterPassword(PASSWORD);
|
||||||
|
await loginPage.submitLogin();
|
||||||
|
// assertions
|
||||||
|
await expect(page).toHaveURL('./overview');
|
||||||
|
});
|
||||||
33
e2e-tests/playwright/tests/models/login_page.ts
Normal file
33
e2e-tests/playwright/tests/models/login_page.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { expect, test, Locator, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
export class LoginPage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly url: string;
|
||||||
|
readonly emailInput: Locator;
|
||||||
|
readonly passwordInput: Locator;
|
||||||
|
readonly submitBtn: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
this.url = './login';
|
||||||
|
this.emailInput = page.locator('id=Email-input-field');
|
||||||
|
this.passwordInput = page.locator('id=Password-input-field');
|
||||||
|
this.submitBtn = page.locator('text=Login');
|
||||||
|
}
|
||||||
|
|
||||||
|
async goto() {
|
||||||
|
await this.page.goto(this.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async enterEmail(email: string) {
|
||||||
|
await this.emailInput.fill(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
async enterPassword(password: string) {
|
||||||
|
await this.passwordInput.fill(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitLogin() {
|
||||||
|
await this.submitBtn.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
e2e-tests/playwright/tests/models/welcome_page.ts
Normal file
13
e2e-tests/playwright/tests/models/welcome_page.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { expect, Locator, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
export class WelcomePage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly url: string;
|
||||||
|
readonly profileLink: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page){
|
||||||
|
this.page = page;
|
||||||
|
this.url = './overview';
|
||||||
|
this.profileLink = page.locator('href=/profile');
|
||||||
|
}
|
||||||
|
}
|
||||||
21
e2e-tests/playwright/tests/playwright.config.ts
Normal file
21
e2e-tests/playwright/tests/playwright.config.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
globalSetup: require.resolve('./global-setup'),
|
||||||
|
ignoreHTTPSErrors: true,
|
||||||
|
locale: 'de-DE',
|
||||||
|
reporter: process.env.CI ? 'github' : 'list',
|
||||||
|
retries: 1,
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
testDir: '.',
|
||||||
|
timeout: 30000,
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
video: 'never',
|
||||||
|
viewport: { width: 1280, height: 720 },
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.URL || 'http://localhost:3000',
|
||||||
|
browserName: 'webkit',
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@ -12,6 +12,7 @@
|
|||||||
atLeastOneSpecialCharater: true,
|
atLeastOneSpecialCharater: true,
|
||||||
noWhitespaceCharacters: true,
|
noWhitespaceCharacters: true,
|
||||||
}"
|
}"
|
||||||
|
id="new-password-input-field"
|
||||||
:label="register ? $t('form.password') : $t('form.password_new')"
|
:label="register ? $t('form.password') : $t('form.password_new')"
|
||||||
:showAllErrors="true"
|
:showAllErrors="true"
|
||||||
:immediate="true"
|
:immediate="true"
|
||||||
|
|||||||
@ -14,7 +14,12 @@
|
|||||||
<b-icon v-if="pending" icon="three-dots" animation="cylon"></b-icon>
|
<b-icon v-if="pending" icon="three-dots" animation="cylon"></b-icon>
|
||||||
<div v-else>{{ pending ? $t('em-dash') : balance | amount }} {{ $t('GDD') }}</div>
|
<div v-else>{{ pending ? $t('em-dash') : balance | amount }} {{ $t('GDD') }}</div>
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
<b-nav-item to="/profile" right class="d-none d-sm-none d-md-none d-lg-flex shadow-lg">
|
<b-nav-item
|
||||||
|
to="/profile"
|
||||||
|
right
|
||||||
|
class="d-none d-sm-none d-md-none d-lg-flex shadow-lg"
|
||||||
|
data-test="navbar-item-username"
|
||||||
|
>
|
||||||
<small>
|
<small>
|
||||||
{{ $store.state.firstName }} {{ $store.state.lastName }}
|
{{ $store.state.firstName }} {{ $store.state.lastName }}
|
||||||
<b>{{ $store.state.email }}</b>
|
<b>{{ $store.state.email }}</b>
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
<b-icon icon="people" aria-hidden="true"></b-icon>
|
<b-icon icon="people" aria-hidden="true"></b-icon>
|
||||||
{{ $t('navigation.community') }}
|
{{ $t('navigation.community') }}
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
<b-nav-item to="/profile" class="mb-3">
|
<b-nav-item to="/profile" class="mb-3" data-test="profile-menu">
|
||||||
<b-icon icon="gear" aria-hidden="true"></b-icon>
|
<b-icon icon="gear" aria-hidden="true"></b-icon>
|
||||||
{{ $t('navigation.profile') }}
|
{{ $t('navigation.profile') }}
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<b-icon icon="shield-check" aria-hidden="true"></b-icon>
|
<b-icon icon="shield-check" aria-hidden="true"></b-icon>
|
||||||
{{ $t('navigation.admin_area') }}
|
{{ $t('navigation.admin_area') }}
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
<b-nav-item class="mb-3" @click="$emit('logout')">
|
<b-nav-item class="mb-3" @click="$emit('logout')" data-test="logout-menu">
|
||||||
<b-icon icon="power" aria-hidden="true"></b-icon>
|
<b-icon icon="power" aria-hidden="true"></b-icon>
|
||||||
{{ $t('navigation.logout') }}
|
{{ $t('navigation.logout') }}
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
<a
|
<a
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="showPassword ? (showPassword = !showPassword) : cancelEdit()"
|
@click="showPassword ? (showPassword = !showPassword) : cancelEdit()"
|
||||||
|
data-test="open-password-change-form"
|
||||||
>
|
>
|
||||||
<span class="pointer mr-3">{{ $t('settings.password.change-password') }}</span>
|
<span class="pointer mr-3">{{ $t('settings.password.change-password') }}</span>
|
||||||
<b-icon v-if="showPassword" class="pointer ml-3" icon="pencil"></b-icon>
|
<b-icon v-if="showPassword" class="pointer ml-3" icon="pencil"></b-icon>
|
||||||
@ -36,6 +37,7 @@
|
|||||||
:variant="disabled ? 'light' : 'success'"
|
:variant="disabled ? 'light' : 'success'"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
data-test="submit-new-password-btn"
|
||||||
>
|
>
|
||||||
{{ $t('form.save') }}
|
{{ $t('form.save') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user