mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2685-Desktop-Sub-Menu
This commit is contained in:
commit
b42801c1ca
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -550,7 +550,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd e2e-tests/
|
cd e2e-tests/
|
||||||
yarn
|
yarn
|
||||||
yarn run cypress run --spec cypress/e2e/User.Authentication.feature,cypress/e2e/User.Authentication.ResetPassword.feature
|
yarn run cypress run --spec cypress/e2e/User.Authentication.feature,cypress/e2e/User.Authentication.ResetPassword.feature,cypress/e2e/User.Registration.feature
|
||||||
- name: End-to-end tests | if tests failed, upload screenshots
|
- name: End-to-end tests | if tests failed, upload screenshots
|
||||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
export const listAllContributions = gql`
|
export const adminListAllContributions = gql`
|
||||||
query (
|
query (
|
||||||
$currentPage: Int = 1
|
$currentPage: Int = 1
|
||||||
$pageSize: Int = 25
|
$pageSize: Int = 25
|
||||||
$order: Order = DESC
|
$order: Order = DESC
|
||||||
$statusFilter: [ContributionStatus!]
|
$statusFilter: [ContributionStatus!]
|
||||||
) {
|
) {
|
||||||
listAllContributions(
|
adminListAllContributions(
|
||||||
currentPage: $currentPage
|
currentPage: $currentPage
|
||||||
pageSize: $pageSize
|
pageSize: $pageSize
|
||||||
order: $order
|
order: $order
|
||||||
@ -28,6 +28,8 @@ export const listAllContributions = gql`
|
|||||||
messagesCount
|
messagesCount
|
||||||
deniedAt
|
deniedAt
|
||||||
deniedBy
|
deniedBy
|
||||||
|
deletedAt
|
||||||
|
deletedBy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils'
|
|||||||
import CreationConfirm from './CreationConfirm'
|
import CreationConfirm from './CreationConfirm'
|
||||||
import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
|
import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
|
||||||
import { denyContribution } from '../graphql/denyContribution'
|
import { denyContribution } from '../graphql/denyContribution'
|
||||||
import { listAllContributions } from '../graphql/listAllContributions'
|
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
||||||
import { confirmContribution } from '../graphql/confirmContribution'
|
import { confirmContribution } from '../graphql/confirmContribution'
|
||||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||||
import VueApollo from 'vue-apollo'
|
import VueApollo from 'vue-apollo'
|
||||||
@ -38,7 +38,7 @@ const mocks = {
|
|||||||
|
|
||||||
const defaultData = () => {
|
const defaultData = () => {
|
||||||
return {
|
return {
|
||||||
listAllContributions: {
|
adminListAllContributions: {
|
||||||
contributionCount: 2,
|
contributionCount: 2,
|
||||||
contributionList: [
|
contributionList: [
|
||||||
{
|
{
|
||||||
@ -92,14 +92,14 @@ const defaultData = () => {
|
|||||||
|
|
||||||
describe('CreationConfirm', () => {
|
describe('CreationConfirm', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
|
const adminListAllContributionsMock = jest.fn()
|
||||||
const adminDeleteContributionMock = jest.fn()
|
const adminDeleteContributionMock = jest.fn()
|
||||||
const adminDenyContributionMock = jest.fn()
|
const adminDenyContributionMock = jest.fn()
|
||||||
const confirmContributionMock = jest.fn()
|
const confirmContributionMock = jest.fn()
|
||||||
|
|
||||||
mockClient.setRequestHandler(
|
mockClient.setRequestHandler(
|
||||||
listAllContributions,
|
adminListAllContributions,
|
||||||
jest
|
adminListAllContributionsMock
|
||||||
.fn()
|
|
||||||
.mockRejectedValueOnce({ message: 'Ouch!' })
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
.mockResolvedValue({ data: defaultData() }),
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
)
|
)
|
||||||
@ -331,78 +331,82 @@ describe('CreationConfirm', () => {
|
|||||||
|
|
||||||
describe('filter tabs', () => {
|
describe('filter tabs', () => {
|
||||||
describe('click tab "confirmed"', () => {
|
describe('click tab "confirmed"', () => {
|
||||||
let refetchSpy
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
|
|
||||||
await wrapper.find('a[data-test="confirmed"]').trigger('click')
|
await wrapper.find('a[data-test="confirmed"]').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has statusFilter set to ["CONFIRMED"]', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables,
|
currentPage: 1,
|
||||||
).toMatchObject({ statusFilter: ['CONFIRMED'] })
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['CONFIRMED'],
|
||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions', () => {
|
|
||||||
expect(refetchSpy).toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('click tab "open"', () => {
|
describe('click tab "open"', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
|
|
||||||
await wrapper.find('a[data-test="open"]').trigger('click')
|
await wrapper.find('a[data-test="open"]').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has statusFilter set to ["IN_PROGRESS", "PENDING"]', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables,
|
currentPage: 1,
|
||||||
).toMatchObject({ statusFilter: ['IN_PROGRESS', 'PENDING'] })
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['IN_PROGRESS', 'PENDING'],
|
||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions', () => {
|
|
||||||
expect(refetchSpy).toBeCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('click tab "denied"', () => {
|
describe('click tab "denied"', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
|
|
||||||
await wrapper.find('a[data-test="denied"]').trigger('click')
|
await wrapper.find('a[data-test="denied"]').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has statusFilter set to ["DENIED"]', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables,
|
currentPage: 1,
|
||||||
).toMatchObject({ statusFilter: ['DENIED'] })
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['DENIED'],
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions', () => {
|
describe('click tab "deleted"', () => {
|
||||||
expect(refetchSpy).toBeCalled()
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('a[data-test="deleted"]').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refetches contributions with proper filter', () => {
|
||||||
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
|
currentPage: 1,
|
||||||
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['DELETED'],
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('click tab "all"', () => {
|
describe('click tab "all"', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
|
|
||||||
await wrapper.find('a[data-test="all"]').trigger('click')
|
await wrapper.find('a[data-test="all"]').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has statusFilter set to ["IN_PROGRESS", "PENDING", "CONFIRMED", "DENIED", "DELETED"]', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables,
|
currentPage: 1,
|
||||||
).toMatchObject({
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions', () => {
|
|
||||||
expect(refetchSpy).toBeCalled()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -412,10 +416,20 @@ describe('CreationConfirm', () => {
|
|||||||
await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('update-state', 2)
|
await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('update-state', 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('updates the status', () => {
|
it('updates the status', () => {
|
||||||
expect(wrapper.vm.items.find((obj) => obj.id === 2).messagesCount).toBe(1)
|
expect(wrapper.vm.items.find((obj) => obj.id === 2).messagesCount).toBe(1)
|
||||||
expect(wrapper.vm.items.find((obj) => obj.id === 2).state).toBe('IN_PROGRESS')
|
expect(wrapper.vm.items.find((obj) => obj.id === 2).state).toBe('IN_PROGRESS')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('unknown variant', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.setData({ variant: 'unknown' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has overlay icon "info"', () => {
|
||||||
|
expect(wrapper.vm.overlayIcon).toBe('info')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -73,7 +73,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Overlay from '../components/Overlay'
|
import Overlay from '../components/Overlay'
|
||||||
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
|
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
|
||||||
import { listAllContributions } from '../graphql/listAllContributions'
|
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
||||||
import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
|
import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
|
||||||
import { confirmContribution } from '../graphql/confirmContribution'
|
import { confirmContribution } from '../graphql/confirmContribution'
|
||||||
import { denyContribution } from '../graphql/denyContribution'
|
import { denyContribution } from '../graphql/denyContribution'
|
||||||
@ -173,15 +173,11 @@ export default {
|
|||||||
this.items.find((obj) => obj.id === id).state = 'IN_PROGRESS'
|
this.items.find((obj) => obj.id === id).state = 'IN_PROGRESS'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
statusFilter() {
|
|
||||||
this.$apollo.queries.ListAllContributions.refetch()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
fields() {
|
fields() {
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
// open contributions
|
||||||
{ key: 'bookmark', label: this.$t('delete') },
|
{ key: 'bookmark', label: this.$t('delete') },
|
||||||
{ key: 'deny', label: this.$t('deny') },
|
{ key: 'deny', label: this.$t('deny') },
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
{ key: 'email', label: this.$t('e_mail') },
|
||||||
@ -207,6 +203,7 @@ export default {
|
|||||||
{ key: 'confirm', label: this.$t('save') },
|
{ key: 'confirm', label: this.$t('save') },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
// confirmed contributions
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
{ key: 'firstName', label: this.$t('firstname') },
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
{
|
{
|
||||||
@ -241,6 +238,7 @@ export default {
|
|||||||
{ key: 'chatCreation', label: this.$t('chat') },
|
{ key: 'chatCreation', label: this.$t('chat') },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
// denied contributions
|
||||||
{ key: 'reActive', label: 'reActive' },
|
{ key: 'reActive', label: 'reActive' },
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
{ key: 'firstName', label: this.$t('firstname') },
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
@ -276,8 +274,45 @@ export default {
|
|||||||
{ key: 'deniedBy', label: this.$t('mod') },
|
{ key: 'deniedBy', label: this.$t('mod') },
|
||||||
{ key: 'chatCreation', label: this.$t('chat') },
|
{ key: 'chatCreation', label: this.$t('chat') },
|
||||||
],
|
],
|
||||||
[],
|
|
||||||
[
|
[
|
||||||
|
// deleted contributions
|
||||||
|
{ key: 'reActive', label: 'reActive' },
|
||||||
|
{ key: 'firstName', label: this.$t('firstname') },
|
||||||
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
|
{
|
||||||
|
key: 'amount',
|
||||||
|
label: this.$t('creation'),
|
||||||
|
formatter: (value) => {
|
||||||
|
return value + ' GDD'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||||
|
{
|
||||||
|
key: 'contributionDate',
|
||||||
|
label: this.$t('created'),
|
||||||
|
formatter: (value) => {
|
||||||
|
return this.$d(new Date(value), 'short')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'createdAt',
|
||||||
|
label: this.$t('createdAt'),
|
||||||
|
formatter: (value) => {
|
||||||
|
return this.$d(new Date(value), 'short')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'deletedAt',
|
||||||
|
label: this.$t('contributions.deleted'),
|
||||||
|
formatter: (value) => {
|
||||||
|
return this.$d(new Date(value), 'short')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ key: 'deletedBy', label: this.$t('mod') },
|
||||||
|
{ key: 'chatCreation', label: this.$t('chat') },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// all contributions
|
||||||
{ key: 'state', label: 'state' },
|
{ key: 'state', label: 'state' },
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
{ key: 'firstName', label: this.$t('firstname') },
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
@ -349,19 +384,19 @@ export default {
|
|||||||
apollo: {
|
apollo: {
|
||||||
ListAllContributions: {
|
ListAllContributions: {
|
||||||
query() {
|
query() {
|
||||||
return listAllContributions
|
return adminListAllContributions
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
// may be at some point we need a pagination here
|
|
||||||
return {
|
return {
|
||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
statusFilter: this.statusFilter,
|
statusFilter: this.statusFilter,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update({ listAllContributions }) {
|
fetchPolicy: 'no-cache',
|
||||||
this.rows = listAllContributions.contributionCount
|
update({ adminListAllContributions }) {
|
||||||
this.items = listAllContributions.contributionList
|
this.rows = adminListAllContributions.contributionCount
|
||||||
|
this.items = adminListAllContributions.contributionList
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
this.toastError(message)
|
this.toastError(message)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Overview from './Overview'
|
import Overview from './Overview'
|
||||||
import { listAllContributions } from '../graphql/listAllContributions'
|
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
||||||
import VueApollo from 'vue-apollo'
|
import VueApollo from 'vue-apollo'
|
||||||
import { createMockClient } from 'mock-apollo-client'
|
import { createMockClient } from 'mock-apollo-client'
|
||||||
import { toastErrorSpy } from '../../test/testSetup'
|
import { toastErrorSpy } from '../../test/testSetup'
|
||||||
@ -30,7 +30,7 @@ const mocks = {
|
|||||||
|
|
||||||
const defaultData = () => {
|
const defaultData = () => {
|
||||||
return {
|
return {
|
||||||
listAllContributions: {
|
adminListAllContributions: {
|
||||||
contributionCount: 2,
|
contributionCount: 2,
|
||||||
contributionList: [
|
contributionList: [
|
||||||
{
|
{
|
||||||
@ -84,11 +84,11 @@ const defaultData = () => {
|
|||||||
|
|
||||||
describe('Overview', () => {
|
describe('Overview', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
const listAllContributionsMock = jest.fn()
|
const adminListAllContributionsMock = jest.fn()
|
||||||
|
|
||||||
mockClient.setRequestHandler(
|
mockClient.setRequestHandler(
|
||||||
listAllContributions,
|
adminListAllContributions,
|
||||||
listAllContributionsMock
|
adminListAllContributionsMock
|
||||||
.mockRejectedValueOnce({ message: 'Ouch!' })
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
.mockResolvedValue({ data: defaultData() }),
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
)
|
)
|
||||||
@ -109,8 +109,8 @@ describe('Overview', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the listAllContributions query', () => {
|
it('calls the adminListAllContributions query', () => {
|
||||||
expect(listAllContributionsMock).toBeCalledWith({
|
expect(adminListAllContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { listAllContributions } from '../graphql/listAllContributions'
|
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'overview',
|
name: 'overview',
|
||||||
@ -43,7 +43,7 @@ export default {
|
|||||||
apollo: {
|
apollo: {
|
||||||
AllContributions: {
|
AllContributions: {
|
||||||
query() {
|
query() {
|
||||||
return listAllContributions
|
return adminListAllContributions
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
// may be at some point we need a pagination here
|
// may be at some point we need a pagination here
|
||||||
@ -51,8 +51,8 @@ export default {
|
|||||||
statusFilter: this.statusFilter,
|
statusFilter: this.statusFilter,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update({ listAllContributions }) {
|
update({ adminListAllContributions }) {
|
||||||
this.$store.commit('setOpenCreations', listAllContributions.contributionCount)
|
this.$store.commit('setOpenCreations', adminListAllContributions.contributionCount)
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
this.toastError(message)
|
this.toastError(message)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
|
|||||||
CONFIG.EMAIL_SMTP_PORT = '1234'
|
CONFIG.EMAIL_SMTP_PORT = '1234'
|
||||||
CONFIG.EMAIL_USERNAME = 'user'
|
CONFIG.EMAIL_USERNAME = 'user'
|
||||||
CONFIG.EMAIL_PASSWORD = 'pwd'
|
CONFIG.EMAIL_PASSWORD = 'pwd'
|
||||||
|
CONFIG.EMAIL_TLS = true
|
||||||
|
|
||||||
jest.mock('nodemailer', () => {
|
jest.mock('nodemailer', () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -12,7 +12,6 @@ export class Contribution {
|
|||||||
this.amount = contribution.amount
|
this.amount = contribution.amount
|
||||||
this.memo = contribution.memo
|
this.memo = contribution.memo
|
||||||
this.createdAt = contribution.createdAt
|
this.createdAt = contribution.createdAt
|
||||||
this.deletedAt = contribution.deletedAt
|
|
||||||
this.confirmedAt = contribution.confirmedAt
|
this.confirmedAt = contribution.confirmedAt
|
||||||
this.confirmedBy = contribution.confirmedBy
|
this.confirmedBy = contribution.confirmedBy
|
||||||
this.contributionDate = contribution.contributionDate
|
this.contributionDate = contribution.contributionDate
|
||||||
@ -20,6 +19,8 @@ export class Contribution {
|
|||||||
this.messagesCount = contribution.messages ? contribution.messages.length : 0
|
this.messagesCount = contribution.messages ? contribution.messages.length : 0
|
||||||
this.deniedAt = contribution.deniedAt
|
this.deniedAt = contribution.deniedAt
|
||||||
this.deniedBy = contribution.deniedBy
|
this.deniedBy = contribution.deniedBy
|
||||||
|
this.deletedAt = contribution.deletedAt
|
||||||
|
this.deletedBy = contribution.deletedBy
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
@ -40,9 +41,6 @@ export class Contribution {
|
|||||||
@Field(() => Date)
|
@Field(() => Date)
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
@Field(() => Date, { nullable: true })
|
|
||||||
deletedAt: Date | null
|
|
||||||
|
|
||||||
@Field(() => Date, { nullable: true })
|
@Field(() => Date, { nullable: true })
|
||||||
confirmedAt: Date | null
|
confirmedAt: Date | null
|
||||||
|
|
||||||
@ -55,6 +53,12 @@ export class Contribution {
|
|||||||
@Field(() => Number, { nullable: true })
|
@Field(() => Number, { nullable: true })
|
||||||
deniedBy: number | null
|
deniedBy: number | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
deletedAt: Date | null
|
||||||
|
|
||||||
|
@Field(() => Number, { nullable: true })
|
||||||
|
deletedBy: number | null
|
||||||
|
|
||||||
@Field(() => Date)
|
@Field(() => Date)
|
||||||
contributionDate: Date
|
contributionDate: Date
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,11 @@ import {
|
|||||||
listContributions,
|
listContributions,
|
||||||
adminListAllContributions,
|
adminListAllContributions,
|
||||||
} from '@/seeds/graphql/queries'
|
} from '@/seeds/graphql/queries'
|
||||||
import { sendContributionConfirmedEmail } from '@/emails/sendEmailVariants'
|
import {
|
||||||
|
sendContributionConfirmedEmail,
|
||||||
|
sendContributionDeletedEmail,
|
||||||
|
sendContributionDeniedEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
import {
|
import {
|
||||||
cleanDB,
|
cleanDB,
|
||||||
resetToken,
|
resetToken,
|
||||||
@ -50,21 +54,7 @@ import { ContributionListResult } from '@model/Contribution'
|
|||||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||||
import { Order } from '@enum/Order'
|
import { Order } from '@enum/Order'
|
||||||
|
|
||||||
// mock account activation email to avoid console spam
|
jest.mock('@/emails/sendEmailVariants')
|
||||||
jest.mock('@/emails/sendEmailVariants', () => {
|
|
||||||
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
...originalModule,
|
|
||||||
// TODO: test the call of …
|
|
||||||
// sendAccountActivationEmail: jest.fn((a) => originalModule.sendAccountActivationEmail(a)),
|
|
||||||
sendContributionConfirmedEmail: jest.fn((a) =>
|
|
||||||
originalModule.sendContributionConfirmedEmail(a),
|
|
||||||
),
|
|
||||||
// TODO: test the call of …
|
|
||||||
// sendContributionRejectedEmail: jest.fn((a) => originalModule.sendContributionRejectedEmail(a)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let mutate: any, query: any, con: any
|
let mutate: any, query: any, con: any
|
||||||
let testEnv: any
|
let testEnv: any
|
||||||
@ -829,6 +819,18 @@ describe('ContributionResolver', () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('calls sendContributionDeniedEmail', async () => {
|
||||||
|
expect(sendContributionDeniedEmail).toBeCalledWith({
|
||||||
|
firstName: 'Bibi',
|
||||||
|
lastName: 'Bloxberg',
|
||||||
|
email: 'bibi@bloxberg.de',
|
||||||
|
language: 'de',
|
||||||
|
senderFirstName: 'Peter',
|
||||||
|
senderLastName: 'Lustig',
|
||||||
|
contributionMemo: 'Test contribution to deny',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -2384,6 +2386,18 @@ describe('ContributionResolver', () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('calls sendContributionDeletedEmail', async () => {
|
||||||
|
expect(sendContributionDeletedEmail).toBeCalledWith({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'de',
|
||||||
|
senderFirstName: 'Peter',
|
||||||
|
senderLastName: 'Lustig',
|
||||||
|
contributionMemo: 'Das war leider zu Viel!',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creation already confirmed', () => {
|
describe('creation already confirmed', () => {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { defineConfig } from 'cypress'
|
|||||||
import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor'
|
import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor'
|
||||||
import browserify from '@badeball/cypress-cucumber-preprocessor/browserify'
|
import browserify from '@badeball/cypress-cucumber-preprocessor/browserify'
|
||||||
|
|
||||||
let resetPasswordLink: string
|
let emailLink: string
|
||||||
|
|
||||||
async function setupNodeEvents(
|
async function setupNodeEvents(
|
||||||
on: Cypress.PluginEvents,
|
on: Cypress.PluginEvents,
|
||||||
@ -18,11 +18,11 @@ async function setupNodeEvents(
|
|||||||
)
|
)
|
||||||
|
|
||||||
on('task', {
|
on('task', {
|
||||||
setResetPasswordLink: (val) => {
|
setEmailLink: (link: string) => {
|
||||||
return (resetPasswordLink = val)
|
return (emailLink = link)
|
||||||
},
|
},
|
||||||
getResetPasswordLink: () => {
|
getEmailLink: () => {
|
||||||
return resetPasswordLink
|
return emailLink
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -13,8 +13,8 @@ Feature: User Authentication - reset password
|
|||||||
And the user navigates to the forgot password page
|
And the user navigates to the forgot password page
|
||||||
When the user enters the e-mail address "bibi@bloxberg.de"
|
When the user enters the e-mail address "bibi@bloxberg.de"
|
||||||
And the user submits the e-mail form
|
And the user submits the e-mail form
|
||||||
Then the user receives an e-mail containing the password reset link
|
Then the user receives an e-mail containing the "password reset" link
|
||||||
When the user opens the password reset link in the browser
|
When the user opens the "password reset" link in the browser
|
||||||
And the user enters the password "12345Aa_"
|
And the user enters the password "12345Aa_"
|
||||||
And the user repeats the password "12345Aa_"
|
And the user repeats the password "12345Aa_"
|
||||||
And the user submits the password form
|
And the user submits the password form
|
||||||
|
|||||||
@ -2,12 +2,16 @@ Feature: User registration
|
|||||||
As a user
|
As a user
|
||||||
I want to register to create an account
|
I want to register to create an account
|
||||||
|
|
||||||
@skip
|
|
||||||
Scenario: Register successfully
|
Scenario: Register successfully
|
||||||
Given the user navigates to page "/register"
|
Given the user navigates to page "/register"
|
||||||
When the user fills name and email "Regina" "Register" "regina@register.com"
|
When the user fills name and email "Regina" "Register" "regina@register.com"
|
||||||
And the user agrees to the privacy policy
|
And the user agrees to the privacy policy
|
||||||
And the user submits the registration form
|
And the user submits the registration form
|
||||||
Then the user can use a provided activation link
|
Then the user receives an e-mail containing the "activation" link
|
||||||
And the user can set a password "Aa12345_"
|
When the user opens the "activation" link in the browser
|
||||||
And the user can login with the credentials "regina@register.com" "Aa12345_"
|
And the user enters the password "12345Aa_"
|
||||||
|
And the user repeats the password "12345Aa_"
|
||||||
|
And the user submits the password form
|
||||||
|
And the user clicks the sign in button
|
||||||
|
Then the user submits the credentials "regina@register.com" "12345Aa_"
|
||||||
|
And the user is logged in with username "Regina Register"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ export class RegistrationPage {
|
|||||||
// selectors
|
// selectors
|
||||||
firstnameInput = '#registerFirstname'
|
firstnameInput = '#registerFirstname'
|
||||||
lastnameInput = '#registerLastname'
|
lastnameInput = '#registerLastname'
|
||||||
emailInput = '#Email-input-field'
|
emailInput = 'input[type=email]'
|
||||||
checkbox = '#registerCheckbox'
|
checkbox = '#registerCheckbox'
|
||||||
submitBtn = '[type=submit]'
|
submitBtn = '[type=submit]'
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ export class RegistrationPage {
|
|||||||
cy.get(this.checkbox).click({ force: true })
|
cy.get(this.checkbox).click({ force: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
submitRegistrationPage() {
|
submitRegistrationForm() {
|
||||||
cy.get(this.submitBtn).should('be.enabled')
|
cy.get(this.submitBtn).should('be.enabled')
|
||||||
cy.get(this.submitBtn).click()
|
cy.get(this.submitBtn).click()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
export class ResetPasswordPage {
|
export class ResetPasswordPage {
|
||||||
// selectors
|
// selectors
|
||||||
newPasswordBlock = '#new-password-input-field'
|
newPasswordInput = '#new-password-input-field'
|
||||||
newPasswordRepeatBlock = '#repeat-new-password-input-field'
|
newPasswordRepeatInput = '#repeat-new-password-input-field'
|
||||||
resetPasswordBtn = 'button[type=submit]'
|
resetPasswordBtn = 'button[type=submit]'
|
||||||
resetPasswordMessageBlock = '[data-test="reset-password-message"]'
|
resetPasswordMessageBlock = '[data-test="reset-password-message"]'
|
||||||
signinBtn = '.btn.test-message-button'
|
signinBtn = '.btn.test-message-button'
|
||||||
|
|
||||||
enterNewPassword(password: string) {
|
enterNewPassword(password: string) {
|
||||||
cy.get(this.newPasswordBlock).find('input[type=password]').type(password)
|
cy.get(this.newPasswordInput).find('input[type=password]').type(password)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatNewPassword(password: string) {
|
repeatNewPassword(password: string) {
|
||||||
cy.get(this.newPasswordRepeatBlock)
|
cy.get(this.newPasswordRepeatInput)
|
||||||
.find('input[type=password]')
|
.find('input[type=password]')
|
||||||
.type(password)
|
.type(password)
|
||||||
return this
|
return this
|
||||||
|
|||||||
@ -5,41 +5,55 @@ import { UserEMailSite } from '../../e2e/models/UserEMailSite'
|
|||||||
const userEMailSite = new UserEMailSite()
|
const userEMailSite = new UserEMailSite()
|
||||||
const resetPasswordPage = new ResetPasswordPage()
|
const resetPasswordPage = new ResetPasswordPage()
|
||||||
|
|
||||||
Then('the user receives an e-mail containing the password reset link', () => {
|
Then('the user receives an e-mail containing the {string} link', (linkName: string) => {
|
||||||
|
let emailSubject: string
|
||||||
|
let linkPattern: RegExp
|
||||||
|
|
||||||
|
switch (linkName) {
|
||||||
|
case 'activation':
|
||||||
|
emailSubject = 'Email Verification'
|
||||||
|
linkPattern = /\/checkEmail\/[0-9]+\d/
|
||||||
|
break
|
||||||
|
case 'password reset':
|
||||||
|
emailSubject = 'asswor'
|
||||||
|
linkPattern = /\/reset-password\/[0-9]+\d/
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Error in "Then the user receives an e-mail containing the {string} link" step: incorrect linkname string "${linkName}"`)
|
||||||
|
}
|
||||||
|
|
||||||
cy.origin(
|
cy.origin(
|
||||||
Cypress.env('mailserverURL'),
|
Cypress.env('mailserverURL'),
|
||||||
{ args: userEMailSite },
|
{ args: { emailSubject, linkPattern, userEMailSite } },
|
||||||
(userEMailSite) => {
|
({ emailSubject, linkPattern, userEMailSite }) => {
|
||||||
const linkPattern = /\/reset-password\/[0-9]+\d/
|
cy.visit('/') // navigate to user's e-mail site (on fake mail server)
|
||||||
|
|
||||||
cy.visit('/') // navigate to user's e-maile site (on fake mail server)
|
|
||||||
cy.get(userEMailSite.emailInbox).should('be.visible')
|
cy.get(userEMailSite.emailInbox).should('be.visible')
|
||||||
|
|
||||||
cy.get(userEMailSite.emailList)
|
cy.get(userEMailSite.emailList)
|
||||||
.find('.email-item')
|
.find('.email-item')
|
||||||
.filter(':contains(asswor)')
|
.filter(`:contains(${emailSubject})`)
|
||||||
.first()
|
.first()
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
cy.get(userEMailSite.emailMeta)
|
cy.get(userEMailSite.emailMeta)
|
||||||
.find(userEMailSite.emailSubject)
|
.find(userEMailSite.emailSubject)
|
||||||
.contains('asswor')
|
.contains(emailSubject)
|
||||||
|
|
||||||
cy.get('.email-content')
|
cy.get('.email-content', { timeout: 2000})
|
||||||
.find('.plain-text')
|
.find('.plain-text')
|
||||||
.contains(linkPattern)
|
.contains(linkPattern)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then((text) => {
|
.then((text) => {
|
||||||
const resetPasswordLink = text.match(linkPattern)[0]
|
const emailLink = text.match(linkPattern)[0]
|
||||||
cy.task('setResetPasswordLink', resetPasswordLink)
|
cy.task('setEmailLink', emailLink)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
When('the user opens the password reset link in the browser', () => {
|
When('the user opens the {string} link in the browser', (linkName: string) => {
|
||||||
cy.task('getResetPasswordLink').then((passwordResetLink) => {
|
cy.task('getEmailLink').then((emailLink) => {
|
||||||
cy.visit(passwordResetLink)
|
cy.visit(emailLink)
|
||||||
})
|
})
|
||||||
cy.get(resetPasswordPage.newPasswordRepeatBlock).should('be.visible')
|
cy.get(resetPasswordPage.newPasswordInput).should('be.visible')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,7 +18,7 @@ And('the user agrees to the privacy policy', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
And('the user submits the registration form', () => {
|
And('the user submits the registration form', () => {
|
||||||
registrationPage.submitRegistrationPage()
|
registrationPage.submitRegistrationForm()
|
||||||
cy.get(registrationPage.RegistrationThanxHeadline).should('be.visible')
|
cy.get(registrationPage.RegistrationThanxHeadline).should('be.visible')
|
||||||
cy.get(registrationPage.RegistrationThanxText).should('be.visible')
|
cy.get(registrationPage.RegistrationThanxText).should('be.visible')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
"@cypress/browserify-preprocessor": "^3.0.2",
|
"@cypress/browserify-preprocessor": "^3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||||
"@typescript-eslint/parser": "^5.38.0",
|
"@typescript-eslint/parser": "^5.38.0",
|
||||||
"cypress": "^10.4.0",
|
"cypress": "^12.7.0",
|
||||||
"eslint": "^8.23.1",
|
"eslint": "^8.23.1",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-standard": "^16.0.3",
|
||||||
|
|||||||
@ -1,6 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="contribution-messages-list-item">
|
<div class="contribution-messages-list-item">
|
||||||
<div v-if="isNotModerator" class="text-right pr-4 pr-lg-0 is-not-moderator">
|
<div v-if="message.type === 'HISTORY'">
|
||||||
|
<b-row class="mb-3 border border-197 p-1">
|
||||||
|
<b-col cols="10">
|
||||||
|
<small>{{ $d(new Date(message.createdAt), 'short') }}</small>
|
||||||
|
<div class="font-weight-bold" data-test="username">
|
||||||
|
{{ storeName.username }} {{ $t('contribution.isEdited') }}
|
||||||
|
</div>
|
||||||
|
<div class="small">
|
||||||
|
{{ $t('contribution.oldContribution') }}
|
||||||
|
</div>
|
||||||
|
<parse-message v-bind="message" data-test="message" class="p-2"></parse-message>
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="2">
|
||||||
|
<avatar :username="storeName.username" :initials="storeName.initials"></avatar>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="isNotModerator" class="text-right pr-4 pr-lg-0 is-not-moderator">
|
||||||
<b-row class="mb-3">
|
<b-row class="mb-3">
|
||||||
<b-col cols="10">
|
<b-col cols="10">
|
||||||
<div class="font-weight-bold" data-test="username">{{ storeName.username }}</div>
|
<div class="font-weight-bold" data-test="username">{{ storeName.username }}</div>
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
"openAmountForMonth": "Für <b>{monthAndYear}</b> kannst du noch <b>{creation}</b> GDD einreichen.",
|
"openAmountForMonth": "Für <b>{monthAndYear}</b> kannst du noch <b>{creation}</b> GDD einreichen.",
|
||||||
"yourContribution": "Dein Beitrag zum Gemeinwohl"
|
"yourContribution": "Dein Beitrag zum Gemeinwohl"
|
||||||
},
|
},
|
||||||
|
"isEdited": "hat den Beitrag bearbeitet",
|
||||||
"lastContribution": "Letzte Beiträge",
|
"lastContribution": "Letzte Beiträge",
|
||||||
"noContributions": {
|
"noContributions": {
|
||||||
"allContributions": "Es wurden noch keine Beiträge eingereicht.",
|
"allContributions": "Es wurden noch keine Beiträge eingereicht.",
|
||||||
@ -67,6 +68,7 @@
|
|||||||
"lastMonth": "Für den ausgewählten Monat ist das Schöpfungslimit erreicht.",
|
"lastMonth": "Für den ausgewählten Monat ist das Schöpfungslimit erreicht.",
|
||||||
"thisMonth": "Für den aktuellen Monat ist das Schöpfungslimit erreicht."
|
"thisMonth": "Für den aktuellen Monat ist das Schöpfungslimit erreicht."
|
||||||
},
|
},
|
||||||
|
"oldContribution": "Vorherige Version",
|
||||||
"selectDate": "Wann war dein Beitrag?",
|
"selectDate": "Wann war dein Beitrag?",
|
||||||
"submit": "Einreichen",
|
"submit": "Einreichen",
|
||||||
"submitted": "Der Beitrag wurde eingereicht.",
|
"submitted": "Der Beitrag wurde eingereicht.",
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
"openAmountForMonth": "For <b>{monthAndYear}</b>, you can still submit <b>{creation}</b> GDD.",
|
"openAmountForMonth": "For <b>{monthAndYear}</b>, you can still submit <b>{creation}</b> GDD.",
|
||||||
"yourContribution": "Your Contributions to the Common Good"
|
"yourContribution": "Your Contributions to the Common Good"
|
||||||
},
|
},
|
||||||
|
"isEdited": "edited the contribution",
|
||||||
"lastContribution": "Last Contributions",
|
"lastContribution": "Last Contributions",
|
||||||
"noContributions": {
|
"noContributions": {
|
||||||
"allContributions": "No contributions have been submitted yet.",
|
"allContributions": "No contributions have been submitted yet.",
|
||||||
@ -67,6 +68,7 @@
|
|||||||
"lastMonth": "The creation limit is reached for the selected month.",
|
"lastMonth": "The creation limit is reached for the selected month.",
|
||||||
"thisMonth": "The creation limit has been reached for the current month."
|
"thisMonth": "The creation limit has been reached for the current month."
|
||||||
},
|
},
|
||||||
|
"oldContribution": "Previous version",
|
||||||
"selectDate": "When was your contribution?",
|
"selectDate": "When was your contribution?",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"submitted": "The contribution was submitted.",
|
"submitted": "The contribution was submitted.",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user