diff --git a/admin/src/components/ContributionLink.vue b/admin/src/components/ContributionLink.vue
index 893e202f4..5621e4330 100644
--- a/admin/src/components/ContributionLink.vue
+++ b/admin/src/components/ContributionLink.vue
@@ -15,7 +15,10 @@
{{ $t('contributionLink.contributionLinks') }}
-
+
@@ -24,6 +27,7 @@
v-if="count > 0"
:items="items"
@editContributionLinkData="editContributionLinkData"
+ @get-contribution-links="$emit('get-contribution-links')"
/>
{{ $t('contributionLink.noContributionLinks') }}
diff --git a/admin/src/components/ContributionLinkForm.vue b/admin/src/components/ContributionLinkForm.vue
index a159d33d3..6daf1e299 100644
--- a/admin/src/components/ContributionLinkForm.vue
+++ b/admin/src/components/ContributionLinkForm.vue
@@ -163,7 +163,6 @@ export default {
if (this.form.validFrom === null)
return this.toastError(this.$t('contributionLink.noStartDate'))
if (this.form.validTo === null) return this.toastError(this.$t('contributionLink.noEndDate'))
- // alert(JSON.stringify(this.form))
this.$apollo
.mutate({
mutation: createContributionLink,
@@ -182,6 +181,8 @@ export default {
this.link = result.data.createContributionLink.link
this.toastSuccess(this.link)
this.onReset()
+ this.$root.$emit('bv::toggle::collapse', 'newContribution')
+ this.$emit('get-contribution-links')
})
.catch((error) => {
this.toastError(error.message)
diff --git a/admin/src/components/ContributionLinkList.spec.js b/admin/src/components/ContributionLinkList.spec.js
index 0b9d131bd..b2baf5e9b 100644
--- a/admin/src/components/ContributionLinkList.spec.js
+++ b/admin/src/components/ContributionLinkList.spec.js
@@ -95,7 +95,7 @@ describe('ContributionLinkList', () => {
})
it('toasts a success message', () => {
- expect(toastSuccessSpy).toBeCalledWith('TODO: request message deleted ')
+ expect(toastSuccessSpy).toBeCalledWith('contributionLink.deleted')
})
})
diff --git a/admin/src/components/ContributionLinkList.vue b/admin/src/components/ContributionLinkList.vue
index 518d7d57e..48b7ce978 100644
--- a/admin/src/components/ContributionLinkList.vue
+++ b/admin/src/components/ContributionLinkList.vue
@@ -1,12 +1,12 @@
-
+
@@ -34,7 +34,7 @@
{{ modalData ? modalData.name : '' }}
- {{ modalData }}
+ {{ modalData.memo ? modalData.memo : '' }}
@@ -70,28 +70,30 @@ export default {
'edit',
'show',
],
- modalData: null,
- modalDataLink: null,
+ modalData: {},
}
},
methods: {
- deleteContributionLink() {
- this.$bvModal.msgBoxConfirm(this.$t('contributionLink.deleteNow')).then(async (value) => {
- if (value)
- await this.$apollo
- .mutate({
- mutation: deleteContributionLink,
- variables: {
- id: this.id,
- },
- })
- .then(() => {
- this.toastSuccess('TODO: request message deleted ')
- })
- .catch((err) => {
- this.toastError(err.message)
- })
- })
+ deleteContributionLink(id, name) {
+ this.$bvModal
+ .msgBoxConfirm(this.$t('contributionLink.deleteNow', { name: name }))
+ .then(async (value) => {
+ if (value)
+ await this.$apollo
+ .mutate({
+ mutation: deleteContributionLink,
+ variables: {
+ id: id,
+ },
+ })
+ .then(() => {
+ this.toastSuccess(this.$t('contributionLink.deleted'))
+ this.$emit('get-contribution-links')
+ })
+ .catch((err) => {
+ this.toastError(err.message)
+ })
+ })
},
editContributionLink(row) {
this.$emit('editContributionLinkData', row)
diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue
index a0e790abe..c9c285eef 100644
--- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue
+++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue
@@ -7,7 +7,6 @@
v-model="form.text"
:placeholder="$t('contributionLink.memo')"
rows="3"
- max-rows="6"
>
diff --git a/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.spec.js b/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.spec.js
index 7cca315d7..b8aaba502 100644
--- a/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.spec.js
+++ b/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.spec.js
@@ -9,50 +9,120 @@ describe('ContributionMessagesListItem', () => {
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
- $store: {
- state: {
- moderator: {
- id: 107,
- },
+ }
+
+ describe('if message author has moderator role', () => {
+ const propsData = {
+ contributionId: 42,
+ state: 'PENDING',
+ message: {
+ id: 111,
+ message: 'Lorem ipsum?',
+ createdAt: '2022-08-29T12:23:27.000Z',
+ updatedAt: null,
+ type: 'DIALOG',
+ userFirstName: 'Peter',
+ userLastName: 'Lustig',
+ userId: 107,
+ isModerator: true,
+ __typename: 'ContributionMessage',
},
- },
- }
+ }
- const propsData = {
- contributionId: 42,
- state: 'PENDING0',
- message: {
- id: 111,
- message: 'asd asda sda sda',
- createdAt: '2022-08-29T12:23:27.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Peter',
- userLastName: 'Lustig',
- userId: 107,
- __typename: 'ContributionMessage',
- },
- }
+ const ModeratorItemWrapper = () => {
+ return mount(ContributionMessagesListItem, {
+ localVue,
+ mocks,
+ propsData,
+ })
+ }
- const Wrapper = () => {
- return mount(ContributionMessagesListItem, {
- localVue,
- mocks,
- propsData,
+ describe('mount', () => {
+ beforeAll(() => {
+ wrapper = ModeratorItemWrapper()
+ })
+
+ it('has a DIV .text-right.is-moderator', () => {
+ expect(wrapper.find('div.text-right.is-moderator').exists()).toBe(true)
+ })
+
+ it('has the complete user name', () => {
+ expect(wrapper.find('div.text-right.is-moderator > span:nth-child(2)').text()).toBe(
+ 'Peter Lustig',
+ )
+ })
+
+ it('has the message creation date', () => {
+ expect(wrapper.find('div.text-right.is-moderator > span:nth-child(3)').text()).toMatch(
+ 'Mon Aug 29 2022 12:23:27 GMT+0000',
+ )
+ })
+
+ it('has the moderator label', () => {
+ expect(wrapper.find('div.text-right.is-moderator > small:nth-child(4)').text()).toBe(
+ 'moderator',
+ )
+ })
+
+ it('has the message', () => {
+ expect(wrapper.find('div.text-right.is-moderator > div:nth-child(5)').text()).toBe(
+ 'Lorem ipsum?',
+ )
+ })
})
- }
+ })
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
+ describe('if message author does not have moderator role', () => {
+ const propsData = {
+ contributionId: 42,
+ state: 'PENDING',
+ message: {
+ id: 113,
+ message: 'Asda sdad ad asdasd, das Ass das Das. ',
+ createdAt: '2022-08-29T12:25:34.000Z',
+ updatedAt: null,
+ type: 'DIALOG',
+ userFirstName: 'Bibi',
+ userLastName: 'Bloxberg',
+ userId: 108,
+ __typename: 'ContributionMessage',
+ },
+ }
- it('has a DIV .contribution-messages-list-item', () => {
- expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
- })
+ const ItemWrapper = () => {
+ return mount(ContributionMessagesListItem, {
+ localVue,
+ mocks,
+ propsData,
+ })
+ }
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
+ describe('mount', () => {
+ beforeAll(() => {
+ wrapper = ItemWrapper()
+ })
+
+ it('has a DIV .text-left.is-not-moderator', () => {
+ expect(wrapper.find('div.text-left.is-not-moderator').exists()).toBe(true)
+ })
+
+ it('has the complete user name', () => {
+ expect(wrapper.find('div.is-not-moderator.text-left > span:nth-child(2)').text()).toBe(
+ 'Bibi Bloxberg',
+ )
+ })
+
+ it('has the message creation date', () => {
+ expect(wrapper.find('div.is-not-moderator.text-left > span:nth-child(3)').text()).toMatch(
+ 'Mon Aug 29 2022 12:25:34 GMT+0000',
+ )
+ })
+
+ it('has the message', () => {
+ expect(wrapper.find('div.is-not-moderator.text-left > div:nth-child(4)').text()).toBe(
+ 'Asda sdad ad asdasd, das Ass das Das.',
+ )
+ })
})
})
})
diff --git a/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.vue b/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.vue
index fa5bdd940..796ff5f30 100644
--- a/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.vue
+++ b/admin/src/components/ContributionMessages/slots/ContributionMessagesListItem.vue
@@ -1,27 +1,44 @@
-
-
+
+
+
{{ message.userFirstName }} {{ message.userLastName }}
+
{{ $d(new Date(message.createdAt), 'short') }}
+
{{ $t('moderator') }}
+
{{ message.message }}
+
+
+
+
{{ message.userFirstName }} {{ message.userLastName }}
+
{{ $d(new Date(message.createdAt), 'short') }}
+
{{ message.message }}
+
+
diff --git a/admin/src/components/ContributionMessages/slots/IsModerator.spec.js b/admin/src/components/ContributionMessages/slots/IsModerator.spec.js
deleted file mode 100644
index b1e09da94..000000000
--- a/admin/src/components/ContributionMessages/slots/IsModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsModerator from './IsModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 111,
- message: 'asd asda sda sda',
- createdAt: '2022-08-29T12:23:27.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Peter',
- userLastName: 'Lustig',
- userId: 107,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-moderator', () => {
- expect(wrapper.find('div.slot-is-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/admin/src/components/ContributionMessages/slots/IsModerator.vue b/admin/src/components/ContributionMessages/slots/IsModerator.vue
deleted file mode 100644
index 0224e042f..000000000
--- a/admin/src/components/ContributionMessages/slots/IsModerator.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ $t('moderator') }}
-
{{ message.message }}
-
-
-
-
-
diff --git a/admin/src/components/ContributionMessages/slots/IsNotModerator.spec.js b/admin/src/components/ContributionMessages/slots/IsNotModerator.spec.js
deleted file mode 100644
index 24152ad1e..000000000
--- a/admin/src/components/ContributionMessages/slots/IsNotModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsNotModerator from './IsNotModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsNotModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 113,
- message: 'asda sdad ad asdasd ',
- createdAt: '2022-08-29T12:25:34.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Bibi',
- userLastName: 'Bloxberg',
- userId: 108,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsNotModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-not-moderator', () => {
- expect(wrapper.find('div.slot-is-not-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/admin/src/components/ContributionMessages/slots/IsNotModerator.vue b/admin/src/components/ContributionMessages/slots/IsNotModerator.vue
deleted file mode 100644
index 64946c557..000000000
--- a/admin/src/components/ContributionMessages/slots/IsNotModerator.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ message.message }}
-
-
-
-
-
diff --git a/admin/src/components/CreationTransactionList.spec.js b/admin/src/components/CreationTransactionList.spec.js
index 3e2d5893e..ff9607424 100644
--- a/admin/src/components/CreationTransactionList.spec.js
+++ b/admin/src/components/CreationTransactionList.spec.js
@@ -6,30 +6,29 @@ const localVue = global.localVue
const apolloQueryMock = jest.fn().mockResolvedValue({
data: {
- creationTransactionList: [
- {
- id: 1,
- amount: 100,
- balanceDate: 0,
- creationDate: new Date(),
- memo: 'Testing',
- linkedUser: {
- firstName: 'Gradido',
- lastName: 'Akademie',
+ creationTransactionList: {
+ contributionCount: 2,
+ contributionList: [
+ {
+ id: 1,
+ amount: 5.8,
+ createdAt: '2022-09-21T11:09:51.000Z',
+ confirmedAt: null,
+ contributionDate: '2022-08-01T00:00:00.000Z',
+ memo: 'für deine Hilfe, Fräulein Rottenmeier',
+ state: 'PENDING',
},
- },
- {
- id: 2,
- amount: 200,
- balanceDate: 0,
- creationDate: new Date(),
- memo: 'Testing 2',
- linkedUser: {
- firstName: 'Gradido',
- lastName: 'Akademie',
+ {
+ id: 2,
+ amount: '47',
+ createdAt: '2022-09-21T11:09:28.000Z',
+ confirmedAt: '2022-09-21T11:09:28.000Z',
+ contributionDate: '2022-08-01T00:00:00.000Z',
+ memo: 'für deine Hilfe, Frau Holle',
+ state: 'CONFIRMED',
},
- },
- ],
+ ],
+ },
},
})
@@ -43,7 +42,7 @@ const mocks = {
const propsData = {
userId: 1,
- fields: ['date', 'balance', 'name', 'memo', 'decay'],
+ fields: ['createdAt', 'contributionDate', 'confirmedAt', 'amount', 'memo'],
}
describe('CreationTransactionList', () => {
@@ -63,7 +62,7 @@ describe('CreationTransactionList', () => {
expect.objectContaining({
variables: {
currentPage: 1,
- pageSize: 25,
+ pageSize: 10,
order: 'DESC',
userId: 1,
},
diff --git a/admin/src/components/CreationTransactionList.vue b/admin/src/components/CreationTransactionList.vue
index ec5c12aa4..2ce143c7f 100644
--- a/admin/src/components/CreationTransactionList.vue
+++ b/admin/src/components/CreationTransactionList.vue
@@ -1,7 +1,44 @@
{{ $t('transactionlist.title') }}
-
+
+
+
+ {{ $d(new Date(data.item.contributionDate), 'month') }}
+
+ {{ $d(new Date(data.item.contributionDate)) }}
+
+
+
+
+
{{ $t('help.help') }}
+
+
+ {{ $t('transactionlist.submitted') }} {{ $t('math.equals') }}
+ {{ $t('help.transactionlist.submitted') }}
+
+
+ {{ $t('transactionlist.period') }} {{ $t('math.equals') }}
+ {{ $t('help.transactionlist.periods') }}
+
+
+ {{ $t('transactionlist.confirmed') }} {{ $t('math.equals') }}
+ {{ $t('help.transactionlist.confirmed') }}
+
+
+ {{ $t('transactionlist.state') }} {{ $t('math.equals') }}
+ {{ $t('help.transactionlist.state') }}
+
+
+
diff --git a/admin/src/graphql/creationTransactionList.js b/admin/src/graphql/creationTransactionList.js
index 327221814..fc3a80b18 100644
--- a/admin/src/graphql/creationTransactionList.js
+++ b/admin/src/graphql/creationTransactionList.js
@@ -8,14 +8,15 @@ export const creationTransactionList = gql`
order: $order
userId: $userId
) {
- id
- amount
- balanceDate
- creationDate
- memo
- linkedUser {
- firstName
- lastName
+ contributionCount
+ contributionList {
+ id
+ amount
+ createdAt
+ confirmedAt
+ contributionDate
+ memo
+ state
}
}
}
diff --git a/admin/src/graphql/showContributionLink.js b/admin/src/graphql/showContributionLink.js
deleted file mode 100644
index 8042db6b5..000000000
--- a/admin/src/graphql/showContributionLink.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import gql from 'graphql-tag'
-
-export const showContributionLink = gql`
- query ($id: Int!) {
- showContributionLink {
- id
- validFrom
- validTo
- name
- memo
- amount
- cycle
- maxPerCycle
- maxAmountPerMonth
- code
- }
- }
-`
diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json
index c977a613e..65611b1ab 100644
--- a/admin/src/locales/de.json
+++ b/admin/src/locales/de.json
@@ -7,7 +7,8 @@
"contributionLinks": "Beitragslinks",
"create": "Anlegen",
"cycle": "Zyklus",
- "deleteNow": "Automatische Creations wirklich löschen?",
+ "deleted": "Automatische Schöpfung gelöscht!",
+ "deleteNow": "Automatische Creations '{name}' wirklich löschen?",
"maximumAmount": "maximaler Betrag",
"maxPerCycle": "Wiederholungen",
"memo": "Nachricht",
@@ -74,10 +75,20 @@
"submit": "Senden"
},
"GDD": "GDD",
+ "help": {
+ "help": "Hilfe",
+ "transactionlist": {
+ "confirmed": "Wann wurde es von einem Moderator / Admin bestätigt.",
+ "periods": "Für welchen Zeitraum wurde vom Mitglied eingereicht.",
+ "state": "[PENDING = eingereicht, DELETED = gelöscht, IN_PROGRESS = im Dialog mit Moderator, DENIED = abgelehnt, CONFIRMED = bestätigt]",
+ "submitted": "Wann wurde es vom Mitglied eingereicht"
+ }
+ },
"hide_details": "Details verbergen",
"lastname": "Nachname",
"math": {
"colon": ":",
+ "equals": "=",
"exclaim": "!",
"pipe": "|",
"plus": "+"
@@ -133,10 +144,11 @@
},
"transactionlist": {
"amount": "Betrag",
- "balanceDate": "Schöpfungsdatum",
- "community": "Gemeinschaft",
- "date": "Datum",
+ "confirmed": "Bestätigt",
"memo": "Nachricht",
+ "period": "Zeitraum",
+ "state": "Status",
+ "submitted": "Eingereicht",
"title": "Alle geschöpften Transaktionen für den Nutzer"
},
"undelete_user": "Nutzer wiederherstellen",
diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json
index e99305e08..57fd1c6b4 100644
--- a/admin/src/locales/en.json
+++ b/admin/src/locales/en.json
@@ -7,7 +7,8 @@
"contributionLinks": "Contribution Links",
"create": "Create",
"cycle": "Cycle",
- "deleteNow": "Do you really delete automatic creations?",
+ "deleted": "Automatic creation deleted!",
+ "deleteNow": "Do you really delete automatic creations '{name}'?",
"maximumAmount": "Maximum amount",
"maxPerCycle": "Repetition",
"memo": "Memo",
@@ -74,10 +75,20 @@
"submit": "Send"
},
"GDD": "GDD",
+ "help": {
+ "help": "Help",
+ "transactionlist": {
+ "confirmed": "When was it confirmed by a moderator / admin.",
+ "periods": "For what period was it submitted by the member.",
+ "state": "[PENDING = submitted, DELETED = deleted, IN_PROGRESS = in dialogue with moderator, DENIED = denied, CONFIRMED = confirmed]",
+ "submitted": "When was it submitted by the member"
+ }
+ },
"hide_details": "Hide details",
"lastname": "Lastname",
"math": {
"colon": ":",
+ "equals": "=",
"exclaim": "!",
"pipe": "|",
"plus": "+"
@@ -133,10 +144,11 @@
},
"transactionlist": {
"amount": "Amount",
- "balanceDate": "Creation date",
- "community": "Community",
- "date": "Date",
+ "confirmed": "Confirmed",
"memo": "Message",
+ "period": "Period",
+ "state": "State",
+ "submitted": "Submitted",
"title": "All creation-transactions for the user"
},
"undelete_user": "Undelete User",
diff --git a/admin/src/pages/Overview.vue b/admin/src/pages/Overview.vue
index 4c4ba47fa..cfa247b8e 100644
--- a/admin/src/pages/Overview.vue
+++ b/admin/src/pages/Overview.vue
@@ -28,7 +28,11 @@
-
+
diff --git a/backend/log4js-config.json b/backend/log4js-config.json
index 848a4fa79..e595e7c52 100644
--- a/backend/log4js-config.json
+++ b/backend/log4js-config.json
@@ -5,41 +5,66 @@
{
"type": "dateFile",
"filename": "../logs/backend/access.log",
- "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
+ "pattern": "yyyy-MM-dd",
+ "layout":
+ {
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
+ },
"keepFileExt" : true,
- "fileNameSep" : "_"
+ "fileNameSep" : "_",
+ "numBackups" : 30
},
"apollo":
{
"type": "dateFile",
"filename": "../logs/backend/apollo.log",
- "pattern": "%d{ISO8601} %p %c %m",
+ "pattern": "yyyy-MM-dd",
+ "layout":
+ {
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
+ },
"keepFileExt" : true,
- "fileNameSep" : "_"
+ "fileNameSep" : "_",
+ "numBackups" : 30
},
"backend":
{
"type": "dateFile",
"filename": "../logs/backend/backend.log",
- "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
+ "pattern": "yyyy-MM-dd",
+ "layout":
+ {
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
+ },
"keepFileExt" : true,
- "fileNameSep" : "_"
+ "fileNameSep" : "_",
+ "numBackups" : 30
},
"klicktipp":
{
"type": "dateFile",
"filename": "../logs/backend/klicktipp.log",
- "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
+ "pattern": "yyyy-MM-dd",
+ "layout":
+ {
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
+ },
"keepFileExt" : true,
- "fileNameSep" : "_"
+ "fileNameSep" : "_",
+ "numBackups" : 30
},
"errorFile":
{
"type": "dateFile",
"filename": "../logs/backend/errors.log",
- "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
+ "pattern": "yyyy-MM-dd",
+ "layout":
+ {
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
+ },
"keepFileExt" : true,
- "fileNameSep" : "_"
+ "fileNameSep" : "_",
+ "numBackups" : 30
},
"errors":
{
@@ -52,7 +77,7 @@
"type": "stdout",
"layout":
{
- "type": "pattern", "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m"
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
}
},
"apolloOut":
@@ -60,7 +85,7 @@
"type": "stdout",
"layout":
{
- "type": "pattern", "pattern": "%d{ISO8601} %p %c %m"
+ "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
}
}
},
diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts
index 1f690a3d8..cf57e632f 100644
--- a/backend/src/graphql/model/Contribution.ts
+++ b/backend/src/graphql/model/Contribution.ts
@@ -5,7 +5,7 @@ import { User } from '@entity/User'
@ObjectType()
export class Contribution {
- constructor(contribution: dbContribution, user: User) {
+ constructor(contribution: dbContribution, user?: User | null) {
this.id = contribution.id
this.firstName = user ? user.firstName : null
this.lastName = user ? user.lastName : null
diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts
index e9ee0b55b..3435edb94 100644
--- a/backend/src/graphql/resolver/AdminResolver.ts
+++ b/backend/src/graphql/resolver/AdminResolver.ts
@@ -15,6 +15,7 @@ import { AdminCreateContributions } from '@model/AdminCreateContributions'
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
import { ContributionLink } from '@model/ContributionLink'
import { ContributionLinkList } from '@model/ContributionLinkList'
+import { Contribution } from '@model/Contribution'
import { RIGHTS } from '@/auth/RIGHTS'
import { UserRepository } from '@repository/User'
import AdminCreateContributionArgs from '@arg/AdminCreateContributionArgs'
@@ -23,12 +24,10 @@ import SearchUsersArgs from '@arg/SearchUsersArgs'
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
-import { Transaction } from '@model/Transaction'
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
-import { TransactionRepository } from '@repository/Transaction'
import { calculateDecay } from '@/util/decay'
-import { Contribution } from '@entity/Contribution'
+import { Contribution as DbContribution } from '@entity/Contribution'
import { hasElopageBuys } from '@/util/hasElopageBuys'
import { User as dbUser } from '@entity/User'
import { User } from '@model/User'
@@ -40,7 +39,6 @@ import { Decay } from '@model/Decay'
import Paginated from '@arg/Paginated'
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
import { Order } from '@enum/Order'
-import { communityUser } from '@/util/communityUser'
import { findUserByEmail, activationLink, printTimeDuration } from './UserResolver'
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
@@ -66,6 +64,7 @@ import { ContributionMessageType } from '@enum/MessageType'
import { ContributionMessage } from '@model/ContributionMessage'
import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail'
import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail'
+import { ContributionListResult } from '../model/Contribution'
// const EMAIL_OPT_IN_REGISTER = 1
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
@@ -248,7 +247,7 @@ export class AdminResolver {
const creationDateObj = new Date(creationDate)
logger.trace('creationDateObj:', creationDateObj)
validateContribution(creations, amount, creationDateObj)
- const contribution = Contribution.create()
+ const contribution = DbContribution.create()
contribution.userId = emailContact.userId
contribution.amount = amount
contribution.createdAt = new Date()
@@ -259,7 +258,7 @@ export class AdminResolver {
contribution.contributionStatus = ContributionStatus.PENDING
logger.trace('contribution to save', contribution)
- await Contribution.save(contribution)
+ await DbContribution.save(contribution)
return getUserCreation(emailContact.userId)
}
@@ -317,7 +316,7 @@ export class AdminResolver {
const moderator = getUser(context)
- const contributionToUpdate = await Contribution.findOne({
+ const contributionToUpdate = await DbContribution.findOne({
where: { id, confirmedAt: IsNull() },
})
@@ -350,7 +349,7 @@ export class AdminResolver {
contributionToUpdate.moderatorId = moderator.id
contributionToUpdate.contributionStatus = ContributionStatus.PENDING
- await Contribution.save(contributionToUpdate)
+ await DbContribution.save(contributionToUpdate)
const result = new AdminUpdateContribution()
result.amount = amount
result.memo = contributionToUpdate.memo
@@ -367,7 +366,7 @@ export class AdminResolver {
const contributions = await getConnection()
.createQueryBuilder()
.select('c')
- .from(Contribution, 'c')
+ .from(DbContribution, 'c')
.leftJoinAndSelect('c.messages', 'm')
.where({ confirmedAt: IsNull() })
.getMany()
@@ -399,7 +398,7 @@ export class AdminResolver {
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
@Mutation(() => Boolean)
async adminDeleteContribution(@Arg('id', () => Int) id: number): Promise {
- const contribution = await Contribution.findOne(id)
+ const contribution = await DbContribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found for given id.')
@@ -416,7 +415,7 @@ export class AdminResolver {
@Arg('id', () => Int) id: number,
@Ctx() context: Context,
): Promise {
- const contribution = await Contribution.findOne(id)
+ const contribution = await DbContribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found to given id.')
@@ -481,7 +480,7 @@ export class AdminResolver {
contribution.confirmedBy = moderatorUser.id
contribution.transactionId = transaction.id
contribution.contributionStatus = ContributionStatus.CONFIRMED
- await queryRunner.manager.update(Contribution, { id: contribution.id }, contribution)
+ await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
await queryRunner.commitTransaction()
logger.info('creation commited successfuly.')
@@ -506,24 +505,29 @@ export class AdminResolver {
}
@Authorized([RIGHTS.CREATION_TRANSACTION_LIST])
- @Query(() => [Transaction])
+ @Query(() => ContributionListResult)
async creationTransactionList(
@Args()
{ currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
@Arg('userId', () => Int) userId: number,
- ): Promise {
+ ): Promise {
const offset = (currentPage - 1) * pageSize
- const transactionRepository = getCustomRepository(TransactionRepository)
- const [userTransactions] = await transactionRepository.findByUserPaged(
- userId,
- pageSize,
- offset,
- order,
- true,
- )
+ const [contributionResult, count] = await getConnection()
+ .createQueryBuilder()
+ .select('c')
+ .from(DbContribution, 'c')
+ .leftJoinAndSelect('c.user', 'u')
+ .where(`user_id = ${userId}`)
+ .limit(pageSize)
+ .offset(offset)
+ .orderBy('c.created_at', order)
+ .getManyAndCount()
- const user = await dbUser.findOneOrFail({ id: userId })
- return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
+ return new ContributionListResult(
+ count,
+ contributionResult.map((contribution) => new Contribution(contribution, contribution.user)),
+ )
+ // return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
}
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
@@ -744,7 +748,7 @@ export class AdminResolver {
await queryRunner.startTransaction('REPEATABLE READ')
const contributionMessage = DbContributionMessage.create()
try {
- const contribution = await Contribution.findOne({
+ const contribution = await DbContribution.findOne({
where: { id: contributionId },
relations: ['user'],
})
@@ -773,7 +777,7 @@ export class AdminResolver {
contribution.contributionStatus === ContributionStatus.PENDING
) {
contribution.contributionStatus = ContributionStatus.IN_PROGRESS
- await queryRunner.manager.update(Contribution, { id: contributionId }, contribution)
+ await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
}
await sendAddedContributionMessageEmail({
diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts
index 5ad578767..00ce1afde 100644
--- a/backend/src/graphql/resolver/UserResolver.ts
+++ b/backend/src/graphql/resolver/UserResolver.ts
@@ -351,7 +351,7 @@ export class UserResolver {
}
// add pubKey in logger-context for layout-pattern X{user} to print it in each logging message
logger.addContext('user', dbUser.id)
- logger.debug('login credentials valid...')
+ logger.debug('validation of login credentials successful...')
const user = new User(dbUser, await getUserCreation(dbUser.id))
logger.debug(`user= ${JSON.stringify(user, null, 2)}`)
@@ -396,6 +396,7 @@ export class UserResolver {
@Args()
{ email, firstName, lastName, language, publisherId, redeemCode = null }: CreateUserArgs,
): Promise {
+ logger.addContext('user', 'unknown')
logger.info(
`createUser(email=${email}, firstName=${firstName}, lastName=${lastName}, language=${language}, publisherId=${publisherId}, redeemCode =${redeemCode})`,
)
@@ -548,6 +549,7 @@ export class UserResolver {
}
await queryRunner.commitTransaction()
+ logger.addContext('user', dbUser.id)
} catch (e) {
logger.error(`error during create user with ${e}`)
await queryRunner.rollbackTransaction()
@@ -571,6 +573,7 @@ export class UserResolver {
@Authorized([RIGHTS.SEND_RESET_PASSWORD_EMAIL])
@Mutation(() => Boolean)
async forgotPassword(@Arg('email') email: string): Promise {
+ logger.addContext('user', 'unknown')
logger.info(`forgotPassword(${email})...`)
email = email.trim().toLowerCase()
const user = await findUserByEmail(email).catch(() => {
diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts
index d1153cdb6..8ae4675db 100644
--- a/backend/src/server/createServer.ts
+++ b/backend/src/server/createServer.ts
@@ -35,6 +35,7 @@ const createServer = async (
context: any = serverContext,
logger: Logger = apolloLogger,
): Promise => {
+ logger.addContext('user', 'unknown')
logger.debug('createServer...')
// open mysql connection
diff --git a/database/migrations/0049-add_user_contacts_table.ts b/database/migrations/0049-add_user_contacts_table.ts
index c3b89ed88..acdd1af61 100644
--- a/database/migrations/0049-add_user_contacts_table.ts
+++ b/database/migrations/0049-add_user_contacts_table.ts
@@ -14,8 +14,8 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
\`type\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
\`user_id\` int(10) unsigned NOT NULL,
\`email\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL UNIQUE,
- \`email_verification_code\` bigint(20) unsigned NOT NULL UNIQUE,
- \`email_opt_in_type_id\` int NOT NULL,
+ \`email_verification_code\` bigint(20) unsigned DEFAULT NULL UNIQUE,
+ \`email_opt_in_type_id\` int DEFAULT NULL,
\`email_resend_count\` int DEFAULT '0',
\`email_checked\` tinyint(4) NOT NULL DEFAULT 0,
\`phone\` varchar(255) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
@@ -41,47 +41,13 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
// merge values from login_email_opt_in table with users.email in new user_contacts table
await queryFn(`
- INSERT INTO user_contacts
- (type, user_id, email, email_verification_code, email_opt_in_type_id, email_resend_count, email_checked, created_at, updated_at, deleted_at)
- SELECT
- 'EMAIL',
- u.id as user_id,
- u.email,
- e.verification_code as email_verification_code,
- e.email_opt_in_type_id,
- e.resend_count as email_resend_count,
- u.email_checked,
- e.created as created_at,
- e.updated as updated_at,
- u.deletedAt as deleted_at\
- FROM
- users as u,
- login_email_opt_in as e
- WHERE
- u.id = e.user_id AND
- e.id in (
- WITH opt_in AS (
- SELECT
- le.id, le.user_id, le.created, le.updated, ROW_NUMBER() OVER (PARTITION BY le.user_id ORDER BY le.created DESC) AS row_num
- FROM
- login_email_opt_in as le
- )
- SELECT
- opt_in.id
- FROM
- opt_in
- WHERE
- row_num = 1);`)
- /*
- // SELECT
- // le.id
- // FROM
- // login_email_opt_in as le
- // WHERE
- // le.user_id = u.id
- // ORDER BY
- // le.updated DESC, le.created DESC LIMIT 1);`)
- */
+ INSERT INTO user_contacts
+ (type, user_id, email, email_verification_code, email_opt_in_type_id, email_resend_count, email_checked, created_at, updated_at, deleted_at)
+ SELECT 'EMAIL', users.id, users.email, optin.verification_code, optin.email_opt_in_type_id, optin.resend_count, users.email_checked, users.created, null, users.deletedAt
+ FROM users LEFT JOIN
+ (SELECT le.id, le.user_id, le.verification_code, le.email_opt_in_type_id, le.resend_count, le.created, le.updated,
+ ROW_NUMBER() OVER (PARTITION BY le.user_id ORDER BY le.created DESC) AS row_num
+ FROM login_email_opt_in as le) AS optin ON users.id = optin.user_id AND row_num = 1;`)
// insert in users table the email_id of the new created email-contacts
const contacts = await queryFn(`SELECT c.id, c.user_id FROM user_contacts as c`)
@@ -113,11 +79,13 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom
)
// reconstruct the previous email back from contacts to users table
- const contacts = await queryFn(`SELECT c.id, c.email, c.user_id FROM user_contacts as c`)
+ const contacts = await queryFn(
+ `SELECT c.id, c.email, c.user_id, c.email_checked FROM user_contacts as c`,
+ )
for (const id in contacts) {
const contact = contacts[id]
await queryFn(
- `UPDATE users SET email = "${contact.email}" WHERE id = "${contact.user_id}" and email_id = "${contact.id}"`,
+ `UPDATE users SET email = "${contact.email}", email_checked="${contact.email_checked}" WHERE id = "${contact.user_id}" and email_id = "${contact.id}"`,
)
}
await queryFn('ALTER TABLE users MODIFY COLUMN email varchar(255) NOT NULL UNIQUE;')
diff --git a/e2e-tests/cypress/README.md b/e2e-tests/cypress/README.md
index b1ddae514..4ec1ebe51 100644
--- a/e2e-tests/cypress/README.md
+++ b/e2e-tests/cypress/README.md
@@ -1,24 +1,73 @@
# 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)
-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
+
+Before running the tests, change to the repo's root directory (gradido).
+
+### Boot up the system under test
```bash
docker-compose up
```
-to boot up the DEV system, before running the test.
+### 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 container and execute the given tests
-docker run -it --network=host gradido_e2e-tests-cypress yarn run cypress-e2e-tests
+# run the Docker image and execute the given tests
+docker run -it --network=host gradido_e2e-tests-cypress yarn cypress-e2e
```
diff --git a/e2e-tests/cypress/tests/cypress.config.ts b/e2e-tests/cypress/tests/cypress.config.ts
index 815394c5e..ad6a8d7de 100644
--- a/e2e-tests/cypress/tests/cypress.config.ts
+++ b/e2e-tests/cypress/tests/cypress.config.ts
@@ -32,6 +32,7 @@ export default defineConfig({
excludeSpecPattern: "*.js",
baseUrl: "http://localhost:3000",
chromeWebSecurity: false,
+ defaultCommandTimeout: 10000,
supportFile: "cypress/support/index.ts",
viewportHeight: 720,
viewportWidth: 1280,
diff --git a/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature b/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature
new file mode 100644
index 000000000..9361d2b84
--- /dev/null
+++ b/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature
@@ -0,0 +1,13 @@
+Feature: User registration
+ As a user
+ I want to register to create an account
+
+ @skip
+ Scenario: Register successfully
+ Given the browser navigates to page "/register"
+ When the user fills name and email "Regina" "Register" "regina@register.com"
+ And the user agrees to the privacy policy
+ And the user submits the registration form
+ Then the user can use a provided activation link
+ And the user can set a password "Aa12345_"
+ And the user can login with the credentials "regina@register.com" "Aa12345_"
diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts
index a16b93a11..9a0df62ee 100644
--- a/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts
+++ b/e2e-tests/cypress/tests/cypress/e2e/models/LoginPage.ts
@@ -2,8 +2,8 @@
export class LoginPage {
// selectors
- emailInput = "#Email-input-field";
- passwordInput = "#Password-input-field";
+ emailInput = "input[type=email]";
+ passwordInput = "input[type=password]";
submitBtn = "[type=submit]";
emailHint = "#vee_Email";
passwordHint = "#vee_Password";
diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts
index b280a0b2a..0532a7ff8 100644
--- a/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts
+++ b/e2e-tests/cypress/tests/cypress/e2e/models/ProfilePage.ts
@@ -4,8 +4,8 @@ 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";
+ newPasswordInput = "#new-password-input-field";
+ newPasswordRepeatInput = "#repeat-new-password-input-field";
submitNewPasswordBtn = "[data-test=submit-new-password-btn]";
goto() {
@@ -19,12 +19,12 @@ export class ProfilePage {
}
enterNewPassword(password: string) {
- cy.get(this.newPasswordInput).clear().type(password);
+ cy.get(this.newPasswordInput).find("input").clear().type(password);
return this;
}
enterRepeatPassword(password: string) {
- cy.get(this.newPasswordRepeatInput).clear().type(password);
+ cy.get(this.newPasswordRepeatInput).find("input").clear().type(password);
return this;
}
diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts b/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts
new file mode 100644
index 000000000..27a9cb8cc
--- /dev/null
+++ b/e2e-tests/cypress/tests/cypress/e2e/models/RegistrationPage.ts
@@ -0,0 +1,42 @@
+///
+
+export class RegistrationPage {
+ // selectors
+ firstnameInput = "#registerFirstname";
+ lastnameInput = "#registerLastname";
+ emailInput = "#Email-input-field";
+ checkbox = "#registerCheckbox";
+ submitBtn = "[type=submit]";
+
+ RegistrationThanxHeadline = ".test-message-headline";
+ RegistrationThanxText = ".test-message-subtitle";
+
+ goto() {
+ cy.visit("/register");
+ return this;
+ }
+
+ enterFirstname(firstname: string) {
+ cy.get(this.firstnameInput).clear().type(firstname);
+ return this;
+ }
+
+ enterLastname(lastname: string) {
+ cy.get(this.lastnameInput).clear().type(lastname);
+ return this;
+ }
+
+ enterEmail(email: string) {
+ cy.get(this.emailInput).clear().type(email);
+ return this;
+ }
+
+ checkPrivacyCheckbox() {
+ cy.get(this.checkbox).click({ force: true });
+ }
+
+ submitRegistrationPage() {
+ cy.get(this.submitBtn).should("be.enabled");
+ cy.get(this.submitBtn).click();
+ }
+}
diff --git a/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts b/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts
index b2198bc8d..aabd0a45e 100644
--- a/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts
+++ b/e2e-tests/cypress/tests/cypress/e2e/models/Toasts.ts
@@ -2,6 +2,9 @@
export class Toasts {
// selectors
+ toastSlot = ".b-toaster-slot";
+ toastTypeSuccess = ".b-toast-success";
+ toastTypeError = ".b-toast-danger";
toastTitle = ".gdd-toaster-title";
toastMessage = ".gdd-toaster-body";
}
diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts
index 439974cda..f45358f3c 100644
--- a/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts
+++ b/e2e-tests/cypress/tests/cypress/support/step_definitions/common_steps.ts
@@ -25,11 +25,11 @@ Then("the user is logged in with username {string}", (username: string) => {
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."
- );
+ cy.get(toast.toastSlot).within(() => {
+ cy.get(toast.toastTypeError);
+ cy.get(toast.toastTitle).should("be.visible");
+ cy.get(toast.toastMessage).should("be.visible");
+ });
});
//
diff --git a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts b/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts
index cbe851f02..5396b66bb 100644
--- a/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts
+++ b/e2e-tests/cypress/tests/cypress/support/step_definitions/user_profile_change_password_steps.ts
@@ -24,9 +24,9 @@ And("the user submits the password form", () => {
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."
- );
+ cy.get(toast.toastSlot).within(() => {
+ cy.get(toast.toastTypeSuccess);
+ cy.get(toast.toastTitle).should("be.visible");
+ cy.get(toast.toastMessage).should("be.visible");
+ });
});
diff --git a/e2e-tests/cypress/tests/package.json b/e2e-tests/cypress/tests/package.json
index a9979725e..a6f817503 100644
--- a/e2e-tests/cypress/tests/package.json
+++ b/e2e-tests/cypress/tests/package.json
@@ -14,7 +14,7 @@
}
},
"scripts": {
- "cypress": "cypress run",
+ "cypress-e2e": "cypress run",
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
},
"dependencies": {
diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue b/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue
index e3f9fd5e7..1a5928cc3 100644
--- a/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue
+++ b/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue
@@ -7,7 +7,6 @@
v-model="form.text"
:placeholder="$t('form.memo')"
rows="3"
- max-rows="6"
>
diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.spec.js b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.spec.js
index b9f6f0029..7504854d0 100644
--- a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.spec.js
+++ b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.spec.js
@@ -1,29 +1,29 @@
import { mount } from '@vue/test-utils'
import ContributionMessagesList from './ContributionMessagesList.vue'
+import ContributionMessagesListItem from './ContributionMessagesListItem.vue'
const localVue = global.localVue
+let wrapper
+
+const mocks = {
+ $t: jest.fn((t) => t),
+ $d: jest.fn((d) => d),
+ $store: {
+ state: {
+ firstName: 'Peter',
+ lastName: 'Lustig',
+ },
+ },
+}
describe('ContributionMessagesList', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- $store: {
- state: {
- firstName: 'Peter',
- lastName: 'Lustig',
- },
- },
- }
-
const propsData = {
contributionId: 42,
- state: 'PENDING0',
+ state: 'PENDING',
messages: [
{
id: 111,
- message: 'asd asda sda sda',
+ message: 'Lorem ipsum?',
createdAt: '2022-08-29T12:23:27.000Z',
updatedAt: null,
type: 'DIALOG',
@@ -32,10 +32,21 @@ describe('ContributionMessagesList', () => {
userId: 107,
__typename: 'ContributionMessage',
},
+ {
+ id: 113,
+ message: 'Asda sdad ad asdasd, das Ass das Das. ',
+ createdAt: '2022-08-29T12:25:34.000Z',
+ updatedAt: null,
+ type: 'DIALOG',
+ userFirstName: 'Bibi',
+ userLastName: 'Bloxberg',
+ userId: 108,
+ __typename: 'ContributionMessage',
+ },
],
}
- const Wrapper = () => {
+ const ListWrapper = () => {
return mount(ContributionMessagesList, {
localVue,
mocks,
@@ -45,11 +56,123 @@ describe('ContributionMessagesList', () => {
describe('mount', () => {
beforeEach(() => {
- wrapper = Wrapper()
+ wrapper = ListWrapper()
})
- it('has a DIV .contribution-messages-list-item', () => {
- expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
+ it('has two DIV .contribution-messages-list-item elements', () => {
+ expect(wrapper.findAll('div.contribution-messages-list-item').length).toBe(2)
+ })
+ })
+})
+
+describe('ContributionMessagesListItem', () => {
+ describe('if message author has moderator role', () => {
+ const propsData = {
+ message: {
+ id: 113,
+ message: 'Asda sdad ad asdasd, das Ass das Das. ',
+ createdAt: '2022-08-29T12:25:34.000Z',
+ updatedAt: null,
+ type: 'DIALOG',
+ userFirstName: 'Bibi',
+ userLastName: 'Bloxberg',
+ userId: 108,
+ __typename: 'ContributionMessage',
+ },
+ }
+
+ const ItemWrapper = () => {
+ return mount(ContributionMessagesListItem, {
+ localVue,
+ mocks,
+ propsData,
+ })
+ }
+
+ describe('mount', () => {
+ beforeAll(() => {
+ wrapper = ItemWrapper()
+ })
+
+ it('has a DIV .is-moderator.text-left', () => {
+ expect(wrapper.find('div.is-moderator.text-left').exists()).toBe(true)
+ })
+
+ it('has the complete user name', () => {
+ expect(wrapper.find('div.is-moderator.text-left > span:nth-child(2)').text()).toBe(
+ 'Bibi Bloxberg',
+ )
+ })
+
+ it('has the message creation date', () => {
+ expect(wrapper.find('div.is-moderator.text-left > span:nth-child(3)').text()).toMatch(
+ 'Mon Aug 29 2022 12:25:34 GMT+0000',
+ )
+ })
+
+ it('has the moderator label', () => {
+ expect(wrapper.find('div.is-moderator.text-left > small:nth-child(4)').text()).toBe(
+ 'community.moderator',
+ )
+ })
+
+ it('has the message', () => {
+ expect(wrapper.find('div.is-moderator.text-left > div:nth-child(5)').text()).toBe(
+ 'Asda sdad ad asdasd, das Ass das Das.',
+ )
+ })
+ })
+ })
+
+ describe('if message author does not have moderator role', () => {
+ const propsData = {
+ message: {
+ id: 111,
+ message: 'Lorem ipsum?',
+ createdAt: '2022-08-29T12:23:27.000Z',
+ updatedAt: null,
+ type: 'DIALOG',
+ userFirstName: 'Peter',
+ userLastName: 'Lustig',
+ userId: 107,
+ __typename: 'ContributionMessage',
+ },
+ }
+
+ const ModeratorItemWrapper = () => {
+ return mount(ContributionMessagesListItem, {
+ localVue,
+ mocks,
+ propsData,
+ })
+ }
+
+ describe('mount', () => {
+ beforeAll(() => {
+ wrapper = ModeratorItemWrapper()
+ })
+
+ it('has a DIV .is-not-moderator.text-right', () => {
+ expect(wrapper.find('div.is-not-moderator.text-right').exists()).toBe(true)
+ })
+
+ it('has the complete user name', () => {
+ expect(wrapper.find('div.is-not-moderator.text-right > span:nth-child(2)').text()).toBe(
+ 'Peter Lustig',
+ )
+ })
+
+ it('has the message creation date', () => {
+ expect(wrapper.find('div.is-not-moderator.text-right > span:nth-child(3)').text()).toMatch(
+ 'Mon Aug 29 2022 12:23:27 GMT+0000',
+ )
+ })
+
+ it('has the message', () => {
+ expect(wrapper.find('div.is-not-moderator.text-right > div:nth-child(4)').text()).toBe(
+ 'Lorem ipsum?',
+ )
+ })
})
})
})
diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue
index 5fde8f825..6c2e555f2 100644
--- a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue
+++ b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue
@@ -1,26 +1,28 @@
-
-
+
+
+
{{ message.userFirstName }} {{ message.userLastName }}
+
{{ $d(new Date(message.createdAt), 'short') }}
+
{{ message.message }}
+
+
+
+
{{ message.userFirstName }} {{ message.userLastName }}
+
{{ $d(new Date(message.createdAt), 'short') }}
+
{{ $t('community.moderator') }}
+
{{ message.message }}
+
-
+
diff --git a/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js b/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js
deleted file mode 100644
index b1e09da94..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsModerator from './IsModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 111,
- message: 'asd asda sda sda',
- createdAt: '2022-08-29T12:23:27.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Peter',
- userLastName: 'Lustig',
- userId: 107,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-moderator', () => {
- expect(wrapper.find('div.slot-is-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/frontend/src/components/ContributionMessages/slots/IsModerator.vue b/frontend/src/components/ContributionMessages/slots/IsModerator.vue
deleted file mode 100644
index 343b92d97..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsModerator.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ $t('community.moderator') }}
-
{{ message.message }}
-
-
-
-
diff --git a/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js b/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js
deleted file mode 100644
index 24152ad1e..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsNotModerator from './IsNotModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsNotModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 113,
- message: 'asda sdad ad asdasd ',
- createdAt: '2022-08-29T12:25:34.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Bibi',
- userLastName: 'Bloxberg',
- userId: 108,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsNotModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-not-moderator', () => {
- expect(wrapper.find('div.slot-is-not-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue b/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue
deleted file mode 100644
index 8efca7270..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ message.message }}
-
-
-
-
-
diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue
index 3a9010ec2..47f2be4c4 100644
--- a/frontend/src/components/Contributions/ContributionForm.vue
+++ b/frontend/src/components/Contributions/ContributionForm.vue
@@ -42,7 +42,6 @@
id="contribution-memo"
v-model="form.memo"
rows="3"
- max-rows="6"
:placeholder="$t('contribution.yourActivity')"
required
>
diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue
index 62361c4d0..69c6b6b45 100644
--- a/frontend/src/components/GddSend/TransactionForm.vue
+++ b/frontend/src/components/GddSend/TransactionForm.vue
@@ -46,7 +46,7 @@
@@ -81,7 +81,11 @@
v-slot="{ errors, valid }"
>
-
+
{{ $t('GDD') }}
@@ -115,7 +119,7 @@
v-slot="{ errors }"
>
-
+
@@ -237,4 +241,7 @@ span.errors {
#input-3:focus {
font-weight: bold;
}
+.border-radius {
+ border-radius: 10px;
+}
diff --git a/frontend/src/components/Inputs/InputPasswordConfirmation.vue b/frontend/src/components/Inputs/InputPasswordConfirmation.vue
index 3209018c3..56d58d9ad 100644
--- a/frontend/src/components/Inputs/InputPasswordConfirmation.vue
+++ b/frontend/src/components/Inputs/InputPasswordConfirmation.vue
@@ -29,6 +29,7 @@
required: true,
samePassword: value.password,
}"
+ id="repeat-new-password-input-field"
:label="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
:immediate="true"
:name="createId(register ? $t('form.passwordRepeat') : $t('form.password_new_repeat'))"