From 65965a2ffd40c7f07b734de8cd0844b0ab8f46b8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 20 Jun 2023 11:34:48 +0200 Subject: [PATCH 01/11] add MODERATOR contribution message type --- backend/src/graphql/arg/ContributionMessageArgs.ts | 5 +++++ .../enum/{MessageType.ts => ContributionMessageType.ts} | 1 + backend/src/graphql/resolver/ContributionMessageResolver.ts | 2 +- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) rename backend/src/graphql/enum/{MessageType.ts => ContributionMessageType.ts} (73%) diff --git a/backend/src/graphql/arg/ContributionMessageArgs.ts b/backend/src/graphql/arg/ContributionMessageArgs.ts index d36e1832d..6482793aa 100644 --- a/backend/src/graphql/arg/ContributionMessageArgs.ts +++ b/backend/src/graphql/arg/ContributionMessageArgs.ts @@ -1,5 +1,7 @@ import { ArgsType, Field, Int, InputType } from 'type-graphql' +import { ContributionMessageType } from '@enum/ContributionMessageType' + @InputType() @ArgsType() export class ContributionMessageArgs { @@ -8,4 +10,7 @@ export class ContributionMessageArgs { @Field(() => String) message: string + + @Field(() => ContributionMessageType, { defaultValue: ContributionMessageType.DIALOG }) + messageType: ContributionMessageType } diff --git a/backend/src/graphql/enum/MessageType.ts b/backend/src/graphql/enum/ContributionMessageType.ts similarity index 73% rename from backend/src/graphql/enum/MessageType.ts rename to backend/src/graphql/enum/ContributionMessageType.ts index a4606e464..83b011a0b 100644 --- a/backend/src/graphql/enum/MessageType.ts +++ b/backend/src/graphql/enum/ContributionMessageType.ts @@ -3,6 +3,7 @@ import { registerEnumType } from 'type-graphql' export enum ContributionMessageType { HISTORY = 'HISTORY', DIALOG = 'DIALOG', + MODERATOR = 'MODERATOR', // messages for moderator communication, can only be seen by moderators } registerEnumType(ContributionMessageType, { diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index b7fd37787..56a7a1ec9 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -8,8 +8,8 @@ import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type import { ContributionMessageArgs } from '@arg/ContributionMessageArgs' import { Paginated } from '@arg/Paginated' +import { ContributionMessageType } from '@enum/ContributionMessageType' import { ContributionStatus } from '@enum/ContributionStatus' -import { ContributionMessageType } from '@enum/MessageType' import { Order } from '@enum/Order' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index fa1590523..42ec2f371 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -11,9 +11,9 @@ import { AdminCreateContributionArgs } from '@arg/AdminCreateContributionArgs' import { AdminUpdateContributionArgs } from '@arg/AdminUpdateContributionArgs' import { ContributionArgs } from '@arg/ContributionArgs' import { Paginated } from '@arg/Paginated' +import { ContributionMessageType } from '@enum/ContributionMessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { ContributionType } from '@enum/ContributionType' -import { ContributionMessageType } from '@enum/MessageType' import { Order } from '@enum/Order' import { TransactionTypeId } from '@enum/TransactionTypeId' import { AdminUpdateContribution } from '@model/AdminUpdateContribution' From 732560aaac7e960ac606897c83cb9a51f9a9585f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 20 Jun 2023 12:11:44 +0200 Subject: [PATCH 02/11] add message type arg to admin create contribution message --- .../ContributionMessageResolver.test.ts | 36 ++++++++++++++++++- .../resolver/ContributionMessageResolver.ts | 4 +-- backend/src/seeds/graphql/mutations.ts | 8 +++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 1b6b034c4..f3b7a636e 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -217,6 +217,33 @@ describe('ContributionMessageResolver', () => { ) }) }) + + describe('contribution message type MODERATOR', () => { + it('creates ContributionMessage', async () => { + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Internal moderator communication', + messageType: 'MODERATOR', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + adminCreateContributionMessage: expect.objectContaining({ + id: expect.any(Number), + message: 'Internal moderator communication', + type: 'MODERATOR', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), + }, + }), + ) + }) + }) }) }) @@ -395,7 +422,7 @@ describe('ContributionMessageResolver', () => { expect.objectContaining({ data: { listContributionMessages: { - count: 2, + count: 3, messages: expect.arrayContaining([ expect.objectContaining({ id: expect.any(Number), @@ -411,6 +438,13 @@ describe('ContributionMessageResolver', () => { userFirstName: 'Bibi', userLastName: 'Bloxberg', }), + expect.objectContaining({ + id: expect.any(Number), + message: 'Internal moderator communication', + type: 'MODERATOR', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), ]), }, }, diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 56a7a1ec9..25f2a2560 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -104,7 +104,7 @@ export class ContributionMessageResolver { @Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTION_MESSAGE]) @Mutation(() => ContributionMessage) async adminCreateContributionMessage( - @Args() { contributionId, message }: ContributionMessageArgs, + @Args() { contributionId, message, messageType }: ContributionMessageArgs, @Ctx() context: Context, ): Promise { const moderator = getUser(context) @@ -133,7 +133,7 @@ export class ContributionMessageResolver { contributionMessage.createdAt = new Date() contributionMessage.message = message contributionMessage.userId = moderator.id - contributionMessage.type = ContributionMessageType.DIALOG + contributionMessage.type = messageType contributionMessage.isModerator = true await queryRunner.manager.insert(DbContributionMessage, contributionMessage) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 22e0b1b09..29d08b20a 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -284,8 +284,12 @@ export const createContributionMessage = gql` ` export const adminCreateContributionMessage = gql` - mutation ($contributionId: Int!, $message: String!) { - adminCreateContributionMessage(contributionId: $contributionId, message: $message) { + mutation ($contributionId: Int!, $message: String!, $messageType: ContributionMessageType) { + adminCreateContributionMessage( + contributionId: $contributionId + message: $message + messageType: $messageType + ) { id message createdAt From 4d119f8911d36fa863f58b7900e52feb3815cce8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 20 Jun 2023 12:44:35 +0200 Subject: [PATCH 03/11] find contribution messages helper function --- .../resolver/ContributionMessageResolver.ts | 18 ++++++------- .../resolver/util/findContributionMessages.ts | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 backend/src/graphql/resolver/util/findContributionMessages.ts diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 25f2a2560..69931467b 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -22,6 +22,8 @@ import { import { Context, getUser } from '@/server/context' import { LogError } from '@/server/LogError' +import { findContributionMessages } from './util/findContributionMessages' + @Resolver() export class ContributionMessageResolver { @Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE]) @@ -82,16 +84,12 @@ export class ContributionMessageResolver { @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, ): Promise { - const [contributionMessages, count] = await getConnection() - .createQueryBuilder() - .select('cm') - .from(DbContributionMessage, 'cm') - .leftJoinAndSelect('cm.user', 'u') - .where({ contributionId }) - .orderBy('cm.createdAt', order) - .limit(pageSize) - .offset((currentPage - 1) * pageSize) - .getManyAndCount() + const [contributionMessages, count] = await findContributionMessages({ + contributionId, + currentPage, + pageSize, + order, + }) return { count, diff --git a/backend/src/graphql/resolver/util/findContributionMessages.ts b/backend/src/graphql/resolver/util/findContributionMessages.ts new file mode 100644 index 000000000..90555cca7 --- /dev/null +++ b/backend/src/graphql/resolver/util/findContributionMessages.ts @@ -0,0 +1,27 @@ +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' + +import { Order } from '@enum/Order' + +interface FindContributionMessagesOptions { + contributionId: number + pageSize: number + currentPage: number + order: Order +} + +export const findContributionMessages = async ( + options: FindContributionMessagesOptions, +): Promise<[DbContributionMessage[], number]> => { + const { contributionId, pageSize, currentPage, order } = options + return DbContributionMessage.findAndCount({ + where: { + contributionId, + }, + relations: ['user'], + order: { + createdAt: order, + }, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) +} From 1e6d4cfe4b00d265c03f09da21752520f08f52d6 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 20 Jun 2023 13:11:36 +0200 Subject: [PATCH 04/11] add admin list contribution message query --- backend/src/auth/RIGHTS.ts | 1 + .../ContributionMessageResolver.test.ts | 82 ++++++++++++++++++- .../resolver/ContributionMessageResolver.ts | 23 ++++++ .../resolver/util/findContributionMessages.ts | 11 ++- backend/src/seeds/graphql/queries.ts | 23 ++++++ 5 files changed, 137 insertions(+), 3 deletions(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index b3627ff7a..772c907cb 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -53,4 +53,5 @@ export enum RIGHTS { ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', ADMIN_OPEN_CREATIONS = 'ADMIN_OPEN_CREATIONS', + ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES = 'ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES', } diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index f3b7a636e..f80fce939 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -20,7 +20,7 @@ import { createContributionMessage, login, } from '@/seeds/graphql/mutations' -import { listContributionMessages } from '@/seeds/graphql/queries' +import { listContributionMessages, adminListContributionMessages } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' @@ -412,7 +412,7 @@ describe('ContributionMessageResolver', () => { resetToken() }) - it('returns a list of contributionmessages', async () => { + it('returns a list of contributionmessages without type MODERATOR', async () => { await expect( mutate({ mutation: listContributionMessages, @@ -422,6 +422,84 @@ describe('ContributionMessageResolver', () => { expect.objectContaining({ data: { listContributionMessages: { + count: 2, + messages: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + message: 'Admin Test', + type: 'DIALOG', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), + expect.objectContaining({ + id: expect.any(Number), + message: 'User Test', + type: 'DIALOG', + userFirstName: 'Bibi', + userLastName: 'Bloxberg', + }), + ]), + }, + }, + }), + ) + }) + }) + }) + + describe('adminListContributionMessages', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: adminListContributionMessages, + variables: { contributionId: 1 }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated as user', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: adminListContributionMessages, + variables: { contributionId: 1 }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(() => { + resetToken() + }) + + it('returns a list of contributionmessages with type MODERATOR', async () => { + await expect( + mutate({ + mutation: adminListContributionMessages, + variables: { contributionId: result.data.createContribution.id }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + adminListContributionMessages: { count: 3, messages: expect.arrayContaining([ expect.objectContaining({ diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 69931467b..6e062fe6e 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -99,6 +99,29 @@ export class ContributionMessageResolver { } } + @Authorized([RIGHTS.ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES]) + @Query(() => ContributionMessageListResult) + async adminListContributionMessages( + @Arg('contributionId', () => Int) contributionId: number, + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + ): Promise { + const [contributionMessages, count] = await findContributionMessages({ + contributionId, + currentPage, + pageSize, + order, + showModeratorType: true, + }) + + return { + count, + messages: contributionMessages.map( + (message) => new ContributionMessage(message, message.user), + ), + } + } + @Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTION_MESSAGE]) @Mutation(() => ContributionMessage) async adminCreateContributionMessage( diff --git a/backend/src/graphql/resolver/util/findContributionMessages.ts b/backend/src/graphql/resolver/util/findContributionMessages.ts index 90555cca7..06b896898 100644 --- a/backend/src/graphql/resolver/util/findContributionMessages.ts +++ b/backend/src/graphql/resolver/util/findContributionMessages.ts @@ -1,5 +1,7 @@ +import { In } from '@dbTools/typeorm' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' +import { ContributionMessageType } from '@enum/ContributionMessageType' import { Order } from '@enum/Order' interface FindContributionMessagesOptions { @@ -7,15 +9,22 @@ interface FindContributionMessagesOptions { pageSize: number currentPage: number order: Order + showModeratorType?: boolean } export const findContributionMessages = async ( options: FindContributionMessagesOptions, ): Promise<[DbContributionMessage[], number]> => { - const { contributionId, pageSize, currentPage, order } = options + const { contributionId, pageSize, currentPage, order, showModeratorType } = options + + const messageTypes = [ContributionMessageType.DIALOG, ContributionMessageType.HISTORY] + + if (showModeratorType) messageTypes.push(ContributionMessageType.MODERATOR) + return DbContributionMessage.findAndCount({ where: { contributionId, + type: In(messageTypes), }, relations: ['user'], order: { diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index a964cdb3a..f82882f97 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -349,6 +349,29 @@ export const listContributionMessages = gql` } ` +export const adminListContributionMessages = gql` + query ($contributionId: Int!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) { + adminListContributionMessages( + contributionId: $contributionId + pageSize: $pageSize + currentPage: $currentPage + order: $order + ) { + count + messages { + id + message + createdAt + updatedAt + type + userFirstName + userLastName + userId + } + } + } +` + export const user = gql` query ($identifier: String!) { user(identifier: $identifier) { From f0eae909b99118e78961f1c80751a28f356d9168 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 20 Jun 2023 13:16:35 +0200 Subject: [PATCH 05/11] fix authentication test --- .../graphql/resolver/ContributionMessageResolver.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index f80fce939..2df996c64 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -464,6 +464,13 @@ describe('ContributionMessageResolver', () => { }) describe('authenticated as user', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + it('returns an error', async () => { await expect( mutate({ From 689705f465498287447b56f30fbe3a7964731573 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 27 Jun 2023 14:03:03 +0200 Subject: [PATCH 06/11] improved describe --- .../src/graphql/resolver/ContributionMessageResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 2df996c64..12df7f056 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -485,7 +485,7 @@ describe('ContributionMessageResolver', () => { }) }) - describe('authenticated', () => { + describe('authenticated as admin', () => { beforeAll(async () => { await mutate({ mutation: login, From 42fc73ab1b2d1b333b327abc08ed664355acf722 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 28 Jun 2023 09:38:28 +0200 Subject: [PATCH 07/11] fix linting config for e2e-tests directory --- e2e-tests/.eslintignore | 1 + e2e-tests/.eslintrc.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e-tests/.eslintignore b/e2e-tests/.eslintignore index 3c3629e64..48192fb7f 100644 --- a/e2e-tests/.eslintignore +++ b/e2e-tests/.eslintignore @@ -1 +1,2 @@ node_modules +playwright diff --git a/e2e-tests/.eslintrc.js b/e2e-tests/.eslintrc.js index 98f13d176..49c60ba99 100644 --- a/e2e-tests/.eslintrc.js +++ b/e2e-tests/.eslintrc.js @@ -2,13 +2,13 @@ module.exports = { root: true, env: { node: true, - cypress: true, }, parser: '@typescript-eslint/parser', plugins: ['cypress', 'prettier', '@typescript-eslint' /*, 'jest' */], extends: [ 'standard', 'eslint:recommended', + 'plugin:cypress/recommended', 'plugin:prettier/recommended', 'plugin:@typescript-eslint/recommended', ], From 61133870e76f0fccf5e4517fa639288b83d26b4b Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 28 Jun 2023 09:42:34 +0200 Subject: [PATCH 08/11] lint cypress config --- e2e-tests/cypress.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e-tests/cypress.config.ts b/e2e-tests/cypress.config.ts index e26259626..1424ab99f 100644 --- a/e2e-tests/cypress.config.ts +++ b/e2e-tests/cypress.config.ts @@ -6,7 +6,7 @@ let emailLink: string async function setupNodeEvents( on: Cypress.PluginEvents, - config: Cypress.PluginConfigOptions + config: Cypress.PluginConfigOptions, ): Promise { await addCucumberPreprocessorPlugin(on, config) @@ -14,7 +14,7 @@ async function setupNodeEvents( 'file:preprocessor', browserify(config, { typescript: require.resolve('typescript'), - }) + }), ) on('task', { From a0af5d87ae576514ee0085284e196154d4d1805c Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 28 Jun 2023 09:46:06 +0200 Subject: [PATCH 09/11] lint e2e model files --- e2e-tests/cypress/e2e/models/OverviewPage.ts | 1 - .../cypress/e2e/models/ResetPasswordPage.ts | 4 +--- e2e-tests/cypress/e2e/models/SendPage.ts | 17 ++++------------- e2e-tests/cypress/e2e/models/UserEMailSite.ts | 5 +---- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/e2e-tests/cypress/e2e/models/OverviewPage.ts b/e2e-tests/cypress/e2e/models/OverviewPage.ts index e8f881eba..1ee9fa47b 100644 --- a/e2e-tests/cypress/e2e/models/OverviewPage.ts +++ b/e2e-tests/cypress/e2e/models/OverviewPage.ts @@ -3,7 +3,6 @@ export class OverviewPage { navbarName = '[data-test="navbar-item-username"]' rightLastTransactionsList = '.rightside-last-transactions' - goto() { cy.visit('/overview') diff --git a/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts b/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts index 153b41a82..7131be3c8 100644 --- a/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts +++ b/e2e-tests/cypress/e2e/models/ResetPasswordPage.ts @@ -14,9 +14,7 @@ export class ResetPasswordPage { } repeatNewPassword(password: string) { - cy.get(this.newPasswordRepeatInput) - .find('input[type=password]') - .type(password) + cy.get(this.newPasswordRepeatInput).find('input[type=password]').type(password) return this } diff --git a/e2e-tests/cypress/e2e/models/SendPage.ts b/e2e-tests/cypress/e2e/models/SendPage.ts index 07b531c4a..25bf8c02b 100644 --- a/e2e-tests/cypress/e2e/models/SendPage.ts +++ b/e2e-tests/cypress/e2e/models/SendPage.ts @@ -5,30 +5,21 @@ export class SendPage { submitBtn = '.btn-gradido' enterReceiverEmail(email: string) { - cy.get('[data-test="input-identifier"]').find('input') - .clear() - .type(email) + cy.get('[data-test="input-identifier"]').find('input').clear().type(email) return this } enterAmount(amount: string) { - cy.get('[data-test="input-amount"]') - .find('input') - .clear() - .type(amount) + cy.get('[data-test="input-amount"]').find('input').clear().type(amount) return this } enterMemoText(text: string) { - cy.get('[data-test="input-textarea"]') - .find('textarea') - .clear() - .type(text) + cy.get('[data-test="input-textarea"]').find('textarea').clear().type(text) return this } submit() { - cy.get(this.submitBtn) - .click() + cy.get(this.submitBtn).click() } } diff --git a/e2e-tests/cypress/e2e/models/UserEMailSite.ts b/e2e-tests/cypress/e2e/models/UserEMailSite.ts index f46f5677b..af5deefeb 100644 --- a/e2e-tests/cypress/e2e/models/UserEMailSite.ts +++ b/e2e-tests/cypress/e2e/models/UserEMailSite.ts @@ -8,10 +8,7 @@ export class UserEMailSite { emailSubject = '.subject' openRecentPasswordResetEMail() { - cy.get(this.emailList) - .find('email-item') - .filter(':contains(asswor)') - .click() + cy.get(this.emailList).find('email-item').filter(':contains(asswor)').click() expect(cy.get(this.emailSubject)).to('contain', 'asswor') } } From 8db179c5174d114a369a036b446c3c348a398f09 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 28 Jun 2023 09:48:11 +0200 Subject: [PATCH 10/11] lint e2e stepdefinition files files --- .../support/step_definitions/common_steps.ts | 9 +- .../support/step_definitions/email_steps.ts | 81 +++++++++--------- .../step_definitions/send_coin_steps.ts | 82 ++++++++++++------- .../user_authentication_steps.ts | 32 +++----- .../user_profile_change_password_steps.ts | 2 +- .../user_registration_steps.ts | 2 +- 6 files changed, 111 insertions(+), 97 deletions(-) diff --git a/e2e-tests/cypress/support/step_definitions/common_steps.ts b/e2e-tests/cypress/support/step_definitions/common_steps.ts index c5d3004ac..656ffb54f 100644 --- a/e2e-tests/cypress/support/step_definitions/common_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/common_steps.ts @@ -9,12 +9,9 @@ Given('the user navigates to page {string}', (page: string) => { // login related -Given( - 'the user is logged in as {string} {string}', - (email: string, password: string) => { - cy.login(email, password) - } -) +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() diff --git a/e2e-tests/cypress/support/step_definitions/email_steps.ts b/e2e-tests/cypress/support/step_definitions/email_steps.ts index 31141e574..c6ef5e376 100644 --- a/e2e-tests/cypress/support/step_definitions/email_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/email_steps.ts @@ -23,13 +23,15 @@ Then('the user receives an e-mail containing the {string} link', (linkName: stri linkPattern = /\/overview/ break default: - throw new Error(`Error in "Then the user receives an e-mail containing the {string} link" step: incorrect linkname string "${linkName}"`) + throw new Error( + `Error in "Then the user receives an e-mail containing the {string} link" step: incorrect linkname string "${linkName}"`, + ) } - + cy.origin( Cypress.env('mailserverURL'), { args: { emailSubject, linkPattern, userEMailSite } }, - ({ emailSubject, linkPattern, userEMailSite }) => { + ({ emailSubject, linkPattern, userEMailSite }) => { cy.visit('/') // navigate to user's e-mail site (on fake mail server) cy.get(userEMailSite.emailInbox).should('be.visible') @@ -39,11 +41,9 @@ Then('the user receives an e-mail containing the {string} link', (linkName: stri .first() .click() - cy.get(userEMailSite.emailMeta) - .find(userEMailSite.emailSubject) - .contains(emailSubject) + cy.get(userEMailSite.emailMeta).find(userEMailSite.emailSubject).contains(emailSubject) - cy.get('.email-content', { timeout: 2000}) + cy.get('.email-content', { timeout: 2000 }) .find('.plain-text') .contains(linkPattern) .invoke('text') @@ -51,41 +51,42 @@ Then('the user receives an e-mail containing the {string} link', (linkName: stri const emailLink = text.match(linkPattern)[0] cy.task('setEmailLink', emailLink) }) - } + }, ) }) -And('the user receives the transaction e-mail about {string} GDD from {string}', (amount: string, senderName:string) => { - cy.origin( - Cypress.env('mailserverURL'), - { args: { amount, senderName, userEMailSite } }, - ({ amount, senderName, userEMailSite }) => { - const subject = `${senderName} hat dir ${amount} Gradido gesendet` - const linkPattern = /\/transactions/ - cy.visit('/') - cy.get(userEMailSite.emailInbox).should('be.visible') +And( + 'the user receives the transaction e-mail about {string} GDD from {string}', + (amount: string, senderName: string) => { + cy.origin( + Cypress.env('mailserverURL'), + { args: { amount, senderName, userEMailSite } }, + ({ amount, senderName, userEMailSite }) => { + const subject = `${senderName} hat dir ${amount} Gradido gesendet` + const linkPattern = /\/transactions/ + cy.visit('/') + cy.get(userEMailSite.emailInbox).should('be.visible') - cy.get(userEMailSite.emailList) - .find('.email-item') - .filter(`:contains(${subject})`) - .first() - .click() - - cy.get(userEMailSite.emailMeta) - .find(userEMailSite.emailSubject) - .contains(subject) - - cy.get('.email-content', { timeout: 2000}) - .find('.plain-text') - .contains(linkPattern) - .invoke('text') - .then((text) => { - const emailLink = text.match(linkPattern)[0] - cy.task('setEmailLink', emailLink) - }) - } - ) -}) + cy.get(userEMailSite.emailList) + .find('.email-item') + .filter(`:contains(${subject})`) + .first() + .click() + + cy.get(userEMailSite.emailMeta).find(userEMailSite.emailSubject).contains(subject) + + cy.get('.email-content', { timeout: 2000 }) + .find('.plain-text') + .contains(linkPattern) + .invoke('text') + .then((text) => { + const emailLink = text.match(linkPattern)[0] + cy.task('setEmailLink', emailLink) + }) + }, + ) + }, +) When('the user opens the {string} link in the browser', (linkName: string) => { const resetPasswordPage = new ResetPasswordPage() @@ -105,6 +106,8 @@ When('the user opens the {string} link in the browser', (linkName: string) => { cy.get(overviewPage.rightLastTransactionsList).should('be.visible') break default: - throw new Error(`Error in "Then the user receives an e-mail containing the {string} link" step: incorrect link name string "${linkName}"`) + throw new Error( + `Error in "Then the user receives an e-mail containing the {string} link" step: incorrect link name string "${linkName}"`, + ) } }) diff --git a/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts b/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts index ba9eab4b4..707b8a147 100644 --- a/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts @@ -9,7 +9,7 @@ When( sendPage.enterReceiverEmail(email) sendPage.enterAmount(amount) sendPage.enterMemoText(memoText) - } + }, ) And('the user submits the send form', () => { @@ -17,15 +17,23 @@ And('the user submits the send form', () => { cy.get(sendPage.confirmationBox).should('be.visible') }) - -Then('the transaction details are presented for confirmation {string} {string} {string} {string} {string}', (receiverEmail: string, sendAmount: string, memoText: string, senderBalance: string, newSenderBalance: string) => { - cy.get('.transaction-confirm-send').contains(receiverEmail) - cy.get('.transaction-confirm-send').contains(`+ ${sendAmount} GDD`) - cy.get('.transaction-confirm-send').contains(memoText) - cy.get('.transaction-confirm-send').contains(`+ ${senderBalance} GDD`) - cy.get('.transaction-confirm-send').contains(`− ${sendAmount} GDD`) - cy.get('.transaction-confirm-send').contains(`+ ${newSenderBalance} GDD`) -}) +Then( + 'the transaction details are presented for confirmation {string} {string} {string} {string} {string}', + ( + receiverEmail: string, + sendAmount: string, + memoText: string, + senderBalance: string, + newSenderBalance: string, + ) => { + cy.get('.transaction-confirm-send').contains(receiverEmail) + cy.get('.transaction-confirm-send').contains(`+ ${sendAmount} GDD`) + cy.get('.transaction-confirm-send').contains(memoText) + cy.get('.transaction-confirm-send').contains(`+ ${senderBalance} GDD`) + cy.get('.transaction-confirm-send').contains(`− ${sendAmount} GDD`) + cy.get('.transaction-confirm-send').contains(`+ ${newSenderBalance} GDD`) + }, +) When('the user submits the transaction by confirming', (receiverName: string, amount) => { cy.intercept({ @@ -38,11 +46,14 @@ When('the user submits the transaction by confirming', (receiverName: string, am cy.wait('@sendCoins').then((interception) => { cy.wrap(interception.response?.statusCode).should('eq', 200) - cy.wrap(interception.request.body) - .should('have.property', 'query', `mutation ($identifier: String!, $amount: Decimal!, $memo: String!) { + cy.wrap(interception.request.body).should( + 'have.property', + 'query', + `mutation ($identifier: String!, $amount: Decimal!, $memo: String!) { sendCoins(identifier: $identifier, amount: $amount, memo: $memo) } -` ) +`, + ) cy.wrap(interception.response?.body) .should('have.nested.property', 'data.sendCoins') .and('equal', true) @@ -50,21 +61,30 @@ When('the user submits the transaction by confirming', (receiverName: string, am cy.get('[data-test="send-transaction-success-text"]').should('be.visible') }) -Then('the {string} and {string} are displayed on the {string} page', (name: string, amount: string, page: string) => { - switch (page) { - case 'overview': - cy.get('.align-items-center').contains(`${name}`) - cy.get('.align-items-center').contains(`${amount} GDD`) - break - case 'send': - cy.get('.align-items-center').contains(`${name}`) - cy.get('.align-items-center').contains(`${amount} GDD`) - break - case 'transactions': - cy.get('div.mt-3 > div > div.test-list-group-item').eq(0).contains('div.gdd-transaction-list-item-name', `${name}`) - cy.get('div.mt-3 > div > div.test-list-group-item').eq(0).contains('[data-test="transaction-amount"]', `${amount} GDD`) - break - default: - throw new Error(`Error in "Then the {string} and {string} are displayed on the {string}} page" step: incorrect page name string "${page}"`) - } -}) \ No newline at end of file +Then( + 'the {string} and {string} are displayed on the {string} page', + (name: string, amount: string, page: string) => { + switch (page) { + case 'overview': + cy.get('.align-items-center').contains(`${name}`) + cy.get('.align-items-center').contains(`${amount} GDD`) + break + case 'send': + cy.get('.align-items-center').contains(`${name}`) + cy.get('.align-items-center').contains(`${amount} GDD`) + break + case 'transactions': + cy.get('div.mt-3 > div > div.test-list-group-item') + .eq(0) + .contains('div.gdd-transaction-list-item-name', `${name}`) + cy.get('div.mt-3 > div > div.test-list-group-item') + .eq(0) + .contains('[data-test="transaction-amount"]', `${amount} GDD`) + break + default: + throw new Error( + `Error in "Then the {string} and {string} are displayed on the {string}} page" step: incorrect page name string "${page}"`, + ) + } + }, +) diff --git a/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts index 5b25f5391..fb8cf400e 100644 --- a/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts @@ -13,26 +13,20 @@ 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' - } - }) +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) - }) - } -) + loginPage.enterEmail(email) + loginPage.enterPassword(password) + loginPage.submitLogin() + cy.wait('@login').then((interception) => { + expect(interception.response.statusCode).equals(200) + }) +}) // password reset related diff --git a/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts index 1dcbe69ef..8cc45d5b0 100644 --- a/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts @@ -11,7 +11,7 @@ And('the user opens the change password menu', () => { }) When('the user fills the password form with:', (table) => { - let hashedTableRows = table.rowsHash() + const hashedTableRows = table.rowsHash() profilePage.enterOldPassword(hashedTableRows['Old password']) profilePage.enterNewPassword(hashedTableRows['New password']) profilePage.enterRepeatPassword(hashedTableRows['Repeat new password']) diff --git a/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts b/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts index e90956b6e..df4972077 100644 --- a/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/user_registration_steps.ts @@ -10,7 +10,7 @@ When( registrationPage.enterFirstname(firstname) registrationPage.enterLastname(lastname) registrationPage.enterEmail(email) - } + }, ) And('the user agrees to the privacy policy', () => { From fb6ec71b3d4342146a76fe3fbd3e2adc129951c9 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 28 Jun 2023 10:17:06 +0200 Subject: [PATCH 11/11] lint cypress support files --- e2e-tests/cypress/support/index.ts | 1 + e2e-tests/cypress/support/step_definitions/common_steps.ts | 2 +- e2e-tests/cypress/support/step_definitions/email_steps.ts | 5 +++-- .../cypress/support/step_definitions/send_coin_steps.ts | 2 +- .../support/step_definitions/user_authentication_steps.ts | 1 + .../step_definitions/user_profile_change_password_steps.ts | 6 +++--- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/e2e-tests/cypress/support/index.ts b/e2e-tests/cypress/support/index.ts index f8d1abacf..6224f0f0e 100644 --- a/e2e-tests/cypress/support/index.ts +++ b/e2e-tests/cypress/support/index.ts @@ -7,6 +7,7 @@ import './e2e' declare global { namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Chainable { login(email: string, password: string): Chainable } diff --git a/e2e-tests/cypress/support/step_definitions/common_steps.ts b/e2e-tests/cypress/support/step_definitions/common_steps.ts index 656ffb54f..c2128a308 100644 --- a/e2e-tests/cypress/support/step_definitions/common_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/common_steps.ts @@ -1,4 +1,4 @@ -import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor' +import { Given, Then } from '@badeball/cypress-cucumber-preprocessor' import { OverviewPage } from '../../e2e/models/OverviewPage' import { SideNavMenu } from '../../e2e/models/SideNavMenu' import { Toasts } from '../../e2e/models/Toasts' diff --git a/e2e-tests/cypress/support/step_definitions/email_steps.ts b/e2e-tests/cypress/support/step_definitions/email_steps.ts index c6ef5e376..a2e6dbcb8 100644 --- a/e2e-tests/cypress/support/step_definitions/email_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/email_steps.ts @@ -1,4 +1,4 @@ -import { And, Then, When } from '@badeball/cypress-cucumber-preprocessor' +import { Then, When } from '@badeball/cypress-cucumber-preprocessor' import { OverviewPage } from '../../e2e/models/OverviewPage' import { ResetPasswordPage } from '../../e2e/models/ResetPasswordPage' import { UserEMailSite } from '../../e2e/models/UserEMailSite' @@ -55,7 +55,7 @@ Then('the user receives an e-mail containing the {string} link', (linkName: stri ) }) -And( +When( 'the user receives the transaction e-mail about {string} GDD from {string}', (amount: string, senderName: string) => { cy.origin( @@ -102,6 +102,7 @@ When('the user opens the {string} link in the browser', (linkName: string) => { cy.get(resetPasswordPage.newPasswordInput).should('be.visible') break case 'transaction': + // eslint-disable-next-line no-case-declarations const overviewPage = new OverviewPage() cy.get(overviewPage.rightLastTransactionsList).should('be.visible') break diff --git a/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts b/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts index 707b8a147..d26733221 100644 --- a/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/send_coin_steps.ts @@ -35,7 +35,7 @@ Then( }, ) -When('the user submits the transaction by confirming', (receiverName: string, amount) => { +When('the user submits the transaction by confirming', () => { cy.intercept({ method: 'POST', url: '/graphql', diff --git a/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts index fb8cf400e..f96499409 100644 --- a/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/user_authentication_steps.ts @@ -15,6 +15,7 @@ When('the user submits no credentials', () => { When('the user submits the credentials {string} {string}', (email: string, password: string) => { cy.intercept('POST', '/graphql', (req) => { + // eslint-disable-next-line no-prototype-builtins if (req.body.hasOwnProperty('query') && req.body.query.includes('mutation')) { req.alias = 'login' } diff --git a/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts index 8cc45d5b0..c08236cde 100644 --- a/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts +++ b/e2e-tests/cypress/support/step_definitions/user_profile_change_password_steps.ts @@ -1,4 +1,4 @@ -import { And, When } from '@badeball/cypress-cucumber-preprocessor' +import { And, DataTable, When } from '@badeball/cypress-cucumber-preprocessor' import { ProfilePage } from '../../e2e/models/ProfilePage' import { Toasts } from '../../e2e/models/Toasts' @@ -10,7 +10,7 @@ And('the user opens the change password menu', () => { cy.get(profilePage.submitNewPasswordBtn).should('be.disabled') }) -When('the user fills the password form with:', (table) => { +When('the user fills the password form with:', (table: DataTable) => { const hashedTableRows = table.rowsHash() profilePage.enterOldPassword(hashedTableRows['Old password']) profilePage.enterNewPassword(hashedTableRows['New password']) @@ -22,7 +22,7 @@ And('the user submits the password form', () => { profilePage.submitPasswordForm() }) -When('the user is presented a {string} message', (type: string) => { +When('the user is presented a {string} message', () => { const toast = new Toasts() cy.get(toast.toastSlot).within(() => { cy.get(toast.toastTypeSuccess)